diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d16fb5cbf..de5202a7ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `format` macro. - Added `String::from_utf16`. +- Added `Vec::spare_capacity_mut` +- Implemented `From<[T; N]>` for Vec +- Added `vec` macro for Vec ### Changed diff --git a/src/sealed.rs b/src/sealed.rs index 674be808d0..5e4ef4bba4 100644 --- a/src/sealed.rs +++ b/src/sealed.rs @@ -3,6 +3,11 @@ pub(crate) const fn smaller_than() { Assert::::LESS; } +#[allow(dead_code, path_statements, clippy::no_effect)] +pub(crate) const fn greater_than_eq() { + Assert::::GREATER_EQ; +} + #[allow(dead_code, path_statements, clippy::no_effect)] pub(crate) const fn greater_than_eq_0() { Assert::::GREATER_EQ; diff --git a/src/vec.rs b/src/vec.rs index 6d2a7cf692..cda8d12959 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -1,4 +1,12 @@ -use core::{cmp::Ordering, fmt, hash, iter::FromIterator, mem::MaybeUninit, ops, ptr, slice}; +use core::{ + cmp::Ordering, + fmt, hash, + iter::FromIterator, + mem::{ManuallyDrop, MaybeUninit}, + ops, ptr, slice, +}; + +use crate::sealed; /// A fixed capacity [`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html). /// @@ -821,6 +829,36 @@ impl Vec { // All item are processed. This can be optimized to `set_len` by LLVM. drop(g); } + + /// Returns the remaining spare capacity of the vector as a slice of `MaybeUninit`. + /// + /// The returned slice can be used to fill the vector with data before marking the data as + /// initialized using the `set_len` method. + /// + /// # Examples + /// + /// ``` + /// use heapless::Vec; + /// + /// // Allocate vector big enough for 10 elements. + /// let mut v: Vec<_, 10> = Vec::new(); + /// + /// // Fill in the first 3 elements. + /// let uninit = v.spare_capacity_mut(); + /// uninit[0].write(0); + /// uninit[1].write(1); + /// uninit[2].write(2); + /// + /// // Mark the first 3 elements of the vector as being initialized. + /// unsafe { + /// v.set_len(3); + /// } + /// + /// assert_eq!(&v, &[0, 1, 2]); + /// ``` + pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { + &mut self.buffer[self.len..] + } } // Trait implementations @@ -858,6 +896,34 @@ impl Drop for Vec { } } +impl From<[T; M]> for Vec { + /// Constructs a vector of capacity `N` by copying in an array of size `M`, where `M` is less + /// or equal to `N`. + /// + /// If `M` is greater than `N` or if `M` is 0, a compile-time error will be raised. + fn from(src: [T; M]) -> Self { + // Statically assert that N >= M and M > 0 + sealed::greater_than_eq::(); + sealed::greater_than_0::(); + + let mut vec = Self::new(); + // Don't run drop code for src, since we're essentially moving it + let src = ManuallyDrop::new(src); + unsafe { + // Unsafe: Cast ManuallyDrop<[T; M]> to [MaybeUninit; M], which have the same layout + let src_mu: [MaybeUninit; M] = core::mem::transmute_copy(&src); + // Unsafe: Reinterpret vec.buffer ([MaybeUninit; N]) as [MaybeUninit; M], which + // is safe as long as N >= M, then write src into the buffer + vec.buffer + .as_mut_ptr() + .cast::<[MaybeUninit; M]>() + .write(src_mu) + }; + vec.len = M; + vec + } +} + impl<'a, T: Clone, const N: usize> TryFrom<&'a [T]> for Vec { type Error = (); @@ -1175,6 +1241,39 @@ where } } +#[macro_export] +/// Shorthand for defining vectors using array syntax. +/// +/// Create `Vec` with a list of elements and capacity: +/// ``` +/// let v = heapless::vec![1, 2, 3; 7]; +/// assert_eq!(v.as_slice(), &[1, 2, 3]); +/// assert_eq!(v.capacity(), 7); +/// ``` +/// +/// Create `Vec` with a repeated element, length, and capacity: +/// ``` +/// let v = heapless::vec!['a'; 3; 4]; +/// assert_eq!(v.as_slice(), &['a', 'a', 'a']); +/// assert_eq!(v.capacity(), 4); +/// ``` +/// +/// Unlike the `std` version of this macro, the repeat element must be `Copy` or a constant, like +/// with repeat elements in array expressions. +macro_rules! vec { + (; $cap:expr) => { + heapless::Vec::<_, $cap>::new() + }; + + ($($elem:expr),+ ; $cap:expr) => { + heapless::Vec::<_, $cap>::from([$($elem),+]) + }; + + ($elem:expr ; $len:expr ; $cap:expr) => { + heapless::Vec::<_, $cap>::from([$elem; $len]) + }; +} + #[cfg(test)] mod tests { use crate::Vec; @@ -1581,4 +1680,39 @@ mod tests { // Validate full assert!(v.is_full()); } + + #[test] + fn spare_capacity_mut() { + let mut v: Vec<_, 4> = Vec::new(); + let uninit = v.spare_capacity_mut(); + assert_eq!(uninit.len(), 4); + uninit[0].write(1); + uninit[1].write(2); + uninit[2].write(3); + unsafe { v.set_len(3) }; + assert_eq!(v.as_slice(), &[1, 2, 3]); + + let uninit = v.spare_capacity_mut(); + assert_eq!(uninit.len(), 1); + uninit[0].write(4); + unsafe { v.set_len(4) }; + assert_eq!(v.as_slice(), &[1, 2, 3, 4]); + + assert!(v.spare_capacity_mut().is_empty()); + } + + #[test] + fn from() { + let v: Vec<_, 4> = Vec::from([1, 2]); + assert_eq!(v.as_slice(), &[1, 2]); + let v: Vec<_, 4> = Vec::from([1, 2, 3, 4]); + assert_eq!(v.as_slice(), &[1, 2, 3, 4]); + } } + +/// ```compile_fail +/// use heapless::Vec; +/// +/// let v: Vec<_, 4> = Vec::from([1, 2, 3, 4, 5]); +/// ``` +fn _doc_test() {}