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(); +}