use std::alloc::{alloc, handle_alloc_error, Layout};
use std::cmp::Ordering as Ord;
use std::ptr::copy_nonoverlapping;
use std::slice;
use std::sync::atomic::{AtomicU64, Ordering as MemOrd};
pub use cow::CowChunk;
mod cow;
pub(super) struct Rb<'a> {
alloc: &'a [u8],
raw_tail: &'a AtomicU64,
raw_head: &'a AtomicU64,
}
impl<'a> Rb<'a> {
pub fn new(alloc: &'a [u8], raw_tail: &'a AtomicU64, raw_head: &'a AtomicU64) -> Self {
Self {
alloc,
raw_tail,
raw_head,
}
}
pub fn lending_pop(&self) -> Option<CowChunk<'a>> {
let rb_ptr = self.alloc.as_ptr();
let size = self.alloc.len();
let raw_tail = unsafe { *self.raw_tail.as_ptr() };
let raw_head = self.raw_head.load(MemOrd::Acquire);
if raw_tail == raw_head {
return None;
}
let tail = raw_tail & (size as u64 - 1);
let chunk_len = {
let d = size as u64 - tail;
match d.cmp(&7) {
Ord::Greater => unsafe {
let ptr = rb_ptr.add((tail + 6) as _);
*(ptr as *const u16)
},
Ord::Less => unsafe {
let ptr = rb_ptr.add((6 - d) as _);
*(ptr as *const u16)
},
Ord::Equal => unsafe {
let hi_part_ptr = rb_ptr.add((tail + 6) as _);
let lo_part_ptr = rb_ptr;
let buf = [*hi_part_ptr, *lo_part_ptr];
u16::from_ne_bytes(buf)
},
}
};
Some(match size as i64 - (tail + chunk_len as u64) as i64 {
d if d >= 0 => {
let chunk = unsafe {
let ptr = rb_ptr.add(tail as _);
slice::from_raw_parts(ptr, chunk_len as _)
};
unsafe { CowChunk::borrowed(self.raw_tail, chunk) }
}
d => {
let buf_layout = unsafe { Layout::from_size_align_unchecked(chunk_len as _, 64) };
let buf_ptr = unsafe { alloc(buf_layout) };
if buf_ptr.is_null() {
handle_alloc_error(buf_layout)
}
unsafe {
let hi_part_ptr = rb_ptr.add(tail as _);
let hi_part_len = (chunk_len as i64 + d) as _;
copy_nonoverlapping(hi_part_ptr, buf_ptr, hi_part_len);
let lo_part_ptr = rb_ptr;
let lo_part_len = -d as _;
copy_nonoverlapping(lo_part_ptr, buf_ptr.add(hi_part_len), lo_part_len);
}
self.raw_tail.fetch_add(chunk_len as _, MemOrd::Release);
unsafe { CowChunk::owned(buf_ptr, buf_layout) }
}
})
}
}