Skip to main content

wasmtime_internal_core/alloc/
boxed.rs

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