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