wasmtime_internal_core/alloc/
boxed.rs1use 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#[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
70pub 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 return Ok(Box::new_uninit_slice(len));
83 }
84
85 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 Ok(unsafe { Box::from_raw(ptr) })
94}
95
96use boxed_slice_builder::BoxedSliceBuilder;
97mod boxed_slice_builder {
98 use super::*;
99
100 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 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 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 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 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 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 let new_ptr = unsafe {
198 std_alloc::alloc::realloc(ptr.cast::<u8>(), old_layout, new_layout.size())
199 };
200
201 if new_ptr.is_null() {
205 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 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#[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#[derive(Debug)]
238pub enum TooFewItemsOrOom {
239 TooFewItems(TooFewItems),
241 Oom(OutOfMemory),
243}
244
245impl TooFewItemsOrOom {
246 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
288pub 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
300pub 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#[derive(Debug)]
337pub enum BoxedSliceFromFallibleIterError<E> {
338 IterError(E),
340 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 pub fn flatten(self) -> OutOfMemory {
374 match self {
375 Self::IterError(oom) | Self::Oom(oom) => oom,
376 }
377 }
378}
379
380pub 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
406pub 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}