1 // Based on 2 // https://github.com/matthieu-m/rfc2580/blob/b58d1d3cba0d4b5e859d3617ea2d0943aaa31329/examples/thin.rs 3 // by matthieu-m 4 use crate::alloc::{self, Layout, LayoutError}; 5 use core::error::Error; 6 use core::fmt::{self, Debug, Display, Formatter}; 7 use core::marker::PhantomData; 8 #[cfg(not(no_global_oom_handling))] 9 use core::marker::Unsize; 10 use core::mem::{self, SizedTypeProperties}; 11 use core::ops::{Deref, DerefMut}; 12 use core::ptr::Pointee; 13 use core::ptr::{self, NonNull}; 14 15 /// ThinBox. 16 /// 17 /// A thin pointer for heap allocation, regardless of T. 18 /// 19 /// # Examples 20 /// 21 /// ``` 22 /// #![feature(thin_box)] 23 /// use std::boxed::ThinBox; 24 /// 25 /// let five = ThinBox::new(5); 26 /// let thin_slice = ThinBox::<[i32]>::new_unsize([1, 2, 3, 4]); 27 /// 28 /// use std::mem::{size_of, size_of_val}; 29 /// let size_of_ptr = size_of::<*const ()>(); 30 /// assert_eq!(size_of_ptr, size_of_val(&five)); 31 /// assert_eq!(size_of_ptr, size_of_val(&thin_slice)); 32 /// ``` 33 #[unstable(feature = "thin_box", issue = "92791")] 34 pub struct ThinBox<T: ?Sized> { 35 // This is essentially `WithHeader<<T as Pointee>::Metadata>`, 36 // but that would be invariant in `T`, and we want covariance. 37 ptr: WithOpaqueHeader, 38 _marker: PhantomData<T>, 39 } 40 41 /// `ThinBox<T>` is `Send` if `T` is `Send` because the data is owned. 42 #[unstable(feature = "thin_box", issue = "92791")] 43 unsafe impl<T: ?Sized + Send> Send for ThinBox<T> {} 44 45 /// `ThinBox<T>` is `Sync` if `T` is `Sync` because the data is owned. 46 #[unstable(feature = "thin_box", issue = "92791")] 47 unsafe impl<T: ?Sized + Sync> Sync for ThinBox<T> {} 48 49 #[unstable(feature = "thin_box", issue = "92791")] 50 impl<T> ThinBox<T> { 51 /// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on 52 /// the stack. 53 /// 54 /// # Examples 55 /// 56 /// ``` 57 /// #![feature(thin_box)] 58 /// use std::boxed::ThinBox; 59 /// 60 /// let five = ThinBox::new(5); 61 /// ``` 62 /// 63 /// [`Metadata`]: core::ptr::Pointee::Metadata 64 #[cfg(not(no_global_oom_handling))] new(value: T) -> Self65 pub fn new(value: T) -> Self { 66 let meta = ptr::metadata(&value); 67 let ptr = WithOpaqueHeader::new(meta, value); 68 ThinBox { ptr, _marker: PhantomData } 69 } 70 } 71 72 #[unstable(feature = "thin_box", issue = "92791")] 73 impl<Dyn: ?Sized> ThinBox<Dyn> { 74 /// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on 75 /// the stack. 76 /// 77 /// # Examples 78 /// 79 /// ``` 80 /// #![feature(thin_box)] 81 /// use std::boxed::ThinBox; 82 /// 83 /// let thin_slice = ThinBox::<[i32]>::new_unsize([1, 2, 3, 4]); 84 /// ``` 85 /// 86 /// [`Metadata`]: core::ptr::Pointee::Metadata 87 #[cfg(not(no_global_oom_handling))] new_unsize<T>(value: T) -> Self where T: Unsize<Dyn>,88 pub fn new_unsize<T>(value: T) -> Self 89 where 90 T: Unsize<Dyn>, 91 { 92 let meta = ptr::metadata(&value as &Dyn); 93 let ptr = WithOpaqueHeader::new(meta, value); 94 ThinBox { ptr, _marker: PhantomData } 95 } 96 } 97 98 #[unstable(feature = "thin_box", issue = "92791")] 99 impl<T: ?Sized + Debug> Debug for ThinBox<T> { fmt(&self, f: &mut Formatter<'_>) -> fmt::Result100 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 101 Debug::fmt(self.deref(), f) 102 } 103 } 104 105 #[unstable(feature = "thin_box", issue = "92791")] 106 impl<T: ?Sized + Display> Display for ThinBox<T> { fmt(&self, f: &mut Formatter<'_>) -> fmt::Result107 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 108 Display::fmt(self.deref(), f) 109 } 110 } 111 112 #[unstable(feature = "thin_box", issue = "92791")] 113 impl<T: ?Sized> Deref for ThinBox<T> { 114 type Target = T; 115 deref(&self) -> &T116 fn deref(&self) -> &T { 117 let value = self.data(); 118 let metadata = self.meta(); 119 let pointer = ptr::from_raw_parts(value as *const (), metadata); 120 unsafe { &*pointer } 121 } 122 } 123 124 #[unstable(feature = "thin_box", issue = "92791")] 125 impl<T: ?Sized> DerefMut for ThinBox<T> { deref_mut(&mut self) -> &mut T126 fn deref_mut(&mut self) -> &mut T { 127 let value = self.data(); 128 let metadata = self.meta(); 129 let pointer = ptr::from_raw_parts_mut::<T>(value as *mut (), metadata); 130 unsafe { &mut *pointer } 131 } 132 } 133 134 #[unstable(feature = "thin_box", issue = "92791")] 135 impl<T: ?Sized> Drop for ThinBox<T> { drop(&mut self)136 fn drop(&mut self) { 137 unsafe { 138 let value = self.deref_mut(); 139 let value = value as *mut T; 140 self.with_header().drop::<T>(value); 141 } 142 } 143 } 144 145 #[unstable(feature = "thin_box", issue = "92791")] 146 impl<T: ?Sized> ThinBox<T> { meta(&self) -> <T as Pointee>::Metadata147 fn meta(&self) -> <T as Pointee>::Metadata { 148 // Safety: 149 // - NonNull and valid. 150 unsafe { *self.with_header().header() } 151 } 152 data(&self) -> *mut u8153 fn data(&self) -> *mut u8 { 154 self.with_header().value() 155 } 156 with_header(&self) -> &WithHeader<<T as Pointee>::Metadata>157 fn with_header(&self) -> &WithHeader<<T as Pointee>::Metadata> { 158 // SAFETY: both types are transparent to `NonNull<u8>` 159 unsafe { &*((&self.ptr) as *const WithOpaqueHeader as *const WithHeader<_>) } 160 } 161 } 162 163 /// A pointer to type-erased data, guaranteed to either be: 164 /// 1. `NonNull::dangling()`, in the case where both the pointee (`T`) and 165 /// metadata (`H`) are ZSTs. 166 /// 2. A pointer to a valid `T` that has a header `H` directly before the 167 /// pointed-to location. 168 #[repr(transparent)] 169 struct WithHeader<H>(NonNull<u8>, PhantomData<H>); 170 171 /// An opaque representation of `WithHeader<H>` to avoid the 172 /// projection invariance of `<T as Pointee>::Metadata`. 173 #[repr(transparent)] 174 struct WithOpaqueHeader(NonNull<u8>); 175 176 impl WithOpaqueHeader { 177 #[cfg(not(no_global_oom_handling))] new<H, T>(header: H, value: T) -> Self178 fn new<H, T>(header: H, value: T) -> Self { 179 let ptr = WithHeader::new(header, value); 180 Self(ptr.0) 181 } 182 } 183 184 impl<H> WithHeader<H> { 185 #[cfg(not(no_global_oom_handling))] new<T>(header: H, value: T) -> WithHeader<H>186 fn new<T>(header: H, value: T) -> WithHeader<H> { 187 let value_layout = Layout::new::<T>(); 188 let Ok((layout, value_offset)) = Self::alloc_layout(value_layout) else { 189 // We pass an empty layout here because we do not know which layout caused the 190 // arithmetic overflow in `Layout::extend` and `handle_alloc_error` takes `Layout` as 191 // its argument rather than `Result<Layout, LayoutError>`, also this function has been 192 // stable since 1.28 ._. 193 // 194 // On the other hand, look at this gorgeous turbofish! 195 alloc::handle_alloc_error(Layout::new::<()>()); 196 }; 197 198 unsafe { 199 // Note: It's UB to pass a layout with a zero size to `alloc::alloc`, so 200 // we use `layout.dangling()` for this case, which should have a valid 201 // alignment for both `T` and `H`. 202 let ptr = if layout.size() == 0 { 203 // Some paranoia checking, mostly so that the ThinBox tests are 204 // more able to catch issues. 205 debug_assert!(value_offset == 0 && T::IS_ZST && H::IS_ZST); 206 layout.dangling() 207 } else { 208 let ptr = alloc::alloc(layout); 209 if ptr.is_null() { 210 alloc::handle_alloc_error(layout); 211 } 212 // Safety: 213 // - The size is at least `aligned_header_size`. 214 let ptr = ptr.add(value_offset) as *mut _; 215 216 NonNull::new_unchecked(ptr) 217 }; 218 219 let result = WithHeader(ptr, PhantomData); 220 ptr::write(result.header(), header); 221 ptr::write(result.value().cast(), value); 222 223 result 224 } 225 } 226 227 // Safety: 228 // - Assumes that either `value` can be dereferenced, or is the 229 // `NonNull::dangling()` we use when both `T` and `H` are ZSTs. drop<T: ?Sized>(&self, value: *mut T)230 unsafe fn drop<T: ?Sized>(&self, value: *mut T) { 231 struct DropGuard<H> { 232 ptr: NonNull<u8>, 233 value_layout: Layout, 234 _marker: PhantomData<H>, 235 } 236 237 impl<H> Drop for DropGuard<H> { 238 fn drop(&mut self) { 239 unsafe { 240 // SAFETY: Layout must have been computable if we're in drop 241 let (layout, value_offset) = 242 WithHeader::<H>::alloc_layout(self.value_layout).unwrap_unchecked(); 243 244 // Note: Don't deallocate if the layout size is zero, because the pointer 245 // didn't come from the allocator. 246 if layout.size() != 0 { 247 alloc::dealloc(self.ptr.as_ptr().sub(value_offset), layout); 248 } else { 249 debug_assert!( 250 value_offset == 0 && H::IS_ZST && self.value_layout.size() == 0 251 ); 252 } 253 } 254 } 255 } 256 257 unsafe { 258 // `_guard` will deallocate the memory when dropped, even if `drop_in_place` unwinds. 259 let _guard = DropGuard { 260 ptr: self.0, 261 value_layout: Layout::for_value_raw(value), 262 _marker: PhantomData::<H>, 263 }; 264 265 // We only drop the value because the Pointee trait requires that the metadata is copy 266 // aka trivially droppable. 267 ptr::drop_in_place::<T>(value); 268 } 269 } 270 header(&self) -> *mut H271 fn header(&self) -> *mut H { 272 // Safety: 273 // - At least `size_of::<H>()` bytes are allocated ahead of the pointer. 274 // - We know that H will be aligned because the middle pointer is aligned to the greater 275 // of the alignment of the header and the data and the header size includes the padding 276 // needed to align the header. Subtracting the header size from the aligned data pointer 277 // will always result in an aligned header pointer, it just may not point to the 278 // beginning of the allocation. 279 let hp = unsafe { self.0.as_ptr().sub(Self::header_size()) as *mut H }; 280 debug_assert!(hp.is_aligned()); 281 hp 282 } 283 value(&self) -> *mut u8284 fn value(&self) -> *mut u8 { 285 self.0.as_ptr() 286 } 287 header_size() -> usize288 const fn header_size() -> usize { 289 mem::size_of::<H>() 290 } 291 alloc_layout(value_layout: Layout) -> Result<(Layout, usize), LayoutError>292 fn alloc_layout(value_layout: Layout) -> Result<(Layout, usize), LayoutError> { 293 Layout::new::<H>().extend(value_layout) 294 } 295 } 296 297 #[unstable(feature = "thin_box", issue = "92791")] 298 impl<T: ?Sized + Error> Error for ThinBox<T> { source(&self) -> Option<&(dyn Error + 'static)>299 fn source(&self) -> Option<&(dyn Error + 'static)> { 300 self.deref().source() 301 } 302 } 303