1 use std::{ 2 alloc, mem, 3 ptr::{self, NonNull}, 4 }; 5 6 /// A typesafe helper that stores the allocated pointer without the data initialized. 7 pub struct BoxAllocation<T>( 8 // ptr cannot be null since it would mean the allocation failed. 9 // Note: covariance is acceptable since this eventually becomes a `Box<T>`, 10 // which is covariant too. 11 NonNull<T>, 12 ); 13 14 impl<T> BoxAllocation<T> { 15 /// Consumes self and writes the given value into the allocation. 16 #[inline(always)] // if this does not get inlined then copying happens init(self, value: T) -> Box<T>17 pub fn init(self, value: T) -> Box<T> { 18 if mem::size_of::<T>() == 0 { 19 return Box::new(value); 20 } 21 22 unsafe { 23 let ptr = self.0.as_ptr(); 24 mem::forget(self); 25 ptr::write(ptr, value); 26 Box::from_raw(ptr) 27 } 28 } 29 } 30 31 impl<T> Drop for BoxAllocation<T> { drop(&mut self)32 fn drop(&mut self) { 33 if mem::size_of::<T>() == 0 { 34 return; 35 } 36 37 let layout = alloc::Layout::new::<T>(); 38 unsafe { 39 alloc::dealloc(self.0.as_ptr() as *mut u8, layout); 40 } 41 } 42 } 43 44 /// Helper trait for a `Box` type that allocates up-front. 45 pub trait BoxHelper<T> { 46 /// Allocates the storage without providing any data. alloc() -> BoxAllocation<T>47 fn alloc() -> BoxAllocation<T>; 48 } 49 50 impl<T> BoxHelper<T> for Box<T> { alloc() -> BoxAllocation<T>51 fn alloc() -> BoxAllocation<T> { 52 if mem::size_of::<T>() == 0 { 53 return BoxAllocation(NonNull::dangling()); 54 } 55 56 let layout = alloc::Layout::new::<T>(); 57 BoxAllocation( 58 NonNull::new(unsafe { alloc::alloc(layout) as *mut T }) 59 .unwrap_or_else(|| alloc::handle_alloc_error(layout)), // oom 60 ) 61 } 62 } 63