wasmtime_internal_core/alloc/
boxed.rs1use 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#[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 return Ok(Box::new(MaybeUninit::uninit()));
21 }
22
23 let ptr = unsafe { try_alloc(layout)? };
25
26 let ptr = ptr.cast::<MaybeUninit<T>>();
27
28 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 let boxed = unsafe { Box::from_raw(ptr) };
83 Ok(boxed)
84 }
85}
86
87pub 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 return Ok(Box::new_uninit_slice(len));
100 }
101
102 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 Ok(unsafe { Box::from_raw(ptr) })
111}
112
113use boxed_slice_builder::BoxedSliceBuilder;
114mod boxed_slice_builder {
115 use super::*;
116
117 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 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 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 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 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 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 let new_ptr = unsafe {
215 std_alloc::alloc::realloc(ptr.cast::<u8>(), old_layout, new_layout.size())
216 };
217
218 if new_ptr.is_null() {
222 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 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#[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#[derive(Debug)]
255pub enum TooFewItemsOrOom {
256 TooFewItems(TooFewItems),
258 Oom(OutOfMemory),
260}
261
262impl TooFewItemsOrOom {
263 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
305pub 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
317pub 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#[derive(Debug)]
354pub enum BoxedSliceFromFallibleIterError<E> {
355 IterError(E),
357 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 pub fn flatten(self) -> OutOfMemory {
391 match self {
392 Self::IterError(oom) | Self::Oom(oom) => oom,
393 }
394 }
395}
396
397pub 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
423pub 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}