From a36117bf77e7bcdb6fdc7502cb78e3fe917ab13a Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Wed, 27 Dec 2023 14:29:46 -0700 Subject: [PATCH 01/14] Generalized Vec --- src/vec.rs | 218 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 126 insertions(+), 92 deletions(-) diff --git a/src/vec.rs b/src/vec.rs index 6d2a7cf692..175c8cc13b 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -1,5 +1,11 @@ use core::{cmp::Ordering, fmt, hash, iter::FromIterator, mem::MaybeUninit, ops, ptr, slice}; +#[repr(C)] +struct VecBuf { + alignment_field: [A; 0], + buffer: [MaybeUninit; N], +} + /// A fixed capacity [`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html). /// /// # Examples @@ -28,20 +34,41 @@ use core::{cmp::Ordering, fmt, hash, iter::FromIterator, mem::MaybeUninit, ops, /// } /// assert_eq!(*vec, [7, 1, 2, 3]); /// ``` -pub struct Vec { +pub struct Vec { // NOTE order is important for optimizations. the `len` first layout lets the compiler optimize // `new` to: reserve stack space and zero the first word. With the fields in the reverse order // the compiler optimizes `new` to `memclr`-ing the *entire* stack space, including the `buffer` // field which should be left uninitialized. Optimizations were last checked with Rust 1.60 len: usize, - buffer: [MaybeUninit; N], + storage: VecBuf, } -impl Vec { +impl VecBuf { const ELEM: MaybeUninit = MaybeUninit::uninit(); const INIT: [MaybeUninit; N] = [Self::ELEM; N]; // important for optimization of `new` + const fn new() -> Self { + Self { + alignment_field: [], + buffer: Self::INIT, + } + } + + const fn as_ptr(&self) -> *const T { + self.buffer.as_ptr() as *const T + } + + fn as_mut_ptr(&mut self) -> *mut T { + self.buffer.as_mut_ptr() as *mut T + } + + unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut MaybeUninit { + self.buffer.get_unchecked_mut(index) + } +} + +impl Vec { /// Constructs a new, empty vector with a fixed capacity of `N` /// /// # Examples @@ -59,7 +86,7 @@ impl Vec { pub const fn new() -> Self { Self { len: 0, - buffer: Self::INIT, + storage: VecBuf::new(), } } @@ -102,12 +129,12 @@ impl Vec { /// Returns a raw pointer to the vector’s buffer. pub fn as_ptr(&self) -> *const T { - self.buffer.as_ptr() as *const T + self.storage.as_ptr() } /// Returns a raw pointer to the vector’s buffer, which may be mutated through. pub fn as_mut_ptr(&mut self) -> *mut T { - self.buffer.as_mut_ptr() as *mut T + self.storage.as_mut_ptr() } /// Extracts a slice containing the entire vector. @@ -124,7 +151,7 @@ impl Vec { pub fn as_slice(&self) -> &[T] { // NOTE(unsafe) avoid bound checks in the slicing operation // &buffer[..self.len] - unsafe { slice::from_raw_parts(self.buffer.as_ptr() as *const T, self.len) } + unsafe { slice::from_raw_parts(self.storage.as_ptr(), self.len) } } /// Returns the contents of the vector as an array of length `M` if the length @@ -141,7 +168,7 @@ impl Vec { pub fn into_array(self) -> Result<[T; M], Self> { if self.len() == M { // This is how the unstable `MaybeUninit::array_assume_init` method does it - let array = unsafe { (&self.buffer as *const _ as *const [T; M]).read() }; + let array = unsafe { (self.as_ptr() as *const [T; M]).read() }; // We don't want `self`'s destructor to be called because that would drop all the // items in the array @@ -168,7 +195,7 @@ impl Vec { pub fn as_mut_slice(&mut self) -> &mut [T] { // NOTE(unsafe) avoid bound checks in the slicing operation // &mut buffer[..self.len] - unsafe { slice::from_raw_parts_mut(self.buffer.as_mut_ptr() as *mut T, self.len) } + unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) } } /// Returns the maximum number of elements the vector can hold. @@ -258,7 +285,7 @@ impl Vec { debug_assert!(!self.is_empty()); self.len -= 1; - self.buffer.get_unchecked_mut(self.len).as_ptr().read() + self.storage.get_unchecked_mut(self.len).as_ptr().read() } /// Appends an `item` to the back of the collection @@ -271,7 +298,7 @@ impl Vec { // use `ptr::write` to avoid running `T`'s destructor on the uninitialized memory debug_assert!(!self.is_full()); - *self.buffer.get_unchecked_mut(self.len) = MaybeUninit::new(item); + *self.storage.get_unchecked_mut(self.len) = MaybeUninit::new(item); self.len += 1; } @@ -741,14 +768,14 @@ impl Vec { // This drop guard will be invoked when predicate or `drop` of element panicked. // It shifts unchecked elements to cover holes and `set_len` to the correct length. // In cases when predicate and `drop` never panick, it will be optimized out. - struct BackshiftOnDrop<'a, T, const N: usize> { - v: &'a mut Vec, + struct BackshiftOnDrop<'a, T, const N: usize, A> { + v: &'a mut Vec, processed_len: usize, deleted_cnt: usize, original_len: usize, } - impl Drop for BackshiftOnDrop<'_, T, N> { + impl Drop for BackshiftOnDrop<'_, T, N, A> { fn drop(&mut self) { if self.deleted_cnt > 0 { // SAFETY: Trailing unchecked items must be valid since we never touch them. @@ -776,10 +803,10 @@ impl Vec { original_len, }; - fn process_loop( + fn process_loop( original_len: usize, f: &mut F, - g: &mut BackshiftOnDrop<'_, T, N>, + g: &mut BackshiftOnDrop<'_, T, N, A>, ) where F: FnMut(&mut T) -> bool, { @@ -813,10 +840,10 @@ impl Vec { } // Stage 1: Nothing was deleted. - process_loop::(original_len, &mut f, &mut g); + process_loop::(original_len, &mut f, &mut g); // Stage 2: Some elements were deleted. - process_loop::(original_len, &mut f, &mut g); + process_loop::(original_len, &mut f, &mut g); // All item are processed. This can be optimized to `set_len` by LLVM. drop(g); @@ -825,13 +852,13 @@ impl Vec { // Trait implementations -impl Default for Vec { +impl Default for Vec { fn default() -> Self { Self::new() } } -impl fmt::Debug for Vec +impl fmt::Debug for Vec where T: fmt::Debug, { @@ -849,7 +876,7 @@ impl fmt::Write for Vec { } } -impl Drop for Vec { +impl Drop for Vec { fn drop(&mut self) { // We drop each element used in the vector by turning into a `&mut [T]`. unsafe { @@ -858,7 +885,7 @@ impl Drop for Vec { } } -impl<'a, T: Clone, const N: usize> TryFrom<&'a [T]> for Vec { +impl<'a, T: Clone, const N: usize, A> TryFrom<&'a [T]> for Vec { type Error = (); fn try_from(slice: &'a [T]) -> Result { @@ -866,7 +893,7 @@ impl<'a, T: Clone, const N: usize> TryFrom<&'a [T]> for Vec { } } -impl Extend for Vec { +impl Extend for Vec { fn extend(&mut self, iter: I) where I: IntoIterator, @@ -875,7 +902,7 @@ impl Extend for Vec { } } -impl<'a, T, const N: usize> Extend<&'a T> for Vec +impl<'a, T, const N: usize, A> Extend<&'a T> for Vec where T: 'a + Copy, { @@ -887,7 +914,7 @@ where } } -impl hash::Hash for Vec +impl hash::Hash for Vec where T: core::hash::Hash, { @@ -896,7 +923,7 @@ where } } -impl<'a, T, const N: usize> IntoIterator for &'a Vec { +impl<'a, T, const N: usize, A> IntoIterator for &'a Vec { type Item = &'a T; type IntoIter = slice::Iter<'a, T>; @@ -905,7 +932,7 @@ impl<'a, T, const N: usize> IntoIterator for &'a Vec { } } -impl<'a, T, const N: usize> IntoIterator for &'a mut Vec { +impl<'a, T, const N: usize, A> IntoIterator for &'a mut Vec { type Item = &'a mut T; type IntoIter = slice::IterMut<'a, T>; @@ -914,7 +941,7 @@ impl<'a, T, const N: usize> IntoIterator for &'a mut Vec { } } -impl FromIterator for Vec { +impl FromIterator for Vec { fn from_iter(iter: I) -> Self where I: IntoIterator, @@ -930,16 +957,22 @@ impl FromIterator for Vec { /// An iterator that moves out of an [`Vec`][`Vec`]. /// /// This struct is created by calling the `into_iter` method on [`Vec`][`Vec`]. -pub struct IntoIter { - vec: Vec, +pub struct IntoIter { + vec: Vec, next: usize, } -impl Iterator for IntoIter { +impl Iterator for IntoIter { type Item = T; fn next(&mut self) -> Option { if self.next < self.vec.len() { - let item = unsafe { self.vec.buffer.get_unchecked_mut(self.next).as_ptr().read() }; + let item = unsafe { + self.vec + .storage + .get_unchecked_mut(self.next) + .as_ptr() + .read() + }; self.next += 1; Some(item) } else { @@ -948,7 +981,7 @@ impl Iterator for IntoIter { } } -impl Clone for IntoIter +impl Clone for IntoIter where T: Clone, { @@ -958,7 +991,7 @@ where if self.next < self.vec.len() { let s = unsafe { slice::from_raw_parts( - (self.vec.buffer.as_ptr() as *const T).add(self.next), + (self.vec.storage.as_ptr()).add(self.next), self.vec.len() - self.next, ) }; @@ -969,7 +1002,7 @@ where } } -impl Drop for IntoIter { +impl Drop for IntoIter { fn drop(&mut self) { unsafe { // Drop all the elements that have not been moved out of vec @@ -980,141 +1013,142 @@ impl Drop for IntoIter { } } -impl IntoIterator for Vec { +impl IntoIterator for Vec { type Item = T; - type IntoIter = IntoIter; + type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { IntoIter { vec: self, next: 0 } } } -impl PartialEq> for Vec +impl PartialEq> + for Vec where - A: PartialEq, + T1: PartialEq, { - fn eq(&self, other: &Vec) -> bool { - <[A]>::eq(self, &**other) + fn eq(&self, other: &Vec) -> bool { + <[T1]>::eq(self, &**other) } } // Vec == [B] -impl PartialEq<[B]> for Vec +impl PartialEq<[T2]> for Vec where - A: PartialEq, + T1: PartialEq, { - fn eq(&self, other: &[B]) -> bool { - <[A]>::eq(self, other) + fn eq(&self, other: &[T2]) -> bool { + <[T1]>::eq(self, other) } } // [B] == Vec -impl PartialEq> for [B] +impl PartialEq> for [T2] where - A: PartialEq, + T1: PartialEq, { - fn eq(&self, other: &Vec) -> bool { - <[A]>::eq(other, self) + fn eq(&self, other: &Vec) -> bool { + <[T1]>::eq(other, self) } } // Vec == &[B] -impl PartialEq<&[B]> for Vec +impl PartialEq<&[T2]> for Vec where - A: PartialEq, + T1: PartialEq, { - fn eq(&self, other: &&[B]) -> bool { - <[A]>::eq(self, &other[..]) + fn eq(&self, other: &&[T2]) -> bool { + <[T1]>::eq(self, &other[..]) } } // &[B] == Vec -impl PartialEq> for &[B] +impl PartialEq> for &[T2] where - A: PartialEq, + T1: PartialEq, { - fn eq(&self, other: &Vec) -> bool { - <[A]>::eq(other, &self[..]) + fn eq(&self, other: &Vec) -> bool { + <[T1]>::eq(other, &self[..]) } } // Vec == &mut [B] -impl PartialEq<&mut [B]> for Vec +impl PartialEq<&mut [T2]> for Vec where - A: PartialEq, + T1: PartialEq, { - fn eq(&self, other: &&mut [B]) -> bool { - <[A]>::eq(self, &other[..]) + fn eq(&self, other: &&mut [T2]) -> bool { + <[T1]>::eq(self, &other[..]) } } // &mut [B] == Vec -impl PartialEq> for &mut [B] +impl PartialEq> for &mut [T2] where - A: PartialEq, + T1: PartialEq, { - fn eq(&self, other: &Vec) -> bool { - <[A]>::eq(other, &self[..]) + fn eq(&self, other: &Vec) -> bool { + <[T1]>::eq(other, &self[..]) } } // Vec == [B; M] // Equality does not require equal capacity -impl PartialEq<[B; M]> for Vec +impl PartialEq<[T2; M]> for Vec where - A: PartialEq, + T1: PartialEq, { - fn eq(&self, other: &[B; M]) -> bool { - <[A]>::eq(self, &other[..]) + fn eq(&self, other: &[T2; M]) -> bool { + <[T1]>::eq(self, &other[..]) } } // [B; M] == Vec // Equality does not require equal capacity -impl PartialEq> for [B; M] +impl PartialEq> for [T2; M] where - A: PartialEq, + T1: PartialEq, { - fn eq(&self, other: &Vec) -> bool { - <[A]>::eq(other, &self[..]) + fn eq(&self, other: &Vec) -> bool { + <[T1]>::eq(other, &self[..]) } } // Vec == &[B; M] // Equality does not require equal capacity -impl PartialEq<&[B; M]> for Vec +impl PartialEq<&[T2; M]> for Vec where - A: PartialEq, + T1: PartialEq, { - fn eq(&self, other: &&[B; M]) -> bool { - <[A]>::eq(self, &other[..]) + fn eq(&self, other: &&[T2; M]) -> bool { + <[T1]>::eq(self, &other[..]) } } // &[B; M] == Vec // Equality does not require equal capacity -impl PartialEq> for &[B; M] +impl PartialEq> for &[T2; M] where - A: PartialEq, + T1: PartialEq, { - fn eq(&self, other: &Vec) -> bool { - <[A]>::eq(other, &self[..]) + fn eq(&self, other: &Vec) -> bool { + <[T1]>::eq(other, &self[..]) } } // Implements Eq if underlying data is Eq -impl Eq for Vec where T: Eq {} +impl Eq for Vec where T: Eq {} -impl PartialOrd> for Vec +impl PartialOrd> for Vec where T: PartialOrd, { - fn partial_cmp(&self, other: &Vec) -> Option { + fn partial_cmp(&self, other: &Vec) -> Option { PartialOrd::partial_cmp(&**self, &**other) } } -impl Ord for Vec +impl Ord for Vec where T: Ord, { @@ -1124,7 +1158,7 @@ where } } -impl ops::Deref for Vec { +impl ops::Deref for Vec { type Target = [T]; fn deref(&self) -> &[T] { @@ -1132,41 +1166,41 @@ impl ops::Deref for Vec { } } -impl ops::DerefMut for Vec { +impl ops::DerefMut for Vec { fn deref_mut(&mut self) -> &mut [T] { self.as_mut_slice() } } -impl AsRef> for Vec { +impl AsRef> for Vec { #[inline] fn as_ref(&self) -> &Self { self } } -impl AsMut> for Vec { +impl AsMut> for Vec { #[inline] fn as_mut(&mut self) -> &mut Self { self } } -impl AsRef<[T]> for Vec { +impl AsRef<[T]> for Vec { #[inline] fn as_ref(&self) -> &[T] { self } } -impl AsMut<[T]> for Vec { +impl AsMut<[T]> for Vec { #[inline] fn as_mut(&mut self) -> &mut [T] { self } } -impl Clone for Vec +impl Clone for Vec where T: Clone, { From 97523fa2560717b336d1e1c8d2d2220aeae35dfd Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Wed, 27 Dec 2023 14:54:46 -0700 Subject: [PATCH 02/14] Implemented transmute_buffer --- src/vec.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/vec.rs b/src/vec.rs index 175c8cc13b..3fa018cf23 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -850,6 +850,55 @@ impl Vec { } } +impl Vec { + /// Transmutes the filled buffer to the output type. The storage of the length of the [`Vec`] does + /// not participate in the transmutation. + /// + /// # Safety + /// + /// - The buffer must be full. + /// - The size of the output type must be equal to the size of the **buffer** (rather than [`Vec`]). + /// - The alignment of the `Vec` must be equal or be a multiple of the alignment of the output. + /// + /// # Panics + /// + /// In debug mode, this method panics... + /// + /// - If the buffer isn't full. + /// - If the size of the output type doesn't match the size of the buffer. + /// - If the alignment of the `Vec` isn't equal or a multiple of the alignment of the output. + /// + /// # Examples + /// + /// ``` + /// use heapless::Vec; + /// + /// let mut v: Vec = Vec::new(); + /// v.extend_from_slice(&[0, 0]).unwrap(); + /// let number: u16 = unsafe { v.transmute_buffer() }; + /// assert_eq!(number, 0u16); + /// ``` + pub unsafe fn transmute_buffer(self) -> O { + // While transmuting to the smaller type is not UB, + // it's discouraged. + #[cfg(debug_assertions)] + if self.len() != N { + panic!("Vec::transmute_buffer: the buffer isn't full"); + }; + #[cfg(debug_assertions)] + if N * core::mem::size_of::() != core::mem::size_of::() { + panic!("Vec::transmute_buffer: size mismatch"); + }; + #[cfg(debug_assertions)] + if core::mem::align_of::() % core::mem::align_of::() != 0 { + panic!("Vec::transmute_buffer: alignment mismatch"); + }; + // transmute wouldn't work because the size of O is unknown + // transmute_unchecked is unstable + core::mem::transmute_copy(&self.storage.buffer) + } +} + // Trait implementations impl Default for Vec { From 6fb4490e74307bb1636ac1dee17e6c8b43a47b4f Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Wed, 27 Dec 2023 14:56:10 -0700 Subject: [PATCH 03/14] Added an entry to CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3248ead1bc..346e941a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `format` macro. - Added `String::from_utf16`. - Added `is_full`, `recent_index`, `oldest`, and `oldest_index` to `HistoryBuffer` +- Added an optional generic parameter to `Vec` to specify the alignment of the backing array. +- Added `transmute_buffer` unsafe method for `Vec`. ### Changed From b9e59865661ad0a0ed4ebb54258b9c12638b2ee4 Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Wed, 27 Dec 2023 15:02:58 -0700 Subject: [PATCH 04/14] Updated the error message cfail/ui/not-send.stderr --- cfail/ui/not-send.stderr | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cfail/ui/not-send.stderr b/cfail/ui/not-send.stderr index 40645b7a6d..fe8afedebc 100644 --- a/cfail/ui/not-send.stderr +++ b/cfail/ui/not-send.stderr @@ -80,10 +80,15 @@ note: required because it appears within the type `ManuallyDrop>` --> $RUST/core/src/mem/maybe_uninit.rs = note: required because it appears within the type `[MaybeUninit>; 4]` +note: required because it appears within the type `VecBuf, 4, u8>` + --> $HEAPLESS/src/vec.rs + | + | struct VecBuf { + | ^^^^^^ note: required because it appears within the type `Vec, 4>` --> $HEAPLESS/src/vec.rs | - | pub struct Vec { + | pub struct Vec { | ^^^ note: required by a bound in `is_send` --> ui/not-send.rs:14:8 @@ -121,3 +126,4 @@ note: required by a bound in `is_send` 13 | where 14 | T: Send, | ^^^^ required by this bound in `is_send` + \ No newline at end of file From a7d7c328c05063279c363ff74b86aa319ed94a90 Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Wed, 27 Dec 2023 15:05:45 -0700 Subject: [PATCH 05/14] Removed the Tab before EOF --- cfail/ui/not-send.stderr | 1 - 1 file changed, 1 deletion(-) diff --git a/cfail/ui/not-send.stderr b/cfail/ui/not-send.stderr index fe8afedebc..c1820775c5 100644 --- a/cfail/ui/not-send.stderr +++ b/cfail/ui/not-send.stderr @@ -126,4 +126,3 @@ note: required by a bound in `is_send` 13 | where 14 | T: Send, | ^^^^ required by this bound in `is_send` - \ No newline at end of file From 179cf132543c683b40933ae0849ebaee4d7d5438 Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Tue, 2 Jan 2024 15:56:05 -0700 Subject: [PATCH 06/14] Fixed the breakage caused by new commits --- src/vec.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/vec.rs b/src/vec.rs index 52eb603596..ed809f6e30 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -48,6 +48,7 @@ pub struct Vec { // field which should be left uninitialized. Optimizations were last checked with Rust 1.60 len: usize, + // self.buffer.buffer access is greatly discouraged. Use the respective methods instead. storage: VecBuf, } @@ -73,6 +74,10 @@ impl VecBuf { unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut MaybeUninit { self.buffer.get_unchecked_mut(index) } + + fn iter_mut(&mut self) -> core::slice::IterMut<'_, MaybeUninit> { + self.buffer.iter_mut() + } } impl Vec { @@ -142,12 +147,14 @@ impl Vec { len: N, // NOTE(unsafe) ManuallyDrop<[T; M]> and [MaybeUninit; N] // have the same layout when N == M. - buffer: unsafe { mem::transmute_copy(&src) }, + storage: unsafe { mem::transmute_copy(&src) }, } } else { - let mut v = Vec::::new(); + let mut v = Vec::::new(); - for (src_elem, dst_elem) in src.iter().zip(v.buffer.iter_mut()) { + // `for` is not allowed in a `const fn` + // see issue #87575 for more information. + for (src_elem, dst_elem) in src.iter().zip(v.storage.iter_mut()) { // NOTE(unsafe) src element is not going to drop as src itself // is wrapped in a ManuallyDrop. dst_elem.write(unsafe { ptr::read(src_elem) }); From a66e735820f2c214b785ffc511a6c0aa81638368 Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Tue, 2 Jan 2024 15:56:42 -0700 Subject: [PATCH 07/14] Renamed the inner field of the Vec back to buffer as requested --- src/vec.rs | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/vec.rs b/src/vec.rs index ed809f6e30..bc1acce55c 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -49,7 +49,7 @@ pub struct Vec { len: usize, // self.buffer.buffer access is greatly discouraged. Use the respective methods instead. - storage: VecBuf, + buffer: VecBuf, } impl VecBuf { @@ -98,7 +98,7 @@ impl Vec { pub const fn new() -> Self { Self { len: 0, - storage: VecBuf::new(), + buffer: VecBuf::new(), } } @@ -147,14 +147,14 @@ impl Vec { len: N, // NOTE(unsafe) ManuallyDrop<[T; M]> and [MaybeUninit; N] // have the same layout when N == M. - storage: unsafe { mem::transmute_copy(&src) }, + buffer: unsafe { mem::transmute_copy(&src) }, } } else { let mut v = Vec::::new(); // `for` is not allowed in a `const fn` // see issue #87575 for more information. - for (src_elem, dst_elem) in src.iter().zip(v.storage.iter_mut()) { + for (src_elem, dst_elem) in src.iter().zip(v.buffer.iter_mut()) { // NOTE(unsafe) src element is not going to drop as src itself // is wrapped in a ManuallyDrop. dst_elem.write(unsafe { ptr::read(src_elem) }); @@ -182,12 +182,12 @@ impl Vec { /// Returns a raw pointer to the vector’s buffer. pub fn as_ptr(&self) -> *const T { - self.storage.as_ptr() + self.buffer.as_ptr() } /// Returns a raw pointer to the vector’s buffer, which may be mutated through. pub fn as_mut_ptr(&mut self) -> *mut T { - self.storage.as_mut_ptr() + self.buffer.as_mut_ptr() } /// Extracts a slice containing the entire vector. @@ -204,7 +204,7 @@ impl Vec { pub fn as_slice(&self) -> &[T] { // NOTE(unsafe) avoid bound checks in the slicing operation // &buffer[..self.len] - unsafe { slice::from_raw_parts(self.storage.as_ptr(), self.len) } + unsafe { slice::from_raw_parts(self.buffer.as_ptr(), self.len) } } /// Returns the contents of the vector as an array of length `M` if the length @@ -338,7 +338,7 @@ impl Vec { debug_assert!(!self.is_empty()); self.len -= 1; - self.storage.get_unchecked_mut(self.len).as_ptr().read() + self.buffer.get_unchecked_mut(self.len).as_ptr().read() } /// Appends an `item` to the back of the collection @@ -351,7 +351,7 @@ impl Vec { // use `ptr::write` to avoid running `T`'s destructor on the uninitialized memory debug_assert!(!self.is_full()); - *self.storage.get_unchecked_mut(self.len) = MaybeUninit::new(item); + *self.buffer.get_unchecked_mut(self.len) = MaybeUninit::new(item); self.len += 1; } @@ -948,7 +948,7 @@ impl Vec { }; // transmute wouldn't work because the size of O is unknown // transmute_unchecked is unstable - core::mem::transmute_copy(&self.storage.buffer) + core::mem::transmute_copy(&self.buffer.buffer) } } @@ -1074,13 +1074,7 @@ impl Iterator for IntoIter { type Item = T; fn next(&mut self) -> Option { if self.next < self.vec.len() { - let item = unsafe { - self.vec - .storage - .get_unchecked_mut(self.next) - .as_ptr() - .read() - }; + let item = unsafe { self.vec.buffer.get_unchecked_mut(self.next).as_ptr().read() }; self.next += 1; Some(item) } else { @@ -1099,7 +1093,7 @@ where if self.next < self.vec.len() { let s = unsafe { slice::from_raw_parts( - (self.vec.storage.as_ptr()).add(self.next), + (self.vec.buffer.as_ptr()).add(self.next), self.vec.len() - self.next, ) }; From 9d8af2517c14be124e8eb0b2470236307396841f Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Tue, 2 Jan 2024 16:00:36 -0700 Subject: [PATCH 08/14] Implemented ops::Deref and ops::DerefMut for VecBuf --- src/vec.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/vec.rs b/src/vec.rs index bc1acce55c..66ec45d786 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -74,9 +74,19 @@ impl VecBuf { unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut MaybeUninit { self.buffer.get_unchecked_mut(index) } +} + +impl ops::Deref for VecBuf { + type Target = [MaybeUninit; N]; + + fn deref(&self) -> &[MaybeUninit; N] { + &self.buffer + } +} - fn iter_mut(&mut self) -> core::slice::IterMut<'_, MaybeUninit> { - self.buffer.iter_mut() +impl ops::DerefMut for VecBuf { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.buffer } } From 4f2da52084c83cee3e0906524919cce2a7d81608 Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Tue, 2 Jan 2024 16:04:27 -0700 Subject: [PATCH 09/14] Tweaked the implementation of transmute_buffer to make it more explicit --- src/vec.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vec.rs b/src/vec.rs index 66ec45d786..3bc048d39c 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -956,9 +956,10 @@ impl Vec { if core::mem::align_of::() % core::mem::align_of::() != 0 { panic!("Vec::transmute_buffer: alignment mismatch"); }; + let inner_buf_ref: &[MaybeUninit; N] = &self.buffer; // transmute wouldn't work because the size of O is unknown // transmute_unchecked is unstable - core::mem::transmute_copy(&self.buffer.buffer) + core::mem::transmute_copy(inner_buf_ref) } } From d5f2647fc92b2be557c7f7fc353027a678f0ae89 Mon Sep 17 00:00:00 2001 From: Dmitrii - Demenev Date: Tue, 16 Jan 2024 12:51:44 -0700 Subject: [PATCH 10/14] Update src/vec.rs Co-authored-by: Markus Reiter --- src/vec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vec.rs b/src/vec.rs index 3bc048d39c..d4fdc376a1 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -214,7 +214,7 @@ impl Vec { pub fn as_slice(&self) -> &[T] { // NOTE(unsafe) avoid bound checks in the slicing operation // &buffer[..self.len] - unsafe { slice::from_raw_parts(self.buffer.as_ptr(), self.len) } + unsafe { slice::from_raw_parts(self.as_ptr(), self.len) } } /// Returns the contents of the vector as an array of length `M` if the length From 167f9378112cdea0eae7d788709c2af8d9c571b2 Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Tue, 16 Jan 2024 12:55:55 -0700 Subject: [PATCH 11/14] Removed the method which is already provided through DerefMut --- src/vec.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vec.rs b/src/vec.rs index d4fdc376a1..8c9acd9ddb 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -70,10 +70,6 @@ impl VecBuf { fn as_mut_ptr(&mut self) -> *mut T { self.buffer.as_mut_ptr() as *mut T } - - unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut MaybeUninit { - self.buffer.get_unchecked_mut(index) - } } impl ops::Deref for VecBuf { From 9cbee3ba931fe4857cc6f6d6b992c68a0287001d Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Sun, 21 Jan 2024 16:57:53 -0700 Subject: [PATCH 12/14] Removed the debug-mode panic documentation --- src/vec.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/vec.rs b/src/vec.rs index 8c9acd9ddb..94c341b470 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -919,14 +919,6 @@ impl Vec { /// - The size of the output type must be equal to the size of the **buffer** (rather than [`Vec`]). /// - The alignment of the `Vec` must be equal or be a multiple of the alignment of the output. /// - /// # Panics - /// - /// In debug mode, this method panics... - /// - /// - If the buffer isn't full. - /// - If the size of the output type doesn't match the size of the buffer. - /// - If the alignment of the `Vec` isn't equal or a multiple of the alignment of the output. - /// /// # Examples /// /// ``` From 7624384a4e5586c71708fe2ebc2ffd2eeac49f7b Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Sun, 21 Jan 2024 19:56:39 -0700 Subject: [PATCH 13/14] Removed the transmute_buffer method --- src/vec.rs | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/src/vec.rs b/src/vec.rs index 94c341b470..34bdab5727 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -909,48 +909,6 @@ impl Vec { } } -impl Vec { - /// Transmutes the filled buffer to the output type. The storage of the length of the [`Vec`] does - /// not participate in the transmutation. - /// - /// # Safety - /// - /// - The buffer must be full. - /// - The size of the output type must be equal to the size of the **buffer** (rather than [`Vec`]). - /// - The alignment of the `Vec` must be equal or be a multiple of the alignment of the output. - /// - /// # Examples - /// - /// ``` - /// use heapless::Vec; - /// - /// let mut v: Vec = Vec::new(); - /// v.extend_from_slice(&[0, 0]).unwrap(); - /// let number: u16 = unsafe { v.transmute_buffer() }; - /// assert_eq!(number, 0u16); - /// ``` - pub unsafe fn transmute_buffer(self) -> O { - // While transmuting to the smaller type is not UB, - // it's discouraged. - #[cfg(debug_assertions)] - if self.len() != N { - panic!("Vec::transmute_buffer: the buffer isn't full"); - }; - #[cfg(debug_assertions)] - if N * core::mem::size_of::() != core::mem::size_of::() { - panic!("Vec::transmute_buffer: size mismatch"); - }; - #[cfg(debug_assertions)] - if core::mem::align_of::() % core::mem::align_of::() != 0 { - panic!("Vec::transmute_buffer: alignment mismatch"); - }; - let inner_buf_ref: &[MaybeUninit; N] = &self.buffer; - // transmute wouldn't work because the size of O is unknown - // transmute_unchecked is unstable - core::mem::transmute_copy(inner_buf_ref) - } -} - // Trait implementations impl Default for Vec { From 587b704d0fec57b91f26bb39e5209865b95ebe18 Mon Sep 17 00:00:00 2001 From: Dmitrii Demenev Date: Tue, 23 Jan 2024 23:29:54 -0700 Subject: [PATCH 14/14] Updated the CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9f3f3aa62..63d43becb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `String::from_utf16`. - Added `is_full`, `recent_index`, `oldest`, and `oldest_index` to `HistoryBuffer` - Added an optional generic parameter to `Vec` to specify the alignment of the backing array. -- Added `transmute_buffer` unsafe method for `Vec`. - Added infallible conversions from arrays to `Vec`. ### Changed