use core::{mem::MaybeUninit, slice};
use alloc::{boxed::Box, vec::Vec};
pub struct DoubleBuffer<'a, T> {
slice: &'a mut [MaybeUninit<T>],
scratch: Box<[MaybeUninit<T>]>,
slice_is_write: bool,
}
impl<'a, T> DoubleBuffer<'a, T> {
pub fn new(slice: &'a mut [T]) -> Self {
let slice = unsafe { slice_as_uninit_mut(slice) };
let scratch = {
let mut v = Vec::with_capacity(slice.len());
unsafe {
v.set_len(slice.len());
}
v.into_boxed_slice()
};
DoubleBuffer {
slice,
scratch,
slice_is_write: false,
}
}
pub fn scatter<F>(&mut self, mut indexer: F)
where
F: FnMut(&T) -> usize,
{
let (read, write) = self.as_read_write();
let len = write.len();
for t in read {
let index = indexer(t);
if index >= len {
return;
}
let write_ptr = write[index].as_mut_ptr();
unsafe {
write_ptr.copy_from_nonoverlapping(t as *const T, 1);
}
}
}
fn as_read_write(&mut self) -> (&[T], &mut [MaybeUninit<T>]) {
let (read, write): (&[MaybeUninit<T>], &mut [MaybeUninit<T>]) = if self.slice_is_write {
(self.scratch.as_ref(), self.slice)
} else {
(self.slice, self.scratch.as_mut())
};
let read = unsafe { slice_assume_init_ref(read) };
(read, write)
}
pub unsafe fn swap(&mut self) {
self.slice_is_write = !self.slice_is_write;
}
}
impl<'a, T> Drop for DoubleBuffer<'a, T> {
fn drop(&mut self) {
if self.slice_is_write {
unsafe {
self.slice
.as_mut_ptr()
.copy_from_nonoverlapping(self.scratch.as_ptr(), self.slice.len());
}
self.slice_is_write = false;
}
}
}
#[inline(always)]
pub unsafe fn slice_assume_init_ref<T>(slice: &[MaybeUninit<T>]) -> &[T] {
unsafe { slice::from_raw_parts(slice.as_ptr() as *const T, slice.len()) }
}
#[inline(always)]
pub unsafe fn slice_as_uninit_mut<T>(slice: &mut [T]) -> &mut [MaybeUninit<T>] {
unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut MaybeUninit<T>, slice.len()) }
}