diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a390ff5..e8a01e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,23 +17,21 @@ jobs: strategy: matrix: include: - - rust: 1.51.0 # MSRV + - rust: 1.57.0 # MSRV features: serde experimental: false - # doctest of `ArrayVec::spare_capacity_mut` has MSRV 1.55 - test-args: --skip spare_capacity_mut - rust: 1.70.0 features: serde experimental: false - rust: stable - features: + features: const bench: true experimental: false - rust: beta - features: serde + features: serde, const experimental: false - rust: nightly - features: serde, borsh, zeroize + features: serde, borsh, zeroize, const experimental: false steps: @@ -42,7 +40,7 @@ jobs: with: toolchain: ${{ matrix.rust }} - name: Pin versions for MSRV - if: "${{ matrix.rust == '1.51.0' }}" + if: "${{ matrix.rust == '1.57.0' }}" run: | cargo update -p serde_test --precise 1.0.163 cargo update -p serde --precise 1.0.69 diff --git a/Cargo.toml b/Cargo.toml index 13917b0..bdfe19f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.7.6" authors = ["bluss"] license = "MIT OR Apache-2.0" edition = "2018" -rust-version = "1.51" +rust-version = "1.57" description = "A vector with fixed capacity, backed by an array (it can be stored on the stack too). Implements fixed capacity ArrayVec and ArrayString." documentation = "https://docs.rs/arrayvec/" @@ -48,6 +48,7 @@ harness = false [features] default = ["std"] std = [] +const = [] [profile.bench] debug = true @@ -55,7 +56,7 @@ debug = true debug = true [package.metadata.docs.rs] -features = ["borsh", "serde", "zeroize"] +features = ["borsh", "serde", "zeroize", "const"] [package.metadata.release] no-dev-version = true diff --git a/src/array_string.rs b/src/array_string.rs index 227e01d..41963a3 100644 --- a/src/array_string.rs +++ b/src/array_string.rs @@ -17,6 +17,7 @@ use crate::CapacityError; use crate::LenUint; use crate::char::encode_utf8; use crate::utils::MakeMaybeUninit; +use crate::const_fn; #[cfg(feature="serde")] use serde::{Serialize, Deserialize, Serializer, Deserializer}; @@ -50,6 +51,8 @@ impl Default for ArrayString impl ArrayString { + + const_fn!{ /// Create a new empty `ArrayString`. /// /// Capacity is inferred from the type parameter. @@ -62,12 +65,12 @@ impl ArrayString /// assert_eq!(&string[..], "foo"); /// assert_eq!(string.capacity(), 16); /// ``` - pub fn new() -> ArrayString { + pub const fn new() -> ArrayString { assert_capacity_limit!(CAP); unsafe { ArrayString { xs: MaybeUninit::uninit().assume_init(), len: 0 } } - } + }} /// Create a new empty `ArrayString` (const fn). /// @@ -91,6 +94,7 @@ impl ArrayString #[inline] pub const fn is_empty(&self) -> bool { self.len() == 0 } + const_fn!{ /// Create a new `ArrayString` from a `str`. /// /// Capacity is inferred from the type parameter. @@ -105,12 +109,15 @@ impl ArrayString /// assert_eq!(string.len(), 3); /// assert_eq!(string.capacity(), 3); /// ``` - pub fn from(s: &str) -> Result> { + pub const fn from(s: &str) -> Result> { let mut arraystr = Self::new(); - arraystr.try_push_str(s)?; - Ok(arraystr) - } + match arraystr.try_push_str(s) { + Ok(()) => Ok(arraystr), + Err(e) => Err(e), + } + }} + const_fn!{ /// Create a new `ArrayString` from a byte string literal. /// /// **Errors** if the byte string literal is not valid UTF-8. @@ -120,9 +127,12 @@ impl ArrayString /// /// let string = ArrayString::from_byte_string(b"hello world").unwrap(); /// ``` - pub fn from_byte_string(b: &[u8; CAP]) -> Result { - let len = str::from_utf8(b)?.len(); - debug_assert_eq!(len, CAP); + pub const fn from_byte_string(b: &[u8; CAP]) -> Result { + let len = match str::from_utf8(b) { + Ok(str) => str.len(), + Err(e) => return Err(e), + }; + debug_assert!(len == CAP); let mut vec = Self::new(); unsafe { (b as *const [u8; CAP] as *const [MaybeUninit; CAP]) @@ -130,8 +140,9 @@ impl ArrayString vec.set_len(CAP); } Ok(vec) - } + }} + const_fn!{ /// Create a new `ArrayString` value fully filled with ASCII NULL characters (`\0`). Useful /// to be used as a buffer to collect external data or as a buffer for intermediate processing. /// @@ -142,7 +153,7 @@ impl ArrayString /// assert_eq!(string.len(), 16); /// ``` #[inline] - pub fn zero_filled() -> Self { + pub const fn zero_filled() -> Self { assert_capacity_limit!(CAP); // SAFETY: `assert_capacity_limit` asserts that `len` won't overflow and // `zeroed` fully fills the array with nulls. @@ -152,7 +163,7 @@ impl ArrayString len: CAP as _ } } - } + }} /// Return the capacity of the `ArrayString`. /// @@ -209,6 +220,7 @@ impl ArrayString self.try_push(c).unwrap(); } + const_fn!{ /// Adds the given char to the end of the string. /// /// Returns `Ok` if the push succeeds. @@ -227,7 +239,7 @@ impl ArrayString /// assert_eq!(&string[..], "ab"); /// assert_eq!(overflow.unwrap_err().element(), 'c'); /// ``` - pub fn try_push(&mut self, c: char) -> Result<(), CapacityError> { + pub const fn try_push(&mut self, c: char) -> Result<(), CapacityError> { let len = self.len(); unsafe { let ptr = self.as_mut_ptr().add(len); @@ -240,7 +252,7 @@ impl ArrayString Err(_) => Err(CapacityError::new(c)), } } - } + }} /// Adds the given string slice to the end of the string. /// @@ -261,6 +273,7 @@ impl ArrayString self.try_push_str(s).unwrap() } + const_fn!{ /// Adds the given string slice to the end of the string. /// /// Returns `Ok` if the push succeeds. @@ -281,7 +294,7 @@ impl ArrayString /// assert_eq!(overflow1.unwrap_err().element(), "bc"); /// assert_eq!(overflow2.unwrap_err().element(), "ef"); /// ``` - pub fn try_push_str<'a>(&mut self, s: &'a str) -> Result<(), CapacityError<&'a str>> { + pub const fn try_push_str<'a>(&mut self, s: &'a str) -> Result<(), CapacityError<&'a str>> { if s.len() > self.capacity() - self.len() { return Err(CapacityError::new(s)); } @@ -293,7 +306,7 @@ impl ArrayString self.set_len(newl); } Ok(()) - } + }} /// Removes the last character from the string and returns it. /// @@ -340,7 +353,7 @@ impl ArrayString /// ``` pub fn truncate(&mut self, new_len: usize) { if new_len <= self.len() { - assert!(self.is_char_boundary(new_len)); + assert!(self.as_str().is_char_boundary(new_len)); unsafe { // In libstd truncate is called on the underlying vector, // which in turns drops each element. @@ -387,13 +400,15 @@ impl ArrayString ch } + const_fn!{ /// Make the string empty. - pub fn clear(&mut self) { + pub const fn clear(&mut self) { unsafe { self.set_len(0); } - } + }} + const_fn!{ /// Set the strings’s length. /// /// This function is `unsafe` because it changes the notion of the @@ -401,31 +416,41 @@ impl ArrayString /// /// This method uses *debug assertions* to check the validity of `length` /// and may use other debug assertions. - pub unsafe fn set_len(&mut self, length: usize) { + pub const unsafe fn set_len(&mut self, length: usize) { // type invariant that capacity always fits in LenUint debug_assert!(length <= self.capacity()); self.len = length as LenUint; - } + }} + const_fn!{ /// Return a string slice of the whole `ArrayString`. - pub fn as_str(&self) -> &str { - self - } + pub const fn as_str(&self) -> &str { + unsafe { + let sl = slice::from_raw_parts(self.as_ptr(), self.len()); + str::from_utf8_unchecked(sl) + } + }} + const_fn!{ /// Return a mutable string slice of the whole `ArrayString`. - pub fn as_mut_str(&mut self) -> &mut str { - self - } + pub const fn as_mut_str(&mut self) -> &mut str { + unsafe { + let len = self.len(); + let sl = slice::from_raw_parts_mut(self.as_mut_ptr(), len); + str::from_utf8_unchecked_mut(sl) + } + }} /// Return a raw pointer to the string's buffer. - pub fn as_ptr(&self) -> *const u8 { + pub const fn as_ptr(&self) -> *const u8 { self.xs.as_ptr() as *const u8 } + const_fn!{ /// Return a raw mutable pointer to the string's buffer. - pub fn as_mut_ptr(&mut self) -> *mut u8 { + pub const fn as_mut_ptr(&mut self) -> *mut u8 { self.xs.as_mut_ptr() as *mut u8 - } + }} } impl Deref for ArrayString @@ -433,10 +458,7 @@ impl Deref for ArrayString type Target = str; #[inline] fn deref(&self) -> &str { - unsafe { - let sl = slice::from_raw_parts(self.as_ptr(), self.len()); - str::from_utf8_unchecked(sl) - } + self.as_str() } } @@ -444,11 +466,7 @@ impl DerefMut for ArrayString { #[inline] fn deref_mut(&mut self) -> &mut str { - unsafe { - let len = self.len(); - let sl = slice::from_raw_parts_mut(self.as_mut_ptr(), len); - str::from_utf8_unchecked_mut(sl) - } + self.as_mut_str() } } diff --git a/src/arrayvec.rs b/src/arrayvec.rs index e5ea52d..eb9086c 100644 --- a/src/arrayvec.rs +++ b/src/arrayvec.rs @@ -22,8 +22,8 @@ use serde::{Serialize, Deserialize, Serializer, Deserializer}; use crate::LenUint; use crate::errors::CapacityError; -use crate::arrayvec_impl::ArrayVecImpl; use crate::utils::MakeMaybeUninit; +use crate::const_fn; /// A vector with a fixed capacity. /// @@ -65,6 +65,7 @@ impl ArrayVec { /// Capacity const CAPACITY: usize = CAP; + const_fn!{ /// Create a new empty `ArrayVec`. /// /// The maximum capacity is given by the generic parameter `CAP`. @@ -80,12 +81,12 @@ impl ArrayVec { /// ``` #[inline] #[track_caller] - pub fn new() -> ArrayVec { + pub const fn new() -> ArrayVec { assert_capacity_limit!(CAP); unsafe { ArrayVec { xs: MaybeUninit::uninit().assume_init(), len: 0 } } - } + }} /// Create a new empty `ArrayVec` (const fn). /// @@ -177,9 +178,10 @@ impl ArrayVec { /// ``` #[track_caller] pub fn push(&mut self, element: T) { - ArrayVecImpl::push(self, element) + self.try_push(element).unwrap() } + const_fn!{ /// Push `element` to the end of the vector. /// /// Return `Ok` if the push succeeds, or return an error if the vector @@ -202,10 +204,18 @@ impl ArrayVec { /// /// assert!(overflow.is_err()); /// ``` - pub fn try_push(&mut self, element: T) -> Result<(), CapacityError> { - ArrayVecImpl::try_push(self, element) - } + pub const fn try_push(&mut self, element: T) -> Result<(), CapacityError> { + if self.len() < Self::CAPACITY { + unsafe { + self.push_unchecked(element); + } + Ok(()) + } else { + Err(CapacityError::new(element)) + } + }} + const_fn!{ /// Push `element` to the end of the vector without checking the capacity. /// /// It is up to the caller to ensure the capacity of the vector is @@ -227,9 +237,12 @@ impl ArrayVec { /// /// assert_eq!(&array[..], &[1, 2]); /// ``` - pub unsafe fn push_unchecked(&mut self, element: T) { - ArrayVecImpl::push_unchecked(self, element) - } + pub const unsafe fn push_unchecked(&mut self, element: T) { + let len = self.len(); + debug_assert!(len < Self::CAPACITY); + ptr::write(self.as_mut_ptr().add(len), element); + self.set_len(len + 1); + }} /// Shortens the vector, keeping the first `len` elements and dropping /// the rest. @@ -247,19 +260,27 @@ impl ArrayVec { /// assert_eq!(&array[..], &[1, 2, 3]); /// ``` pub fn truncate(&mut self, new_len: usize) { - ArrayVecImpl::truncate(self, new_len) + unsafe { + let len = self.len(); + if new_len < len { + self.set_len(new_len); + let tail = slice::from_raw_parts_mut(self.as_mut_ptr().add(new_len), len - new_len); + ptr::drop_in_place(tail); + } + } } /// Remove all elements in the vector. pub fn clear(&mut self) { - ArrayVecImpl::clear(self) + self.truncate(0) } + const_fn!{ /// Get pointer to where element at `index` would be - unsafe fn get_unchecked_ptr(&mut self, index: usize) -> *mut T { + const unsafe fn get_unchecked_ptr(&mut self, index: usize) -> *mut T { self.as_mut_ptr().add(index) - } + }} /// Insert `element` at position `index`. /// @@ -332,6 +353,7 @@ impl ArrayVec { Ok(()) } + const_fn!{ /// Remove the last element in the vector and return it. /// /// Return `Some(` *element* `)` if the vector is non-empty, else `None`. @@ -346,9 +368,16 @@ impl ArrayVec { /// assert_eq!(array.pop(), Some(1)); /// assert_eq!(array.pop(), None); /// ``` - pub fn pop(&mut self) -> Option { - ArrayVecImpl::pop(self) - } + pub const fn pop(&mut self) -> Option { + if self.len() == 0 { + return None; + } + unsafe { + let new_len = self.len() - 1; + self.set_len(new_len); + Some(ptr::read(self.as_ptr().add(new_len))) + } + }} /// Remove the element at `index` and swap the last element into its place. /// @@ -535,6 +564,7 @@ impl ArrayVec { drop(g); } + const_fn!{ /// Returns the remaining spare capacity of the vector as a slice of /// `MaybeUninit`. /// @@ -565,11 +595,12 @@ impl ArrayVec { /// /// assert_eq!(&v[..], &[0, 1, 2]); /// ``` - pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { + pub const fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { let len = self.len(); - &mut self.xs[len..] - } + self.xs.split_at_mut(len).1 + }} + const_fn!{ /// Set the vector’s length without dropping or moving out elements /// /// This method is `unsafe` because it changes the notion of the @@ -577,12 +608,13 @@ impl ArrayVec { /// /// This method uses *debug assertions* to check that `length` is /// not greater than the capacity. - pub unsafe fn set_len(&mut self, length: usize) { + pub const unsafe fn set_len(&mut self, length: usize) { // type invariant that capacity always fits in LenUint debug_assert!(length <= self.capacity()); self.len = length as LenUint; - } + }} + const_fn!{ /// Copy all elements from the slice and append to the `ArrayVec`. /// /// ``` @@ -601,7 +633,7 @@ impl ArrayVec { /// slice. /// /// [`remaining_capacity`]: #method.remaining_capacity - pub fn try_extend_from_slice(&mut self, other: &[T]) -> Result<(), CapacityError> + pub const fn try_extend_from_slice(&mut self, other: &[T]) -> Result<(), CapacityError> where T: Copy, { if self.remaining_capacity() < other.len() { @@ -617,7 +649,7 @@ impl ArrayVec { self.set_len(self_len + other_len); } Ok(()) - } + }} /// Create a draining iterator that removes the specified range in the vector /// and yields the removed items from start to end. The element range is @@ -685,29 +717,32 @@ impl ArrayVec { } } + const_fn!{ /// Return the inner fixed size array, if it is full to its capacity. /// /// Return an `Ok` value with the array if length equals capacity, /// return an `Err` with self otherwise. - pub fn into_inner(self) -> Result<[T; CAP], Self> { + pub const fn into_inner(self) -> Result<[T; CAP], Self> { if self.len() < self.capacity() { Err(self) } else { unsafe { Ok(self.into_inner_unchecked()) } } - } + }} + const_fn!{ /// Return the inner fixed size array. /// /// Safety: /// This operation is safe if and only if length equals capacity. - pub unsafe fn into_inner_unchecked(self) -> [T; CAP] { - debug_assert_eq!(self.len(), self.capacity()); - let self_ = ManuallyDrop::new(self); - let array = ptr::read(self_.as_ptr() as *const [T; CAP]); - array - } - + pub const unsafe fn into_inner_unchecked(self) -> [T; CAP] { + debug_assert!(self.len() == self.capacity()); + let ptr = self.as_ptr(); + mem::forget(self); + ptr::read(ptr as *const [T; CAP]) + }} + + const_fn!{ /// Returns the ArrayVec, replacing the original with a new empty ArrayVec. /// /// ``` @@ -717,49 +752,38 @@ impl ArrayVec { /// assert_eq!([0, 1, 2, 3], v.take().into_inner().unwrap()); /// assert!(v.is_empty()); /// ``` - pub fn take(&mut self) -> Self { - mem::replace(self, Self::new()) - } + pub const fn take(&mut self) -> Self { + mem::replace(self, Self::new_const()) + }} + const_fn!{ /// Return a slice containing all elements of the vector. - pub fn as_slice(&self) -> &[T] { - ArrayVecImpl::as_slice(self) - } + pub const fn as_slice(&self) -> &[T] { + let len = self.len(); + unsafe { + slice::from_raw_parts(self.as_ptr(), len) + } + }} + const_fn!{ /// Return a mutable slice containing all elements of the vector. - pub fn as_mut_slice(&mut self) -> &mut [T] { - ArrayVecImpl::as_mut_slice(self) - } + pub const fn as_mut_slice(&mut self) -> &mut [T] { + let len = self.len(); + unsafe { + std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) + } + }} /// Return a raw pointer to the vector's buffer. - pub fn as_ptr(&self) -> *const T { - ArrayVecImpl::as_ptr(self) - } - - /// Return a raw mutable pointer to the vector's buffer. - pub fn as_mut_ptr(&mut self) -> *mut T { - ArrayVecImpl::as_mut_ptr(self) - } -} - -impl ArrayVecImpl for ArrayVec { - type Item = T; - const CAPACITY: usize = CAP; - - fn len(&self) -> usize { self.len() } - - unsafe fn set_len(&mut self, length: usize) { - debug_assert!(length <= CAP); - self.len = length as LenUint; - } - - fn as_ptr(&self) -> *const Self::Item { + pub const fn as_ptr(&self) -> *const T { self.xs.as_ptr() as _ } - fn as_mut_ptr(&mut self) -> *mut Self::Item { + const_fn!{ + /// Return a raw mutable pointer to the vector's buffer. + pub const fn as_mut_ptr(&mut self) -> *mut T { self.xs.as_mut_ptr() as _ - } + }} } impl Deref for ArrayVec { @@ -916,15 +940,17 @@ pub struct IntoIter { v: ArrayVec, } impl IntoIter { + const_fn!{ /// Returns the remaining items of this iterator as a slice. - pub fn as_slice(&self) -> &[T] { - &self.v[self.index..] - } + pub const fn as_slice(&self) -> &[T] { + self.v.as_slice().split_at(self.index).1 + }} + const_fn!{ /// Returns the remaining items of this iterator as a mutable slice. - pub fn as_mut_slice(&mut self) -> &mut [T] { - &mut self.v[self.index..] - } + pub const fn as_mut_slice(&mut self) -> &mut [T] { + self.v.as_mut_slice().split_at_mut(self.index).1 + }} } impl Iterator for IntoIter { @@ -1100,7 +1126,7 @@ impl Extend for ArrayVec { #[inline(never)] #[cold] #[track_caller] -fn extend_panic() { +const fn extend_panic() { panic!("ArrayVec: capacity exceeded in extend/from_iter"); } @@ -1160,15 +1186,16 @@ impl ArrayVec { } } +const_fn!{ /// Rawptr add but uses arithmetic distance for ZST -unsafe fn raw_ptr_add(ptr: *mut T, offset: usize) -> *mut T { +const unsafe fn raw_ptr_add(ptr: *mut T, offset: usize) -> *mut T { if mem::size_of::() == 0 { // Special case for ZST ptr.cast::().wrapping_add(offset).cast::() } else { ptr.add(offset) } -} +}} /// Create an `ArrayVec` from an iterator. /// diff --git a/src/arrayvec_impl.rs b/src/arrayvec_impl.rs deleted file mode 100644 index c5ebe7b..0000000 --- a/src/arrayvec_impl.rs +++ /dev/null @@ -1,87 +0,0 @@ -use std::ptr; -use std::slice; - -use crate::CapacityError; - -/// Implements basic arrayvec methods - based on a few required methods -/// for length and element access. -pub(crate) trait ArrayVecImpl { - type Item; - const CAPACITY: usize; - - fn len(&self) -> usize; - - unsafe fn set_len(&mut self, new_len: usize); - - /// Return a slice containing all elements of the vector. - fn as_slice(&self) -> &[Self::Item] { - let len = self.len(); - unsafe { - slice::from_raw_parts(self.as_ptr(), len) - } - } - - /// Return a mutable slice containing all elements of the vector. - fn as_mut_slice(&mut self) -> &mut [Self::Item] { - let len = self.len(); - unsafe { - std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) - } - } - - /// Return a raw pointer to the vector's buffer. - fn as_ptr(&self) -> *const Self::Item; - - /// Return a raw mutable pointer to the vector's buffer. - fn as_mut_ptr(&mut self) -> *mut Self::Item; - - #[track_caller] - fn push(&mut self, element: Self::Item) { - self.try_push(element).unwrap() - } - - fn try_push(&mut self, element: Self::Item) -> Result<(), CapacityError> { - if self.len() < Self::CAPACITY { - unsafe { - self.push_unchecked(element); - } - Ok(()) - } else { - Err(CapacityError::new(element)) - } - } - - unsafe fn push_unchecked(&mut self, element: Self::Item) { - let len = self.len(); - debug_assert!(len < Self::CAPACITY); - ptr::write(self.as_mut_ptr().add(len), element); - self.set_len(len + 1); - } - - fn pop(&mut self) -> Option { - if self.len() == 0 { - return None; - } - unsafe { - let new_len = self.len() - 1; - self.set_len(new_len); - Some(ptr::read(self.as_ptr().add(new_len))) - } - } - - fn clear(&mut self) { - self.truncate(0) - } - - fn truncate(&mut self, new_len: usize) { - unsafe { - let len = self.len(); - if new_len < len { - self.set_len(new_len); - let tail = slice::from_raw_parts_mut(self.as_mut_ptr().add(new_len), len - new_len); - ptr::drop_in_place(tail); - } - } - } -} - diff --git a/src/char.rs b/src/char.rs index 939b6b4..8969eec 100644 --- a/src/char.rs +++ b/src/char.rs @@ -22,6 +22,7 @@ const MAX_THREE_B: u32 = 0x10000; /// Placeholder pub struct EncodeUtf8Error; +crate::const_fn!{ /// Encode a char into buf using UTF-8. /// /// On success, return the byte length of the encoding (1, 2, 3 or 4).
@@ -29,7 +30,7 @@ pub struct EncodeUtf8Error; /// /// Safety: `ptr` must be writable for `len` bytes. #[inline] -pub unsafe fn encode_utf8(ch: char, ptr: *mut u8, len: usize) -> Result +pub const unsafe fn encode_utf8(ch: char, ptr: *mut u8, len: usize) -> Result { let code = ch as u32; if code < MAX_ONE_B && len >= 1 { @@ -52,7 +53,7 @@ pub unsafe fn encode_utf8(ch: char, ptr: *mut u8, len: usize) -> Result { + $(#[$($metas)*])* + $v $($t)+ + }; +} + +#[cfg(feature = "const")] +macro_rules! const_fn { + ($(#[$($metas:meta)*])* $v:vis const $($t:tt)+) => { + $(#[$($metas)*])* + $v const $($t)+ + }; +} + +pub(crate) use const_fn;