Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/core/src/alloc/boxed.rs
3071 views
1
use super::{TryClone, TryNew, Vec, try_alloc};
2
use crate::{alloc::str_ptr_from_slice_ptr, 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
impl 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`].
92
pub 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
113
use boxed_slice_builder::BoxedSliceBuilder;
114
mod 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)]
243
pub struct TooFewItems;
244
245
impl 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
251
impl core::error::Error for TooFewItems {}
252
253
/// An error returned by [`new_boxed_slice_from_iter`].
254
#[derive(Debug)]
255
pub 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
262
impl 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
273
impl From<TooFewItems> for TooFewItemsOrOom {
274
fn from(e: TooFewItems) -> Self {
275
Self::TooFewItems(e)
276
}
277
}
278
279
impl From<OutOfMemory> for TooFewItemsOrOom {
280
fn from(oom: OutOfMemory) -> Self {
281
Self::Oom(oom)
282
}
283
}
284
285
impl 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
296
impl 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.
307
pub 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.
324
pub 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
334
fn 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)]
354
pub 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
361
impl<E> From<OutOfMemory> for BoxedSliceFromFallibleIterError<E> {
362
fn from(oom: OutOfMemory) -> Self {
363
Self::Oom(oom)
364
}
365
}
366
367
impl<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
376
impl<E> core::error::Error for BoxedSliceFromFallibleIterError<E>
377
where
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
388
impl 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`.
400
pub 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.
426
pub 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)]
439
mod 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
}
578
579