From 7c3b198127982522bd1b1efffc73dbe666461caf Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Thu, 14 Dec 2023 17:25:46 +0100 Subject: [PATCH] Add a simple `Copy`-able `ArrayVec` The `arrayvec::ArrayVec` type is not `Copy` which is not nice and we would like to have a `Copy` type in our crates. While the PR to add support to the `arrayvec` crate is not merged we implement our own simplified version. This one acts mostly as a dumb storage - it has just a few methods and traits. The new ones can be added as needed later. --- internals/src/array_vec.rs | 206 +++++++++++++++++++++++++++++++++++++ internals/src/lib.rs | 1 + 2 files changed, 207 insertions(+) create mode 100644 internals/src/array_vec.rs diff --git a/internals/src/array_vec.rs b/internals/src/array_vec.rs new file mode 100644 index 00000000..0137b532 --- /dev/null +++ b/internals/src/array_vec.rs @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! A simplified `Copy` version of `arrayvec::ArrayVec`. + +use core::fmt; + +pub use safety_boundary::ArrayVec; + +/// Limits the scope of `unsafe` auditing. +// New trait impls and fns that don't need to access internals should go below the module, not +// inside it! +mod safety_boundary { + use core::mem::MaybeUninit; + use crate::const_tools::cond_const; + + /// A growable contiguous collection backed by array. + #[derive(Copy)] + pub struct ArrayVec { + len: usize, + data: [MaybeUninit; CAP], + } + + impl ArrayVec { + // The bounds are const-unstable until 1.61 + cond_const! { + /// Creates an empty `ArrayVec`. + pub const(in rust_v_1_61 = "1.61") fn new() -> Self { + Self { + len: 0, + data: [MaybeUninit::uninit(); CAP], + } + } + + /// Creates an `ArrayVec` initialized with the contets of `slice`. + /// + /// # Panics + /// + /// If the slice is longer than `CAP`. + pub const(in rust_v_1_61 = "1.61") fn from_slice(slice: &[T]) -> Self { + assert!(slice.len() <= CAP); + let mut data = [MaybeUninit::uninit(); CAP]; + let mut i = 0; + // can't use mutable references and operators in const + while i < slice.len() { + data[i] = MaybeUninit::new(slice[i]); + i += 1; + } + + Self { + len: slice.len(), + data, + } + } + } + + // from_raw_parts is const-unstable until 1.64 + cond_const! { + /// Returns a reference to the underlying data. + pub const(in rust_v_1_64 = "1.64") fn as_slice(&self) -> &[T] { + let ptr = &self.data as *const _ as *const T; + unsafe { core::slice::from_raw_parts(ptr, self.len) } + } + } + + /// Returns a mutable reference to the underlying data. + pub fn as_mut_slice(&mut self) -> &mut [T] { + unsafe { &mut *(&mut self.data[..self.len] as *mut _ as *mut [T]) } + } + + /// Adds an element into `self`. + /// + /// # Panics + /// + /// If the length would increase past CAP. + pub fn push(&mut self, element: T) { + assert!(self.len < CAP); + self.data[self.len] = MaybeUninit::new(element); + self.len += 1; + } + + /// Copies and appends all elements from `slice` into `self`. + /// + /// # Panics + /// + /// If the length would increase past CAP. + pub fn extend_from_slice(&mut self, slice: &[T]) { + let new_len = self.len.checked_add(slice.len()).expect("integer/buffer overflow"); + assert!(new_len <= CAP, "buffer overflow"); + // SAFETY: MaybeUninit has the same layout as T + let slice = unsafe { &*(slice as *const _ as *const [MaybeUninit]) }; + self.data[self.len..].copy_from_slice(slice); + self.len = new_len; + } + } +} + +/// Clones the value *faster* than using `Copy`. +/// +/// Because we avoid copying the uninitialized part of the array this copies the value faster than +/// memcpy. +#[allow(clippy::non_canonical_clone_impl)] +impl Clone for ArrayVec { + fn clone(&self) -> Self { + Self::from_slice(self) + } +} + + +impl core::ops::Deref for ArrayVec { + type Target = [T]; + + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} + +impl core::ops::DerefMut for ArrayVec { + fn deref_mut(&mut self) -> &mut Self::Target { + self.as_mut_slice() + } +} + +impl Eq for ArrayVec {} + +impl PartialEq> for ArrayVec { + fn eq(&self, other: &ArrayVec) -> bool { + **self == **other + } +} + +impl PartialEq<[T]> for ArrayVec { + fn eq(&self, other: &[T]) -> bool { + **self == *other + } +} + +impl PartialEq> for [T] { + fn eq(&self, other: &ArrayVec) -> bool { + *self == **other + } +} + +impl PartialEq<[T; LEN]> for ArrayVec { + fn eq(&self, other: &[T; LEN]) -> bool { + **self == *other + } +} + +impl PartialEq> for [T; LEN] { + fn eq(&self, other: &ArrayVec) -> bool { + *self == **other + } +} + +impl Ord for ArrayVec { + fn cmp(&self, other: &ArrayVec) -> core::cmp::Ordering { + (**self).cmp(&**other) + } +} + +impl PartialOrd> for ArrayVec { + fn partial_cmp(&self, other: &ArrayVec) -> Option { + (**self).partial_cmp(&**other) + } +} + +impl fmt::Debug for ArrayVec { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl core::hash::Hash for ArrayVec { + fn hash(&self, state: &mut H) { + core::hash::Hash::hash(&**self, state) + } +} + + +#[cfg(test)] +mod tests { + use super::ArrayVec; + + #[test] + fn arrayvec_ops() { + let mut av = ArrayVec::<_, 1>::new(); + assert!(av.is_empty()); + av.push(42); + assert_eq!(av.len(), 1); + assert_eq!(av, [42]); + } + + #[test] + #[should_panic] + fn overflow_push() { + let mut av = ArrayVec::<_, 0>::new(); + av.push(42); + } + + #[test] + #[should_panic] + fn overflow_extend() { + let mut av = ArrayVec::<_, 0>::new(); + av.extend_from_slice(&[42]); + } +} diff --git a/internals/src/lib.rs b/internals/src/lib.rs index 025fc553..f9f41410 100644 --- a/internals/src/lib.rs +++ b/internals/src/lib.rs @@ -20,6 +20,7 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std; +pub mod array_vec; pub mod const_tools; pub mod error; pub mod macros;