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