xref: /wasmtime-44.0.1/crates/core/src/alloc/boxed.rs (revision aedc5480)
1 use super::{TryNew, Vec, try_alloc};
2 use crate::error::OutOfMemory;
3 use core::{
4     alloc::Layout,
5     mem::{self, MaybeUninit},
6 };
7 use std_alloc::boxed::Box;
8 
9 /// Allocate an `Box<MaybeUninit<T>>` with uninitialized contents, returning
10 /// `Err(OutOfMemory)` on allocation failure.
11 ///
12 /// You can initialize the resulting box's value via [`Box::write`].
13 #[inline]
14 fn new_uninit_box<T>() -> Result<Box<MaybeUninit<T>>, OutOfMemory> {
15     let layout = Layout::new::<MaybeUninit<T>>();
16 
17     if layout.size() == 0 {
18         // NB: no actual allocation takes place when boxing zero-sized
19         // types.
20         return Ok(Box::new(MaybeUninit::uninit()));
21     }
22 
23     // Safety: layout size is non-zero.
24     let ptr = unsafe { try_alloc(layout)? };
25 
26     let ptr = ptr.cast::<MaybeUninit<T>>();
27 
28     // Safety: The pointer's memory block was allocated by the global allocator.
29     Ok(unsafe { Box::from_raw(ptr.as_ptr()) })
30 }
31 
32 impl<T> TryNew for Box<T> {
33     type Value = T;
34 
35     #[inline]
36     fn try_new(value: T) -> Result<Self, OutOfMemory>
37     where
38         Self: Sized,
39     {
40         let boxed = new_uninit_box::<T>()?;
41         Ok(Box::write(boxed, value))
42     }
43 }
44 
45 /// Allocate a new `Box<[MaybeUninit<T>]>` of the given length with
46 /// uninitialized contents, returning `Err(OutOfMemory)` on allocation failure.
47 ///
48 /// You can initialize the resulting boxed slice with
49 /// [`boxed_slice_write_iter`].
50 pub fn new_uninit_boxed_slice<T>(len: usize) -> Result<Box<[MaybeUninit<T>]>, OutOfMemory> {
51     let layout = Layout::array::<MaybeUninit<T>>(len)
52         .map_err(|_| OutOfMemory::new(mem::size_of::<T>().saturating_mul(len)))?;
53 
54     if layout.size() == 0 {
55         // NB: no actual allocation takes place when boxing zero-sized
56         // types.
57         return Ok(Box::new_uninit_slice(len));
58     }
59 
60     // Safety: layout size is non-zero.
61     let ptr = unsafe { try_alloc(layout)? };
62 
63     let ptr = ptr.cast::<MaybeUninit<T>>().as_ptr();
64     let ptr = core::ptr::slice_from_raw_parts_mut(ptr, len);
65 
66     // Safety: The pointer's memory block was allocated by the global allocator
67     // and holds room for `[T; len]`.
68     Ok(unsafe { Box::from_raw(ptr) })
69 }
70 
71 use boxed_slice_builder::BoxedSliceBuilder;
72 mod boxed_slice_builder {
73     use super::*;
74 
75     /// Builder for constructing and initalizing a boxed slice.
76     ///
77     /// Also acts as an RAII guard to handle dropping the already-initialized
78     /// elements when we get too few items or an iterator panics during
79     /// construction.
80     pub struct BoxedSliceBuilder<T> {
81         vec: Vec<T>,
82     }
83 
84     impl<T> BoxedSliceBuilder<T> {
85         pub fn new(len: usize) -> Result<Self, OutOfMemory> {
86             let mut vec = Vec::new();
87             vec.reserve_exact(len)?;
88             Ok(Self { vec })
89         }
90 
91         pub fn from_boxed_slice(boxed: Box<[MaybeUninit<T>]>) -> Self {
92             let len = boxed.len();
93             let ptr = Box::into_raw(boxed);
94             let ptr = ptr.cast::<T>();
95             // Safety: the pointer was allocated by the global allocator and is
96             // valid for `[T; len]` since it was a boxed slice.
97             let vec = unsafe { Vec::from_raw_parts(ptr, 0, len) };
98             Self { vec }
99         }
100 
101         pub fn init_len(&self) -> usize {
102             self.vec.len()
103         }
104 
105         pub fn capacity(&self) -> usize {
106             self.vec.capacity()
107         }
108 
109         pub fn push(&mut self, value: T) -> Result<(), OutOfMemory> {
110             self.vec.push(value)
111         }
112 
113         /// Finish this builder and take its boxed slice out.
114         ///
115         /// Panics if `self.init_len() != self.capacity()`. Call
116         /// `self.shrink_to_fit()` if necessary.
117         pub fn finish(mut self) -> Box<[T]> {
118             assert_eq!(self.init_len(), self.capacity());
119             let vec = mem::take(&mut self.vec);
120             mem::forget(self);
121             let (ptr, len, cap) = vec.into_raw_parts();
122             debug_assert_eq!(len, cap);
123             let ptr = core::ptr::slice_from_raw_parts_mut(ptr, len);
124             unsafe { Box::from_raw(ptr) }
125         }
126 
127         /// Shrink this builder's allocation such that `self.init_len() ==
128         /// self.capacity()`.
129         pub fn shrink_to_fit(&mut self) -> Result<(), OutOfMemory> {
130             if self.init_len() == self.capacity() {
131                 return Ok(());
132             }
133 
134             let len = self.init_len();
135             let cap = self.capacity();
136             let vec = mem::take(&mut self.vec);
137 
138             let old_layout = Layout::array::<T>(cap).expect(
139                 "already have an allocation with this layout so should be able to recreate it",
140             );
141             let new_layout = Layout::array::<T>(len)
142                 .expect("if `cap` is fine for an array layout, then `len` must be as well");
143             debug_assert_eq!(old_layout.align(), new_layout.align());
144 
145             // Handle zero-sized reallocations, since the global `realloc` function
146             // does not.
147             if new_layout.size() == 0 {
148                 debug_assert!(mem::size_of::<T>() == 0 || len == 0);
149                 if len == 0 {
150                     debug_assert_eq!(self.capacity(), 0);
151                     debug_assert_eq!(self.init_len(), 0);
152                 } else {
153                     debug_assert_eq!(mem::size_of::<T>(), 0);
154                     let ptr = core::ptr::dangling_mut::<T>();
155                     debug_assert!(!ptr.is_null());
156                     debug_assert!(ptr.is_aligned());
157                     // Safety: T's dangling pointer is always non-null and aligned.
158                     self.vec = unsafe { Vec::from_raw_parts(ptr, len, len) };
159                 }
160                 debug_assert_eq!(self.capacity(), self.init_len());
161                 return Ok(());
162             }
163 
164             let (ptr, _len, _cap) = vec.into_raw_parts();
165             debug_assert_eq!(len, _len);
166             debug_assert_eq!(cap, _cap);
167 
168             // Safety: `ptr` was allocated by the global allocator, its memory block
169             // is described by `old_layout`, the new size is non-zero, and the new
170             // size will not overflow `isize::MAX` when rounded up to the layout's
171             // alignment (this is checked in the construction of `new_layout`).
172             let new_ptr = unsafe {
173                 std_alloc::alloc::realloc(ptr.cast::<u8>(), old_layout, new_layout.size())
174             };
175 
176             // Update `self` based on whether the reallocation succeeded or not,
177             // either inserting the new vec or reconstructing and replacing the
178             // old one.
179             if new_ptr.is_null() {
180                 // Safety: The allocation failed so we retain ownership of `ptr`,
181                 // which was a valid vec and we can safely make it a vec again.
182                 self.vec = unsafe { Vec::from_raw_parts(ptr, len, cap) };
183                 Err(OutOfMemory::new(new_layout.size()))
184             } else {
185                 let new_ptr = new_ptr.cast::<T>();
186                 // Safety: The allocation succeeded, `new_ptr` was reallocated by
187                 // the global allocator and points to a valid boxed slice of length
188                 // `len`.
189                 self.vec = unsafe { Vec::from_raw_parts(new_ptr, len, len) };
190                 debug_assert_eq!(self.capacity(), self.init_len());
191                 Ok(())
192             }
193         }
194     }
195 }
196 
197 /// An error returned when an iterator yields too few items to fully initialize
198 /// a `Box<[MaybeUninit<T>]>`.
199 #[non_exhaustive]
200 #[derive(Debug, Clone, Copy)]
201 pub struct TooFewItems;
202 
203 impl core::fmt::Display for TooFewItems {
204     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
205         f.write_str("iterator yielded too few items to fully initialize boxed slice")
206     }
207 }
208 
209 impl core::error::Error for TooFewItems {}
210 
211 /// An error returned by [`new_boxed_slice_from_iter`].
212 #[derive(Debug)]
213 pub enum TooFewItemsOrOom {
214     /// The iterator did not yield enough items to fill the boxed slice.
215     TooFewItems(TooFewItems),
216     /// Failed to allocate space for the boxed slice.
217     Oom(OutOfMemory),
218 }
219 
220 impl TooFewItemsOrOom {
221     /// Unwrap the inner `OutOfMemory` error, or panic if this is a different
222     /// error variant.
223     pub fn unwrap_oom(&self) -> OutOfMemory {
224         match self {
225             TooFewItemsOrOom::TooFewItems(_) => panic!("`unwrap_oom` on non-OOM error"),
226             TooFewItemsOrOom::Oom(oom) => *oom,
227         }
228     }
229 }
230 
231 impl From<TooFewItems> for TooFewItemsOrOom {
232     fn from(e: TooFewItems) -> Self {
233         Self::TooFewItems(e)
234     }
235 }
236 
237 impl From<OutOfMemory> for TooFewItemsOrOom {
238     fn from(oom: OutOfMemory) -> Self {
239         Self::Oom(oom)
240     }
241 }
242 
243 impl core::fmt::Display for TooFewItemsOrOom {
244     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
245         match self {
246             Self::TooFewItems(_) => {
247                 f.write_str("The iterator did not yield enough items to fill the boxed slice")
248             }
249             Self::Oom(_) => f.write_str("Failed to allocate space for the boxed slice"),
250         }
251     }
252 }
253 
254 impl core::error::Error for TooFewItemsOrOom {
255     fn cause(&self) -> Option<&dyn core::error::Error> {
256         match self {
257             Self::TooFewItems(e) => Some(e),
258             Self::Oom(oom) => Some(oom),
259         }
260     }
261 }
262 
263 /// Initialize a `Box<[MaybeUninit<T>]>` slice by writing the elements of the
264 /// given iterator into it.
265 pub fn boxed_slice_write_iter<T>(
266     boxed: Box<[MaybeUninit<T>]>,
267     iter: impl IntoIterator<Item = T>,
268 ) -> Result<Box<[T]>, TooFewItems> {
269     let len = boxed.len();
270     let builder = BoxedSliceBuilder::from_boxed_slice(boxed);
271     assert_eq!(len, builder.capacity());
272     write_iter_into_builder(builder, iter)
273 }
274 
275 /// Create a `Box<[T]>` of length `len` from the given iterator's elements.
276 ///
277 /// Returns an error on allocation failure, or if `iter` yields fewer than `len`
278 /// elements.
279 ///
280 /// The iterator is dropped after `len` elements have been yielded, this
281 /// function does not check that the iterator yields exactly `len` elements.
282 pub fn new_boxed_slice_from_iter_with_len<T>(
283     len: usize,
284     iter: impl IntoIterator<Item = T>,
285 ) -> Result<Box<[T]>, TooFewItemsOrOom> {
286     let builder = BoxedSliceBuilder::new(len)?;
287     assert_eq!(len, builder.capacity());
288     let boxed = write_iter_into_builder(builder, iter)?;
289     Ok(boxed)
290 }
291 
292 fn write_iter_into_builder<T>(
293     mut builder: BoxedSliceBuilder<T>,
294     iter: impl IntoIterator<Item = T>,
295 ) -> Result<Box<[T]>, TooFewItems> {
296     let len = builder.capacity();
297 
298     for elem in iter.into_iter().take(len) {
299         builder.push(elem).expect("reserved capacity");
300     }
301 
302     if builder.init_len() < builder.capacity() {
303         return Err(TooFewItems);
304     }
305 
306     debug_assert_eq!(builder.init_len(), builder.capacity());
307     Ok(builder.finish())
308 }
309 
310 /// An error returned by [`new_boxed_slice_from_fallible_iter`].
311 #[derive(Debug)]
312 pub enum BoxedSliceFromFallibleIterError<E> {
313     /// The fallible iterator produced an error.
314     IterError(E),
315     /// Failed to allocate space for the boxed slice.
316     Oom(OutOfMemory),
317 }
318 
319 impl<E> From<OutOfMemory> for BoxedSliceFromFallibleIterError<E> {
320     fn from(oom: OutOfMemory) -> Self {
321         Self::Oom(oom)
322     }
323 }
324 
325 impl<E> core::fmt::Display for BoxedSliceFromFallibleIterError<E> {
326     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
327         match self {
328             Self::IterError(_) => f.write_str("The fallible iterator produced an error"),
329             Self::Oom(_) => f.write_str("Failed to allocate space for the boxed slice"),
330         }
331     }
332 }
333 
334 impl<E> core::error::Error for BoxedSliceFromFallibleIterError<E>
335 where
336     E: core::error::Error,
337 {
338     fn cause(&self) -> Option<&dyn core::error::Error> {
339         match self {
340             Self::IterError(e) => Some(e),
341             Self::Oom(oom) => Some(oom),
342         }
343     }
344 }
345 
346 impl BoxedSliceFromFallibleIterError<OutOfMemory> {
347     /// Flatten this error into its inner OOM.
348     pub fn flatten(self) -> OutOfMemory {
349         match self {
350             Self::IterError(oom) | Self::Oom(oom) => oom,
351         }
352     }
353 }
354 
355 /// Create a `Box<[T]>` from the given iterator's `Result<T, E>` items.
356 ///
357 /// Returns an error on allocation failure or if an iterator item is an `Err`.
358 pub fn new_boxed_slice_from_fallible_iter<T, E>(
359     iter: impl IntoIterator<Item = Result<T, E>>,
360 ) -> Result<Box<[T]>, BoxedSliceFromFallibleIterError<E>> {
361     let iter = iter.into_iter();
362 
363     let (min, max) = iter.size_hint();
364     let len = max.unwrap_or_else(|| min);
365 
366     let mut builder = BoxedSliceBuilder::new(len)?;
367     assert_eq!(len, builder.capacity());
368 
369     for result in iter {
370         let elem = result.map_err(BoxedSliceFromFallibleIterError::IterError)?;
371         builder.push(elem)?;
372     }
373 
374     debug_assert!(builder.init_len() <= builder.capacity());
375     builder.shrink_to_fit()?;
376     debug_assert_eq!(builder.init_len(), builder.capacity());
377 
378     Ok(builder.finish())
379 }
380 
381 /// Create a `Box<[T]>` from the given iterator's elements.
382 ///
383 /// Returns an error on allocation failure.
384 pub fn new_boxed_slice_from_iter<T>(
385     iter: impl IntoIterator<Item = T>,
386 ) -> Result<Box<[T]>, OutOfMemory> {
387     let iter = iter
388         .into_iter()
389         .map(Result::<T, core::convert::Infallible>::Ok);
390     new_boxed_slice_from_fallible_iter(iter).map_err(|e| match e {
391         BoxedSliceFromFallibleIterError::Oom(oom) => oom,
392         BoxedSliceFromFallibleIterError::IterError(_) => unreachable!(),
393     })
394 }
395 
396 #[cfg(test)]
397 mod tests {
398     use super::*;
399     use core::cell::Cell;
400     use std_alloc::rc::Rc;
401 
402     struct SetFlagOnDrop(Rc<Cell<bool>>);
403 
404     impl Drop for SetFlagOnDrop {
405         fn drop(&mut self) {
406             let old_value = self.0.replace(true);
407             assert_eq!(old_value, false);
408         }
409     }
410 
411     impl SetFlagOnDrop {
412         fn new() -> (Rc<Cell<bool>>, Self) {
413             let flag = Rc::new(Cell::new(false));
414             (flag.clone(), SetFlagOnDrop(flag))
415         }
416     }
417 
418     #[test]
419     fn try_new() {
420         <Box<_> as TryNew>::try_new(4).unwrap();
421     }
422 
423     #[test]
424     fn new_boxed_slice_from_iter_with_len_smoke_test() {
425         let slice = new_boxed_slice_from_iter_with_len(3, [42, 36, 1337]).unwrap();
426         assert_eq!(&*slice, &[42, 36, 1337]);
427     }
428 
429     #[test]
430     fn new_boxed_slice_from_iter_with_len_with_too_few_elems() {
431         let (a_dropped, a) = SetFlagOnDrop::new();
432         let (b_dropped, b) = SetFlagOnDrop::new();
433         let (c_dropped, c) = SetFlagOnDrop::new();
434 
435         match new_boxed_slice_from_iter_with_len(4, [a, b, c]) {
436             Err(TooFewItemsOrOom::TooFewItems(_)) => {}
437             Ok(_) | Err(TooFewItemsOrOom::Oom(_)) => unreachable!(),
438         }
439 
440         assert!(a_dropped.get());
441         assert!(b_dropped.get());
442         assert!(c_dropped.get());
443     }
444 
445     #[test]
446     fn new_boxed_slice_from_iter_with_len_with_too_many_elems() {
447         let (a_dropped, a) = SetFlagOnDrop::new();
448         let (b_dropped, b) = SetFlagOnDrop::new();
449         let (c_dropped, c) = SetFlagOnDrop::new();
450 
451         let slice = new_boxed_slice_from_iter_with_len(2, [a, b, c]).unwrap();
452 
453         assert!(!a_dropped.get());
454         assert!(!b_dropped.get());
455         assert!(c_dropped.get());
456 
457         drop(slice);
458 
459         assert!(a_dropped.get());
460         assert!(b_dropped.get());
461         assert!(c_dropped.get());
462     }
463 
464     #[test]
465     fn new_boxed_slice_from_iter_smoke_test() {
466         let slice = new_boxed_slice_from_iter([10, 20, 30]).unwrap();
467         assert_eq!(&*slice, &[10, 20, 30]);
468     }
469 
470     #[test]
471     fn new_boxed_slice_from_fallible_iter_smoke_test() {
472         let slice =
473             new_boxed_slice_from_fallible_iter::<_, &str>([Ok(10), Ok(20), Ok(30)]).unwrap();
474         assert_eq!(&*slice, &[10, 20, 30]);
475     }
476 
477     #[test]
478     fn new_boxed_slice_from_fallible_iter_error() {
479         let result = new_boxed_slice_from_fallible_iter::<_, u32>([Ok(10), Ok(20), Err(30)]);
480         let Err(BoxedSliceFromFallibleIterError::IterError(err)) = result else {
481             panic!("unexpected result: {result:?}");
482         };
483         assert_eq!(err, 30);
484     }
485 
486     #[test]
487     fn new_uninit_boxed_slice_smoke_test() {
488         let slice = new_uninit_boxed_slice::<u32>(5).unwrap();
489         assert_eq!(slice.len(), 5);
490     }
491 
492     #[test]
493     fn boxed_slice_write_iter_smoke_test() {
494         let uninit = new_uninit_boxed_slice(3).unwrap();
495         let init = boxed_slice_write_iter(uninit, [10, 20, 30]).unwrap();
496         assert_eq!(&*init, &[10, 20, 30]);
497     }
498 
499     #[test]
500     fn boxed_slice_write_iter_with_too_few_elems() {
501         let (a_dropped, a) = SetFlagOnDrop::new();
502         let (b_dropped, b) = SetFlagOnDrop::new();
503         let (c_dropped, c) = SetFlagOnDrop::new();
504 
505         let uninit = new_uninit_boxed_slice(4).unwrap();
506         match boxed_slice_write_iter(uninit, [a, b, c]) {
507             Err(_) => {}
508             Ok(_) => unreachable!(),
509         }
510 
511         assert!(a_dropped.get());
512         assert!(b_dropped.get());
513         assert!(c_dropped.get());
514     }
515 
516     #[test]
517     fn boxed_slice_write_iter_with_too_many_elems() {
518         let (a_dropped, a) = SetFlagOnDrop::new();
519         let (b_dropped, b) = SetFlagOnDrop::new();
520         let (c_dropped, c) = SetFlagOnDrop::new();
521 
522         let uninit = new_uninit_boxed_slice(2).unwrap();
523         let slice = boxed_slice_write_iter(uninit, [a, b, c]).unwrap();
524 
525         assert!(!a_dropped.get());
526         assert!(!b_dropped.get());
527         assert!(c_dropped.get());
528 
529         drop(slice);
530 
531         assert!(a_dropped.get());
532         assert!(b_dropped.get());
533         assert!(c_dropped.get());
534     }
535 }
536