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