use yog_abi::YogColumn;
use yog_sync::atomic::{AtomicPtr, AtomicU32, Ordering};
pub const INITIAL_ROWS: u32 = 64;
const WORD: usize = std::mem::size_of::<u64>();
struct Words {
ptr: *mut u64,
len: usize,
}
impl Words {
fn zeroed(len: usize) -> Words {
let ptr = Box::into_raw(vec![0u64; len].into_boxed_slice()).cast::<u64>();
Words { ptr, len }
}
fn bytes(&self) -> usize {
self.len * WORD
}
}
impl Drop for Words {
fn drop(&mut self) {
drop(unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(self.ptr, self.len)) });
}
}
pub struct Buffer {
raw: Box<YogColumn>,
data: Words,
kept: Vec<Words>,
}
unsafe impl Send for Buffer {}
unsafe impl Sync for Buffer {}
fn words(width: u32, rows: u32) -> usize {
(width as usize * rows as usize).div_ceil(WORD)
}
impl Buffer {
pub fn new(width: u32, rows: u32) -> Buffer {
let data = Words::zeroed(words(width, rows));
let raw = Box::new(YogColumn {
seq: AtomicU32::new(0),
width,
capacity: AtomicU32::new(rows),
generation: AtomicU32::new(0),
data: AtomicPtr::new(data.ptr.cast()),
});
Buffer {
raw,
data,
kept: Vec::new(),
}
}
pub fn raw(&self) -> &YogColumn {
&self.raw
}
pub fn capacity(&self) -> u32 {
self.raw.capacity.load(Ordering::Relaxed)
}
pub fn locked<R>(&mut self, write: impl FnOnce(&mut Buffer) -> R) -> R {
let began = crate::seq::begin(&self.raw.seq);
let r = write(self);
crate::seq::end(&self.raw.seq, began);
r
}
pub fn reserve(&mut self, rows: u32) {
if rows <= self.capacity() {
return;
}
let capacity = rows.next_power_of_two().max(INITIAL_ROWS);
let data = Words::zeroed(words(self.raw.width, capacity));
unsafe {
std::ptr::copy_nonoverlapping(self.data.ptr, data.ptr, self.data.len);
}
self.raw.data.store(data.ptr.cast(), Ordering::Release);
self.raw.capacity.store(capacity, Ordering::Release);
self.raw.generation.fetch_add(1, Ordering::Release);
self.kept.push(std::mem::replace(&mut self.data, data));
}
pub fn bytes(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.data.ptr.cast(), self.data.bytes()) }
}
pub fn bytes_mut(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.data.ptr.cast(), self.data.bytes()) }
}
pub fn row_mut(&mut self, row: u32) -> &mut [u8] {
let w = self.raw.width as usize;
let at = row as usize * w;
&mut self.bytes_mut()[at..at + w]
}
pub fn clear(&mut self) {
self.bytes_mut().fill(0);
}
}