use crate::{FamBox, FamBoxOwned, FamHeader};
use core::{
marker::PhantomData,
mem::{align_of, size_of, ManuallyDrop},
ops::ControlFlow,
ptr::{self, NonNull},
};
pub struct FamBoxBuilder<H: FamHeader> {
ptr: NonNull<u8>,
next_write: NonNull<H::Element>,
ty: PhantomData<H>,
}
impl<H: FamHeader> FamBoxBuilder<H> {
pub fn new(header: H) -> ControlFlow<FamBoxOwned<H>, FamBoxBuilder<H>> {
let size = header.total_size();
let fam_len = header.fam_len();
debug_assert!(
size_of::<H>() <= size,
"invalid impl: size_of::<H>() > total size"
);
if size == 0 {
return if fam_len == 0 {
ControlFlow::Break(unsafe {
FamBoxBuilder::<H> {
ptr: NonNull::dangling(),
next_write: NonNull::dangling(),
ty: PhantomData,
}
.build()
})
} else {
ControlFlow::Continue(FamBoxBuilder::<H> {
ptr: NonNull::dangling(),
next_write: NonNull::dangling(),
ty: PhantomData,
})
};
}
let layout =
alloc::alloc::Layout::from_size_align(size, align_of::<H>()).expect("invalid layout");
let Some(ptr) = NonNull::new(unsafe { alloc::alloc::alloc(layout) }.cast::<H>()) else {
alloc::alloc::handle_alloc_error(layout);
};
unsafe { ptr.as_ptr().write(header) };
let next_write = unsafe { NonNull::new_unchecked(ptr.as_ptr().add(1)).cast() };
if fam_len == 0 {
ControlFlow::Break(unsafe {
FamBoxBuilder {
ptr: ptr.cast(),
next_write,
ty: PhantomData,
}
.build()
})
} else {
ControlFlow::Continue(FamBoxBuilder {
ptr: ptr.cast(),
next_write,
ty: PhantomData,
})
}
}
pub fn add_element(mut self, val: H::Element) -> ControlFlow<FamBoxOwned<H>, FamBoxBuilder<H>> {
unsafe { self.next_write.as_ptr().write(val) };
self.next_write = unsafe { NonNull::new_unchecked(self.next_write.as_ptr().add(1)) };
let total_size = unsafe { self.ptr.cast::<H>().as_ref() }.total_size();
if self.next_write.as_ptr() as usize - self.ptr.as_ptr() as usize == total_size {
ControlFlow::Break(unsafe { self.build() })
} else {
ControlFlow::Continue(self)
}
}
}
impl<H: FamHeader> FamBoxBuilder<H> {
pub(crate) unsafe fn from_built(ptr: NonNull<H>) -> Self {
Self {
ptr: ptr.cast(),
next_write: unsafe {
NonNull::new_unchecked(ptr.as_ptr().byte_add(ptr.as_ref().total_size()).cast())
},
ty: PhantomData,
}
}
unsafe fn build(self) -> FamBoxOwned<H> {
unsafe { FamBox::from_raw(ManuallyDrop::new(self).ptr.cast()) }
}
}
impl<H: FamHeader> Drop for FamBoxBuilder<H> {
fn drop(&mut self) {
let size = unsafe { self.ptr.cast::<H>().as_ref().total_size() };
debug_assert!(
size_of::<H>() <= size,
"invalid impl: size_of::<H>() > total size"
);
unsafe { ptr::drop_in_place(self.ptr.cast::<H>().as_ptr()) };
let mut next_to_drop = unsafe { self.ptr.cast::<H>().as_ptr().add(1).cast::<H::Element>() };
while next_to_drop != self.next_write.as_ptr() {
unsafe { ptr::drop_in_place(next_to_drop) };
next_to_drop = unsafe { next_to_drop.add(1) };
}
if size == 0 {
return;
}
let layout =
alloc::alloc::Layout::from_size_align(size, align_of::<H>()).expect("invalid layout");
unsafe { alloc::alloc::dealloc(self.ptr.as_ptr(), layout) };
}
}