xref: /wasmtime-44.0.1/crates/core/src/alloc/boxed.rs (revision 443eb304)
1 use super::{TryNew, try_alloc};
2 use crate::error::OutOfMemory;
3 use core::{alloc::Layout, mem::MaybeUninit};
4 use std_alloc::boxed::Box;
5 
6 /// Allocate an `Box<MaybeUninit<T>>` with uninitialized contents, returning
7 /// `Err(OutOfMemory)` on allocation failure.
8 ///
9 /// You can initialize the resulting box's value via [`Box::write`].
10 #[inline]
11 fn new_uninit_box<T>() -> Result<Box<MaybeUninit<T>>, OutOfMemory> {
12     let layout = Layout::new::<MaybeUninit<T>>();
13 
14     if layout.size() == 0 {
15         // NB: no actual allocation takes place when boxing zero-sized
16         // types.
17         return Ok(Box::new(MaybeUninit::uninit()));
18     }
19 
20     // Safety: layout size is non-zero.
21     let ptr = unsafe { try_alloc(layout)? };
22 
23     let ptr = ptr.cast::<MaybeUninit<T>>();
24 
25     // Safety: The pointer's memory block was allocated by the global allocator.
26     Ok(unsafe { Box::from_raw(ptr.as_ptr()) })
27 }
28 
29 impl<T> TryNew for Box<T> {
30     type Value = T;
31 
32     #[inline]
33     fn try_new(value: T) -> Result<Self, OutOfMemory>
34     where
35         Self: Sized,
36     {
37         let boxed = new_uninit_box::<T>()?;
38         Ok(Box::write(boxed, value))
39     }
40 }
41 
42 fn new_uninit_boxed_slice<T>(len: usize) -> Result<Box<[MaybeUninit<T>]>, OutOfMemory> {
43     let layout = Layout::array::<MaybeUninit<T>>(len)
44         .map_err(|_| OutOfMemory::new(len.saturating_mul(core::mem::size_of::<T>())))?;
45 
46     if layout.size() == 0 {
47         // NB: no actual allocation takes place when boxing zero-sized
48         // types.
49         return Ok(Box::new_uninit_slice(len));
50     }
51 
52     // Safety: we just ensured that the new length is non-zero.
53     debug_assert_ne!(layout.size(), 0);
54     let ptr = unsafe { try_alloc(layout)? };
55 
56     let ptr = ptr.cast::<MaybeUninit<T>>().as_ptr();
57     let ptr = core::ptr::slice_from_raw_parts_mut(ptr, len);
58 
59     // Safety: `ptr` points to a memory block that is valid for
60     // `[MaybeUninit<T>; len]` and which was allocated by the global memory
61     // allocator.
62     let boxed = unsafe { Box::from_raw(ptr) };
63     Ok(boxed)
64 }
65 
66 /// An error returned by [`new_boxed_slice_from_iter`].
67 #[derive(Debug)]
68 pub enum BoxedSliceFromIterError {
69     /// The iterator did not yield enough items to fill the boxed slice.
70     TooFewItems,
71     /// Failed to allocate space for the boxed slice.
72     Oom(OutOfMemory),
73 }
74 
75 impl From<OutOfMemory> for BoxedSliceFromIterError {
76     fn from(oom: OutOfMemory) -> Self {
77         Self::Oom(oom)
78     }
79 }
80 
81 impl core::fmt::Display for BoxedSliceFromIterError {
82     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
83         match self {
84             BoxedSliceFromIterError::TooFewItems => {
85                 f.write_str("The iterator did not yield enough items to fill the boxed slice")
86             }
87             BoxedSliceFromIterError::Oom(_) => {
88                 f.write_str("Failed to allocate space for the boxed slice")
89             }
90         }
91     }
92 }
93 
94 impl core::error::Error for BoxedSliceFromIterError {
95     fn cause(&self) -> Option<&dyn core::error::Error> {
96         match self {
97             BoxedSliceFromIterError::TooFewItems => None,
98             BoxedSliceFromIterError::Oom(oom) => Some(oom),
99         }
100     }
101 }
102 
103 /// Create a `Box<[T]>` of length `len` from the given iterator's elements.
104 ///
105 /// Returns an error on allocation failure, or if `iter` yields fewer than `len`
106 /// elements.
107 ///
108 /// The iterator is dropped after `len` elements have been yielded, this
109 /// function does not check that the iterator yields exactly `len` elements.
110 pub fn new_boxed_slice_from_iter<T>(
111     len: usize,
112     iter: impl IntoIterator<Item = T>,
113 ) -> Result<Box<[T]>, BoxedSliceFromIterError> {
114     /// RAII guard to handle dropping the initialized elements of the boxed
115     /// slice in the cases where we get too few items or the iterator panics.
116     struct DropGuard<T> {
117         boxed: Box<[MaybeUninit<T>]>,
118         init_len: usize,
119     }
120 
121     impl<T> Drop for DropGuard<T> {
122         fn drop(&mut self) {
123             debug_assert!(self.init_len <= self.boxed.len());
124 
125             if !core::mem::needs_drop::<T>() {
126                 return;
127             }
128 
129             for elem in self.boxed.iter_mut().take(self.init_len) {
130                 // Safety: the elements in `self.boxed[..self.init_len]` are
131                 // valid and initialized and will not be used again.
132                 unsafe {
133                     core::ptr::drop_in_place(elem.as_mut_ptr());
134                 }
135             }
136         }
137     }
138 
139     let mut guard = DropGuard {
140         boxed: new_uninit_boxed_slice(len)?,
141         init_len: 0,
142     };
143     assert_eq!(len, guard.boxed.len());
144 
145     for (i, elem) in iter.into_iter().enumerate().take(len) {
146         debug_assert!(i < len);
147         debug_assert_eq!(guard.init_len, i);
148         guard.boxed[i].write(elem);
149         guard.init_len += 1;
150     }
151 
152     debug_assert!(guard.init_len <= guard.boxed.len());
153 
154     if guard.init_len < guard.boxed.len() {
155         return Err(BoxedSliceFromIterError::TooFewItems);
156     }
157 
158     debug_assert_eq!(guard.init_len, guard.boxed.len());
159 
160     // Take the boxed slice out of the guard.
161     let boxed = {
162         guard.init_len = 0;
163         let boxed = core::mem::take(&mut guard.boxed);
164         core::mem::forget(guard);
165         boxed
166     };
167 
168     // Safety: we initialized all elements.
169     let boxed = unsafe { boxed.assume_init() };
170 
171     Ok(boxed)
172 }
173 
174 #[cfg(test)]
175 mod tests {
176     use super::*;
177     use core::cell::Cell;
178     use std_alloc::rc::Rc;
179 
180     struct SetFlagOnDrop(Rc<Cell<bool>>);
181 
182     impl Drop for SetFlagOnDrop {
183         fn drop(&mut self) {
184             let old_value = self.0.replace(true);
185             assert_eq!(old_value, false);
186         }
187     }
188 
189     impl SetFlagOnDrop {
190         fn new() -> (Rc<Cell<bool>>, Self) {
191             let flag = Rc::new(Cell::new(false));
192             (flag.clone(), SetFlagOnDrop(flag))
193         }
194     }
195 
196     #[test]
197     fn try_new() {
198         <Box<_> as TryNew>::try_new(4).unwrap();
199     }
200 
201     #[test]
202     fn new_boxed_slice_from_iter_smoke_test() {
203         let slice = new_boxed_slice_from_iter(3, [42, 36, 1337]).unwrap();
204         assert_eq!(&*slice, &[42, 36, 1337]);
205     }
206 
207     #[test]
208     fn new_boxed_slice_from_iter_with_too_few_elems() {
209         let (a_dropped, a) = SetFlagOnDrop::new();
210         let (b_dropped, b) = SetFlagOnDrop::new();
211         let (c_dropped, c) = SetFlagOnDrop::new();
212 
213         match new_boxed_slice_from_iter(4, [a, b, c]) {
214             Err(BoxedSliceFromIterError::TooFewItems) => {}
215             Ok(_) | Err(BoxedSliceFromIterError::Oom(_)) => unreachable!(),
216         }
217 
218         assert!(a_dropped.get());
219         assert!(b_dropped.get());
220         assert!(c_dropped.get());
221     }
222 
223     #[test]
224     fn new_boxed_slice_from_iter_with_too_many_elems() {
225         let (a_dropped, a) = SetFlagOnDrop::new();
226         let (b_dropped, b) = SetFlagOnDrop::new();
227         let (c_dropped, c) = SetFlagOnDrop::new();
228 
229         let slice = new_boxed_slice_from_iter(2, [a, b, c]).unwrap();
230 
231         assert!(!a_dropped.get());
232         assert!(!b_dropped.get());
233         assert!(c_dropped.get());
234 
235         drop(slice);
236 
237         assert!(a_dropped.get());
238         assert!(b_dropped.get());
239         assert!(c_dropped.get());
240     }
241 }
242