From 0c90469b61ce73f9a9dc63f3ea16f56b12187f23 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 24 Mar 2021 18:14:57 +0100 Subject: [PATCH] FEAT: Use u32 for the length field in arrayvec Store the length as u32 internally. This is to shrink the size of the ArrayVec value (when possible, depending on element type). Inline storage vectors larger than u32::MAX are very unlikely to be useful - for these cases, prefer using Vec instead. It's not possible to have the CAP type parameter be of type u32 (missing features in const evaluation/const generics). We also have to panic at runtime instead of having a static assertion for capacity, for similar reasons. --- src/array_string.rs | 21 +++++++++++++++------ src/arrayvec.rs | 29 +++++++++++++++++------------ src/lib.rs | 14 +++++++++++++- tests/tests.rs | 22 +++++++++++++++++++--- 4 files changed, 64 insertions(+), 22 deletions(-) diff --git a/src/array_string.rs b/src/array_string.rs index c642e2b..076544f 100644 --- a/src/array_string.rs +++ b/src/array_string.rs @@ -12,15 +12,20 @@ use std::str::FromStr; use std::str::Utf8Error; use crate::CapacityError; +use crate::LenUint; use crate::char::encode_utf8; #[cfg(feature="serde")] use serde::{Serialize, Deserialize, Serializer, Deserializer}; + /// A string with a fixed capacity. /// /// The `ArrayString` is a string backed by a fixed size array. It keeps track -/// of its length. +/// of its length, and is parameterized by `CAP` for the maximum capacity. +/// +/// `CAP` is of type `usize` but is range limited to `u32::MAX`; attempting to create larger +/// arrayvecs with larger capacity will panic. /// /// The string is a contiguous value that you can store directly on the stack /// if needed. @@ -28,7 +33,7 @@ use serde::{Serialize, Deserialize, Serializer, Deserializer}; pub struct ArrayString { // the `len` first elements of the array are initialized xs: [MaybeUninit; CAP], - len: usize, + len: LenUint, } impl Default for ArrayString @@ -55,6 +60,7 @@ impl ArrayString /// ``` #[cfg(not(feature="unstable-const-fn"))] pub fn new() -> ArrayString { + assert_capacity_limit!(CAP); unsafe { ArrayString { xs: MaybeUninit::uninit().assume_init(), len: 0 } } @@ -62,6 +68,7 @@ impl ArrayString #[cfg(feature="unstable-const-fn")] pub const fn new() -> ArrayString { + assert_capacity_limit!(CAP); unsafe { ArrayString { xs: MaybeUninit::uninit().assume_init(), len: 0 } } @@ -69,7 +76,7 @@ impl ArrayString /// Return the length of the string. #[inline] - pub fn len(&self) -> usize { self.len } + pub fn len(&self) -> usize { self.len as usize } /// Returns whether the string is empty. #[inline] @@ -347,8 +354,9 @@ 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) { + // type invariant that capacity always fits in LenUint debug_assert!(length <= self.capacity()); - self.len = length; + self.len = length as LenUint; } /// Return a string slice of the whole `ArrayString`. @@ -371,7 +379,7 @@ impl Deref for ArrayString #[inline] fn deref(&self) -> &str { unsafe { - let sl = slice::from_raw_parts(self.as_ptr(), self.len); + let sl = slice::from_raw_parts(self.as_ptr(), self.len()); str::from_utf8_unchecked(sl) } } @@ -382,7 +390,8 @@ impl DerefMut for ArrayString #[inline] fn deref_mut(&mut self) -> &mut str { unsafe { - let sl = slice::from_raw_parts_mut(self.as_mut_ptr(), self.len); + let len = self.len(); + let sl = slice::from_raw_parts_mut(self.as_mut_ptr(), len); str::from_utf8_unchecked_mut(sl) } } diff --git a/src/arrayvec.rs b/src/arrayvec.rs index d1fa24a..2ab9b0d 100644 --- a/src/arrayvec.rs +++ b/src/arrayvec.rs @@ -20,6 +20,7 @@ use std::mem::MaybeUninit; #[cfg(feature="serde")] use serde::{Serialize, Deserialize, Serializer, Deserializer}; +use crate::LenUint; use crate::errors::CapacityError; use crate::arrayvec_impl::ArrayVecImpl; @@ -29,17 +30,18 @@ use crate::arrayvec_impl::ArrayVecImpl; /// the number of initialized elements. The `ArrayVec` is parameterized /// by `T` for the element type and `CAP` for the maximum capacity. /// +/// `CAP` is of type `usize` but is range limited to `u32::MAX`; attempting to create larger +/// arrayvecs with larger capacity will panic. +/// /// The vector is a contiguous value (storing the elements inline) that you can store directly on /// the stack if needed. /// -/// It offers a simple API but also dereferences to a slice, so -/// that the full slice API is available. -/// -/// ArrayVec can be converted into a by value iterator. +/// It offers a simple API but also dereferences to a slice, so that the full slice API is +/// available. The ArrayVec can be converted into a by value iterator. pub struct ArrayVec { // the `len` first elements of the array are initialized xs: [MaybeUninit; CAP], - len: usize, + len: LenUint, } impl Drop for ArrayVec { @@ -76,6 +78,7 @@ impl ArrayVec { /// ``` #[cfg(not(feature="unstable-const-fn"))] pub fn new() -> ArrayVec { + assert_capacity_limit!(CAP); unsafe { ArrayVec { xs: MaybeUninit::uninit().assume_init(), len: 0 } } @@ -83,6 +86,7 @@ impl ArrayVec { #[cfg(feature="unstable-const-fn")] pub const fn new() -> ArrayVec { + assert_capacity_limit!(CAP); unsafe { ArrayVec { xs: MaybeUninit::uninit().assume_init(), len: 0 } } @@ -97,7 +101,7 @@ impl ArrayVec { /// array.pop(); /// assert_eq!(array.len(), 2); /// ``` - #[inline] + #[inline(always)] pub fn len(&self) -> usize { self.len as usize } /// Returns whether the `ArrayVec` is empty. @@ -475,8 +479,9 @@ 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) { + // type invariant that capacity always fits in LenUint debug_assert!(length <= self.capacity()); - self.len = length; + self.len = length as LenUint; } /// Copy all elements from the slice and append to the `ArrayVec`. @@ -569,7 +574,7 @@ impl ArrayVec { // Calling `set_len` creates a fresh and thus unique mutable references, making all // older aliases we created invalid. So we cannot call that function. - self.len = start; + self.len = start as LenUint; unsafe { Drain { @@ -626,7 +631,7 @@ impl ArrayVecImpl for ArrayVec { unsafe fn set_len(&mut self, length: usize) { debug_assert!(length <= CAP); - self.len = length; + self.len = length as LenUint; } fn as_ptr(&self) -> *const Self::Item { @@ -769,7 +774,7 @@ impl Iterator for IntoIter { type Item = T; fn next(&mut self) -> Option { - if self.index == self.v.len { + if self.index == self.v.len() { None } else { unsafe { @@ -788,7 +793,7 @@ impl Iterator for IntoIter { impl DoubleEndedIterator for IntoIter { fn next_back(&mut self) -> Option { - if self.index == self.v.len { + if self.index == self.v.len() { None } else { unsafe { @@ -963,7 +968,7 @@ impl ArrayVec { value: &mut self.len, data: len, f: move |&len, self_len| { - **self_len = len; + **self_len = len as LenUint; } }; let mut iter = iterable.into_iter(); diff --git a/src/lib.rs b/src/lib.rs index e0a5817..53a5e16 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ //! #![doc(html_root_url="https://docs.rs/arrayvec/0.5/")] #![cfg_attr(not(feature="std"), no_std)] -#![cfg_attr(feature="unstable-const-fn", feature(const_fn, const_maybe_uninit_assume_init))] +#![cfg_attr(feature="unstable-const-fn", feature(const_fn, const_maybe_uninit_assume_init, const_panic))] #[cfg(feature="serde")] extern crate serde; @@ -31,6 +31,18 @@ extern crate serde; #[cfg(not(feature="std"))] extern crate core as std; +pub(crate) type LenUint = u32; + +macro_rules! assert_capacity_limit { + ($cap:expr) => { + if std::mem::size_of::() > std::mem::size_of::() { + if CAP > LenUint::MAX as usize { + panic!("ArrayVec: largest supported capacity is u32::MAX") + } + } + } +} + mod arrayvec_impl; mod arrayvec; mod array_string; diff --git a/tests/tests.rs b/tests/tests.rs index b89529e..f4698e0 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -295,17 +295,17 @@ fn test_compact_size() { // 4 bytes + padding + length type ByteArray = ArrayVec; println!("{}", mem::size_of::()); - assert!(mem::size_of::() <= 2 * mem::size_of::()); + assert!(mem::size_of::() <= 2 * mem::size_of::()); // just length type EmptyArray = ArrayVec; println!("{}", mem::size_of::()); - assert!(mem::size_of::() <= mem::size_of::()); + assert!(mem::size_of::() <= mem::size_of::()); // 3 elements + padding + length type QuadArray = ArrayVec; println!("{}", mem::size_of::()); - assert!(mem::size_of::() <= 4 * 4 + mem::size_of::()); + assert!(mem::size_of::() <= 4 * 4 + mem::size_of::()); } #[test] @@ -711,3 +711,19 @@ fn test_try_from_argument() { let v = ArrayString::<16>::try_from(format_args!("Hello {}", 123)).unwrap(); assert_eq!(&v, "Hello 123"); } + +#[test] +fn allow_max_capacity_arrayvec_type() { + // this type is allowed to be used (but can't be constructed) + let _v: ArrayVec<(), {usize::MAX}>; +} + +#[should_panic(expected="ArrayVec: largest supported")] +#[test] +fn deny_max_capacity_arrayvec_value() { + if mem::size_of::() <= mem::size_of::() { + panic!("This test does not work on this platform. 'ArrayVec: largest supported'"); + } + // this type is allowed to be used (but can't be constructed) + let _v: ArrayVec<(), {usize::MAX}> = ArrayVec::new(); +}