From 1dd82f419983a080096f6bd1d26601c05b6b01d6 Mon Sep 17 00:00:00 2001 From: YuhanLiin Date: Wed, 20 Dec 2023 00:20:04 -0500 Subject: [PATCH 1/5] Add spare_capacity_mut --- src/vec.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/vec.rs b/src/vec.rs index 6d2a7cf692..4eb3b96e08 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -821,6 +821,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 @@ -1581,4 +1611,24 @@ 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()); + } } From 1e29749620c53c1c71c88b3e9f5f9467e607c9ba Mon Sep 17 00:00:00 2001 From: YuhanLiin Date: Wed, 20 Dec 2023 00:20:42 -0500 Subject: [PATCH 2/5] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d16fb5cbf..7c63e4c4b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `format` macro. - Added `String::from_utf16`. +- Added `Vec::spare_capacity_mut` ### Changed From 25de8531c074527545d11d362c07930bffc5b9fd Mon Sep 17 00:00:00 2001 From: YuhanLiin Date: Wed, 20 Dec 2023 01:58:46 -0500 Subject: [PATCH 3/5] Add From impl --- CHANGELOG.md | 1 + src/vec.rs | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c63e4c4b4..345561c927 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ 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 ### Changed diff --git a/src/vec.rs b/src/vec.rs index 4eb3b96e08..c572f68d58 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -888,6 +888,17 @@ impl Drop for Vec { } } +impl From<[T; N]> for Vec { + /// Converts array to `Vec` of same size and capacity without copying + fn from(buffer: [T; N]) -> Self { + Self { + // cast [T; N] into [MaybeUninit; N] + buffer: unsafe { buffer.as_ptr().cast::<[MaybeUninit; N]>().read() }, + len: N, + } + } +} + impl<'a, T: Clone, const N: usize> TryFrom<&'a [T]> for Vec { type Error = (); From a667813252ddc9e5107151b6a3a5fc08df13f7ee Mon Sep 17 00:00:00 2001 From: YuhanLiin Date: Wed, 20 Dec 2023 22:16:25 -0500 Subject: [PATCH 4/5] Allow From impl for arrays of M, where M <= N --- src/sealed.rs | 5 +++++ src/vec.rs | 58 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 9 deletions(-) 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 c572f68d58..743797f326 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). /// @@ -888,14 +896,31 @@ impl Drop for Vec { } } -impl From<[T; N]> for Vec { - /// Converts array to `Vec` of same size and capacity without copying - fn from(buffer: [T; N]) -> Self { - Self { - // cast [T; N] into [MaybeUninit; N] - buffer: unsafe { buffer.as_ptr().cast::<[MaybeUninit; N]>().read() }, - len: N, - } +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 } } @@ -1642,4 +1667,19 @@ mod tests { 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() {} From 0cc1d5f56514e46ce1f3811aa61ffef6e79450ae Mon Sep 17 00:00:00 2001 From: YuhanLiin Date: Wed, 20 Dec 2023 23:32:24 -0500 Subject: [PATCH 5/5] Add vec macro --- CHANGELOG.md | 1 + src/vec.rs | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 345561c927..de5202a7ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - 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/vec.rs b/src/vec.rs index 743797f326..cda8d12959 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -1241,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;