Skip to content

Commit 83cf59f

Browse files
authored
Add a vec! macro for our OOM-handling Vec (#12581)
1 parent 1f16b28 commit 83cf59f

3 files changed

Lines changed: 78 additions & 2 deletions

File tree

crates/core/src/alloc/vec.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::alloc::{TryClone, try_realloc};
22
use crate::error::OutOfMemory;
33
use core::cmp::Ordering;
44
use core::marker::PhantomData;
5+
use core::num::NonZeroUsize;
56
use core::{
67
fmt, mem,
78
ops::{Deref, DerefMut, Index, IndexMut},
@@ -11,6 +12,38 @@ use std_alloc::alloc::Layout;
1112
use std_alloc::boxed::Box;
1213
use std_alloc::vec::Vec as StdVec;
1314

15+
/// Same as the [`std::vec!`] macro but returns an error on allocation failure.
16+
#[macro_export]
17+
macro_rules! vec {
18+
( $( $elem:expr ),* ) => {{
19+
let len = $crate::private_len!( $( $elem ),* );
20+
$crate::alloc::Vec::with_capacity(len).and_then(|mut v| {
21+
$( v.push($elem)?; )*
22+
let _ = &mut v;
23+
Ok(v)
24+
})
25+
}};
26+
27+
( $elem:expr; $len:expr ) => {{
28+
let len: usize = $len;
29+
if let Some(len) = ::core::num::NonZeroUsize::new(len) {
30+
let elem = $elem;
31+
$crate::alloc::Vec::from_elem(elem, len)
32+
} else {
33+
Ok($crate::alloc::Vec::new())
34+
}
35+
}};
36+
37+
}
38+
39+
// Only for use by the `vec!` macro.
40+
#[doc(hidden)]
41+
#[macro_export]
42+
macro_rules! private_len {
43+
( ) => { 0 };
44+
( $e:expr $( , $es:expr )* ) => { 1 + $crate::private_len!( $( $es ),* ) };
45+
}
46+
1447
/// Like `std::vec::Vec` but all methods that allocate force handling allocation
1548
/// failure.
1649
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -61,6 +94,25 @@ impl<T> Vec<T> {
6194
Ok(v)
6295
}
6396

97+
// For use with the `vec!` macro.
98+
#[doc(hidden)]
99+
#[inline]
100+
pub fn from_elem(elem: T, len: NonZeroUsize) -> Result<Self, OutOfMemory>
101+
where
102+
T: TryClone,
103+
{
104+
let mut v = Self::with_capacity(len.get())?;
105+
106+
// Minimize calls to `TryClone` by always pushing `elem` itself as the
107+
// last element.
108+
for _ in 0..len.get() - 1 {
109+
v.push(elem.try_clone()?)?;
110+
}
111+
v.push(elem)?;
112+
113+
Ok(v)
114+
}
115+
64116
/// Same as [`std::vec::Vec::reserve`] but returns an error on allocation
65117
/// failure.
66118
pub fn reserve(&mut self, additional: usize) -> Result<(), OutOfMemory> {

crates/environ/src/collections.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ pub use hash_set::HashSet;
1313
pub use index_map::IndexMap;
1414
pub use primary_map::PrimaryMap;
1515
pub use secondary_map::SecondaryMap;
16-
pub use wasmtime_core::alloc::{String, TryClone, TryNew, Vec, try_new};
16+
pub use wasmtime_core::{
17+
alloc::{String, TryClone, TryNew, Vec, try_new},
18+
vec,
19+
};
1720

1821
/// Collections which abort on OOM.
1922
//

crates/fuzzing/tests/oom.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ use std::{
88
};
99
use wasmtime::{error::OutOfMemory, *};
1010
use wasmtime_core::alloc::TryCollect;
11-
use wasmtime_environ::{PrimaryMap, SecondaryMap, collections::*};
11+
use wasmtime_environ::{
12+
PrimaryMap, SecondaryMap,
13+
collections::{self, *},
14+
};
1215
use wasmtime_fuzzing::oom::{OomTest, OomTestAllocator};
1316

1417
#[global_allocator]
@@ -749,6 +752,24 @@ fn vec_extend() -> Result<()> {
749752
})
750753
}
751754

755+
#[test]
756+
fn vec_macro_elems() -> Result<()> {
757+
OomTest::new().test(|| {
758+
let v = collections::vec![100, 200, 300, 400]?;
759+
assert_eq!(&*v, &[100, 200, 300, 400]);
760+
Ok(())
761+
})
762+
}
763+
764+
#[test]
765+
fn vec_macro_elem_len() -> Result<()> {
766+
OomTest::new().test(|| {
767+
let v = collections::vec![100; 3]?;
768+
assert_eq!(&*v, &[100, 100, 100]);
769+
Ok(())
770+
})
771+
}
772+
752773
#[test]
753774
fn index_map_try_clone() -> Result<()> {
754775
OomTest::new()

0 commit comments

Comments
 (0)