Skip to main content

wasmtime_internal_core/alloc/
boxed.rs

1use super::{TryClone, TryNew, Vec, try_alloc};
2use crate::error::OutOfMemory;
3use core::{
4    alloc::Layout,
5    mem::{self, MaybeUninit},
6};
7use 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]
14fn 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
32impl<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
45impl<T> TryClone for Box<T>
46where
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
56impl<T> TryClone for Box<[T]>
57where
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`].
75pub 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
96use boxed_slice_builder::BoxedSliceBuilder;
97mod 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)]
226pub struct TooFewItems;
227
228impl 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
234impl core::error::Error for TooFewItems {}
235
236/// An error returned by [`new_boxed_slice_from_iter`].
237#[derive(Debug)]
238pub 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
245impl 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
256impl From<TooFewItems> for TooFewItemsOrOom {
257    fn from(e: TooFewItems) -> Self {
258        Self::TooFewItems(e)
259    }
260}
261
262impl From<OutOfMemory> for TooFewItemsOrOom {
263    fn from(oom: OutOfMemory) -> Self {
264        Self::Oom(oom)
265    }
266}
267
268impl 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
279impl 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.
290pub 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.
307pub 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
317fn 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)]
337pub 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
344impl<E> From<OutOfMemory> for BoxedSliceFromFallibleIterError<E> {
345    fn from(oom: OutOfMemory) -> Self {
346        Self::Oom(oom)
347    }
348}
349
350impl<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
359impl<E> core::error::Error for BoxedSliceFromFallibleIterError<E>
360where
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
371impl 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`.
383pub 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.
409pub 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)]
422mod 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}