From 30d4c15dba5ec61aa96807353f37dd53e7668c77 Mon Sep 17 00:00:00 2001 From: Daniel Bloom Date: Sun, 23 Feb 2025 02:17:01 -0800 Subject: [PATCH 1/5] feat+refactor: Maximize `const` --- src/array_string.rs | 61 +++++++++++---------- src/arrayvec.rs | 124 +++++++++++++++++++++++-------------------- src/arrayvec_impl.rs | 87 ------------------------------ src/char.rs | 2 +- src/lib.rs | 1 - 5 files changed, 101 insertions(+), 174 deletions(-) delete mode 100644 src/arrayvec_impl.rs diff --git a/src/array_string.rs b/src/array_string.rs index 227e01d..c49e321 100644 --- a/src/array_string.rs +++ b/src/array_string.rs @@ -62,7 +62,7 @@ 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 } @@ -105,10 +105,12 @@ 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), + } } /// Create a new `ArrayString` from a byte string literal. @@ -120,9 +122,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]) @@ -142,7 +147,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. @@ -227,7 +232,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); @@ -281,7 +286,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)); } @@ -340,7 +345,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. @@ -388,7 +393,7 @@ impl ArrayString } /// Make the string empty. - pub fn clear(&mut self) { + pub const fn clear(&mut self) { unsafe { self.set_len(0); } @@ -401,29 +406,36 @@ 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; } /// 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) + } } /// 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 } /// 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 } } @@ -433,10 +445,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 +453,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..7bd95b4 100644 --- a/src/arrayvec.rs +++ b/src/arrayvec.rs @@ -22,7 +22,6 @@ use serde::{Serialize, Deserialize, Serializer, Deserializer}; use crate::LenUint; use crate::errors::CapacityError; -use crate::arrayvec_impl::ArrayVecImpl; use crate::utils::MakeMaybeUninit; /// A vector with a fixed capacity. @@ -80,7 +79,7 @@ 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 } @@ -177,7 +176,7 @@ impl ArrayVec { /// ``` #[track_caller] pub fn push(&mut self, element: T) { - ArrayVecImpl::push(self, element) + self.try_push(element).unwrap() } /// Push `element` to the end of the vector. @@ -202,8 +201,15 @@ 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)) + } } /// Push `element` to the end of the vector without checking the capacity. @@ -227,8 +233,11 @@ 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 @@ -247,17 +256,24 @@ 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) } /// 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) } @@ -346,8 +362,15 @@ 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. @@ -565,9 +588,9 @@ 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 } /// Set the vector’s length without dropping or moving out elements @@ -577,7 +600,7 @@ 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; @@ -601,7 +624,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() { @@ -689,7 +712,7 @@ impl ArrayVec { /// /// 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 { @@ -701,11 +724,11 @@ impl ArrayVec { /// /// 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]) } /// Returns the ArrayVec, replacing the original with a new empty ArrayVec. @@ -717,49 +740,36 @@ 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()) } /// 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) + } } /// 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 { + /// 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 { @@ -917,13 +927,13 @@ pub struct IntoIter { } impl IntoIter { /// 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 } /// 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 } } @@ -1100,7 +1110,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"); } @@ -1161,7 +1171,7 @@ impl ArrayVec { } /// 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::() 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..87eab23 100644 --- a/src/char.rs +++ b/src/char.rs @@ -29,7 +29,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 { diff --git a/src/lib.rs b/src/lib.rs index 5c4bcee..7300319 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,7 +57,6 @@ macro_rules! assert_capacity_limit_const { } } -mod arrayvec_impl; mod arrayvec; mod array_string; mod char; From 52b3b1843bf7e9c5f62d8630ecc34f581aeecca0 Mon Sep 17 00:00:00 2001 From: Daniel Bloom Date: Sun, 23 Feb 2025 02:32:29 -0800 Subject: [PATCH 2/5] bump msrv --- .github/workflows/ci.yml | 11 +++++------ src/lib.rs | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a390ff5..8d789cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,8 +1,8 @@ on: push: - branches: [ master ] + branches: [master] pull_request: - branches: [ master ] + branches: [master] name: Continuous integration @@ -17,12 +17,12 @@ jobs: strategy: matrix: include: - - rust: 1.51.0 # MSRV + - rust: 1.83.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 + - rust: 1.84.0 features: serde experimental: false - rust: stable @@ -42,7 +42,7 @@ jobs: with: toolchain: ${{ matrix.rust }} - name: Pin versions for MSRV - if: "${{ matrix.rust == '1.51.0' }}" + if: "${{ matrix.rust == '1.83.0' }}" run: | cargo update -p serde_test --precise 1.0.163 cargo update -p serde --precise 1.0.69 @@ -80,7 +80,6 @@ jobs: run: | cargo rustc "--target=${{ matrix.target }}" --no-default-features --features "${{ matrix.features }}" - miri: runs-on: ubuntu-latest steps: diff --git a/src/lib.rs b/src/lib.rs index 7300319..5ab12c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,7 @@ //! //! ## Rust Version //! -//! This version of arrayvec requires Rust 1.51 or later. +//! This version of arrayvec requires Rust 1.83 or later. //! #![doc(html_root_url="https://docs.rs/arrayvec/0.7/")] #![cfg_attr(not(feature="std"), no_std)] From a074b5c0333da60e169bed5daf2d00cbc351fc3c Mon Sep 17 00:00:00 2001 From: Daniel Bloom Date: Sun, 23 Feb 2025 02:34:07 -0800 Subject: [PATCH 3/5] bump --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d789cf..8cb8188 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: experimental: false # doctest of `ArrayVec::spare_capacity_mut` has MSRV 1.55 test-args: --skip spare_capacity_mut - - rust: 1.84.0 + - rust: 1.85.0 features: serde experimental: false - rust: stable From e2ea8679140da95f498fc0de165c33922db09490 Mon Sep 17 00:00:00 2001 From: Daniel Bloom Date: Sun, 23 Feb 2025 02:35:06 -0800 Subject: [PATCH 4/5] remove skip --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8cb8188..657917e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,8 +20,6 @@ jobs: - rust: 1.83.0 # MSRV features: serde experimental: false - # doctest of `ArrayVec::spare_capacity_mut` has MSRV 1.55 - test-args: --skip spare_capacity_mut - rust: 1.85.0 features: serde experimental: false From e3caaa3f25de54e258b4859da8610b044534b324 Mon Sep 17 00:00:00 2001 From: Daniel Bloom Date: Mon, 24 Feb 2025 14:03:45 -0800 Subject: [PATCH 5/5] cargo toml version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 13917b0..2d157dc 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.83" 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/"