use std::{
alloc::{Layout, alloc_zeroed, dealloc, handle_alloc_error},
marker::PhantomData,
mem::{align_of, size_of},
ptr::{self, NonNull},
};
pub struct VariableSizedBox<T> {
size: usize,
data: NonNull<T>,
pd: PhantomData<T>,
}
impl<T> VariableSizedBox<T> {
pub fn new(size: usize) -> VariableSizedBox<T> {
if size == 0 {
return VariableSizedBox::default();
}
let layout = Layout::from_size_align(size, align_of::<T>()).unwrap();
if let Some(data) = NonNull::new(unsafe { alloc_zeroed(layout) }) {
VariableSizedBox {
size,
data: data.cast(),
pd: PhantomData,
}
} else {
handle_alloc_error(layout)
}
}
pub fn as_mut_ptr(&mut self) -> *mut T {
if self.size == 0 {
ptr::null_mut()
} else {
self.data.as_ptr()
}
}
pub unsafe fn as_ref(&self) -> &T {
assert!(self.size >= size_of::<T>());
unsafe { self.data.as_ref() }
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.size
}
}
impl<T> Drop for VariableSizedBox<T> {
fn drop(&mut self) {
if self.size == 0 {
return;
}
let layout = Layout::from_size_align(self.size, align_of::<T>()).unwrap();
unsafe { dealloc(self.as_mut_ptr().cast(), layout) }
}
}
impl<T> Default for VariableSizedBox<T> {
fn default() -> Self {
VariableSizedBox {
size: 0,
data: NonNull::dangling(),
pd: PhantomData,
}
}
}