Hello. I want something that essentially does the same thing as smallbox. ie. exposes a type with a fixed size and a little bit of local storage, and will allocate if the contained T is bigger than the fixed storage allotment.
Unlike smallbox, I don't need to support DSTs, and also I don't want to spend the extra 8 bytes on an additional internal pointer, like smallbox does.
Also, it looks like smallbox creates a self-referential pointer in the local case (the code here: smallbox/src/smallbox.rs at 5f33267ec0dbbb399f0556c1174f71cfb38d0543 · andylokandy/smallbox · GitHub) which didn't seem like it would work if the structure is moved. But it's possible I misunderstood the smallbox code.
First question is: is there a reliable, well-tested crate for this that already exists? I didn't see one and it looked like smallbox has the most users.
If not, are there any safety / soundness issues with my solution here?
// #[repr(align(64))]
//TODO, find a way to tell the compiler to set alignment to match the minimum of T and Box<T>
union LocalOrHeap<T> {
heap: ManuallyDrop<Box<T>>,
local: MaybeUninit<TStorage>,
}
type TStorage = [u8; 16];
impl<T> LocalOrHeap<T> {
pub fn new(val: T) -> Self {
//TODO, see above, we want to tell the compiler to align this, rather than panicking if it's unaligned
if core::mem::align_of::<Self>() < core::mem::align_of::<T>() {
panic!("BoxOrValue<T> must have an alignment that is the same or coarser than T");
}
if core::mem::size_of::<T>() > core::mem::size_of::<TStorage>() {
Self{ heap: ManuallyDrop::new(Box::new(val))}
} else {
let mut storage = MaybeUninit::<TStorage>::uninit();
let size = core::mem::size_of::<T>();
// SAFETY: We know we won't overwrite storage, and we also know storage
// will be aligned at least as coarsely as T
unsafe{ core::ptr::copy_nonoverlapping(&val as *const T as *const u8, storage.as_mut_ptr() as *mut u8, size); }
core::mem::forget(val);
Self{ local: storage }
}
}
}
impl<T> Drop for LocalOrHeap<T> {
fn drop(&mut self) {
if core::mem::size_of::<T>() > core::mem::size_of::<TStorage>() {
// SAFETY: We know we have a `heap` because of the size of T
unsafe{ ManuallyDrop::drop(&mut self.heap) };
} else {
// SAFETY: We know we have a `local` because of the size of T
unsafe{ core::ptr::drop_in_place::<T>((self.local.as_mut_ptr()).cast()); }
}
}
}
impl<T> AsRef<T> for LocalOrHeap<T> {
fn as_ref(&self) -> &T {
if core::mem::size_of::<T>() > core::mem::size_of::<TStorage>() {
// SAFETY: We know we have a `heap` because of the size of T
unsafe{ &self.heap }
} else {
// SAFETY: We know we have a `local` because of the size of T
unsafe{ &*self.local.as_ptr().cast() }
}
}
}
Finally, is there a way I can specify "Align my type to the maximum of the alignments of these other types"?
Any feedback is appreciated. Thank you.