Skip to content

Commit 0c90469

Browse files
committed
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.
1 parent dfd882b commit 0c90469

File tree

4 files changed

+64
-22
lines changed

4 files changed

+64
-22
lines changed

src/array_string.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,28 @@ use std::str::FromStr;
1212
use std::str::Utf8Error;
1313

1414
use crate::CapacityError;
15+
use crate::LenUint;
1516
use crate::char::encode_utf8;
1617

1718
#[cfg(feature="serde")]
1819
use serde::{Serialize, Deserialize, Serializer, Deserializer};
1920

21+
2022
/// A string with a fixed capacity.
2123
///
2224
/// The `ArrayString` is a string backed by a fixed size array. It keeps track
23-
/// of its length.
25+
/// of its length, and is parameterized by `CAP` for the maximum capacity.
26+
///
27+
/// `CAP` is of type `usize` but is range limited to `u32::MAX`; attempting to create larger
28+
/// arrayvecs with larger capacity will panic.
2429
///
2530
/// The string is a contiguous value that you can store directly on the stack
2631
/// if needed.
2732
#[derive(Copy)]
2833
pub struct ArrayString<const CAP: usize> {
2934
// the `len` first elements of the array are initialized
3035
xs: [MaybeUninit<u8>; CAP],
31-
len: usize,
36+
len: LenUint,
3237
}
3338

3439
impl<const CAP: usize> Default for ArrayString<CAP>
@@ -55,21 +60,23 @@ impl<const CAP: usize> ArrayString<CAP>
5560
/// ```
5661
#[cfg(not(feature="unstable-const-fn"))]
5762
pub fn new() -> ArrayString<CAP> {
63+
assert_capacity_limit!(CAP);
5864
unsafe {
5965
ArrayString { xs: MaybeUninit::uninit().assume_init(), len: 0 }
6066
}
6167
}
6268

6369
#[cfg(feature="unstable-const-fn")]
6470
pub const fn new() -> ArrayString<CAP> {
71+
assert_capacity_limit!(CAP);
6572
unsafe {
6673
ArrayString { xs: MaybeUninit::uninit().assume_init(), len: 0 }
6774
}
6875
}
6976

7077
/// Return the length of the string.
7178
#[inline]
72-
pub fn len(&self) -> usize { self.len }
79+
pub fn len(&self) -> usize { self.len as usize }
7380

7481
/// Returns whether the string is empty.
7582
#[inline]
@@ -347,8 +354,9 @@ impl<const CAP: usize> ArrayString<CAP>
347354
/// This method uses *debug assertions* to check the validity of `length`
348355
/// and may use other debug assertions.
349356
pub unsafe fn set_len(&mut self, length: usize) {
357+
// type invariant that capacity always fits in LenUint
350358
debug_assert!(length <= self.capacity());
351-
self.len = length;
359+
self.len = length as LenUint;
352360
}
353361

354362
/// Return a string slice of the whole `ArrayString`.
@@ -371,7 +379,7 @@ impl<const CAP: usize> Deref for ArrayString<CAP>
371379
#[inline]
372380
fn deref(&self) -> &str {
373381
unsafe {
374-
let sl = slice::from_raw_parts(self.as_ptr(), self.len);
382+
let sl = slice::from_raw_parts(self.as_ptr(), self.len());
375383
str::from_utf8_unchecked(sl)
376384
}
377385
}
@@ -382,7 +390,8 @@ impl<const CAP: usize> DerefMut for ArrayString<CAP>
382390
#[inline]
383391
fn deref_mut(&mut self) -> &mut str {
384392
unsafe {
385-
let sl = slice::from_raw_parts_mut(self.as_mut_ptr(), self.len);
393+
let len = self.len();
394+
let sl = slice::from_raw_parts_mut(self.as_mut_ptr(), len);
386395
str::from_utf8_unchecked_mut(sl)
387396
}
388397
}

src/arrayvec.rs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use std::mem::MaybeUninit;
2020
#[cfg(feature="serde")]
2121
use serde::{Serialize, Deserialize, Serializer, Deserializer};
2222

23+
use crate::LenUint;
2324
use crate::errors::CapacityError;
2425
use crate::arrayvec_impl::ArrayVecImpl;
2526

@@ -29,17 +30,18 @@ use crate::arrayvec_impl::ArrayVecImpl;
2930
/// the number of initialized elements. The `ArrayVec<T, CAP>` is parameterized
3031
/// by `T` for the element type and `CAP` for the maximum capacity.
3132
///
33+
/// `CAP` is of type `usize` but is range limited to `u32::MAX`; attempting to create larger
34+
/// arrayvecs with larger capacity will panic.
35+
///
3236
/// The vector is a contiguous value (storing the elements inline) that you can store directly on
3337
/// the stack if needed.
3438
///
35-
/// It offers a simple API but also dereferences to a slice, so
36-
/// that the full slice API is available.
37-
///
38-
/// ArrayVec can be converted into a by value iterator.
39+
/// It offers a simple API but also dereferences to a slice, so that the full slice API is
40+
/// available. The ArrayVec can be converted into a by value iterator.
3941
pub struct ArrayVec<T, const CAP: usize> {
4042
// the `len` first elements of the array are initialized
4143
xs: [MaybeUninit<T>; CAP],
42-
len: usize,
44+
len: LenUint,
4345
}
4446

4547
impl<T, const CAP: usize> Drop for ArrayVec<T, CAP> {
@@ -76,13 +78,15 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
7678
/// ```
7779
#[cfg(not(feature="unstable-const-fn"))]
7880
pub fn new() -> ArrayVec<T, CAP> {
81+
assert_capacity_limit!(CAP);
7982
unsafe {
8083
ArrayVec { xs: MaybeUninit::uninit().assume_init(), len: 0 }
8184
}
8285
}
8386

8487
#[cfg(feature="unstable-const-fn")]
8588
pub const fn new() -> ArrayVec<T, CAP> {
89+
assert_capacity_limit!(CAP);
8690
unsafe {
8791
ArrayVec { xs: MaybeUninit::uninit().assume_init(), len: 0 }
8892
}
@@ -97,7 +101,7 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
97101
/// array.pop();
98102
/// assert_eq!(array.len(), 2);
99103
/// ```
100-
#[inline]
104+
#[inline(always)]
101105
pub fn len(&self) -> usize { self.len as usize }
102106

103107
/// Returns whether the `ArrayVec` is empty.
@@ -475,8 +479,9 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
475479
/// This method uses *debug assertions* to check that `length` is
476480
/// not greater than the capacity.
477481
pub unsafe fn set_len(&mut self, length: usize) {
482+
// type invariant that capacity always fits in LenUint
478483
debug_assert!(length <= self.capacity());
479-
self.len = length;
484+
self.len = length as LenUint;
480485
}
481486

482487
/// Copy all elements from the slice and append to the `ArrayVec`.
@@ -569,7 +574,7 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
569574

570575
// Calling `set_len` creates a fresh and thus unique mutable references, making all
571576
// older aliases we created invalid. So we cannot call that function.
572-
self.len = start;
577+
self.len = start as LenUint;
573578

574579
unsafe {
575580
Drain {
@@ -626,7 +631,7 @@ impl<T, const CAP: usize> ArrayVecImpl for ArrayVec<T, CAP> {
626631

627632
unsafe fn set_len(&mut self, length: usize) {
628633
debug_assert!(length <= CAP);
629-
self.len = length;
634+
self.len = length as LenUint;
630635
}
631636

632637
fn as_ptr(&self) -> *const Self::Item {
@@ -769,7 +774,7 @@ impl<T, const CAP: usize> Iterator for IntoIter<T, CAP> {
769774
type Item = T;
770775

771776
fn next(&mut self) -> Option<Self::Item> {
772-
if self.index == self.v.len {
777+
if self.index == self.v.len() {
773778
None
774779
} else {
775780
unsafe {
@@ -788,7 +793,7 @@ impl<T, const CAP: usize> Iterator for IntoIter<T, CAP> {
788793

789794
impl<T, const CAP: usize> DoubleEndedIterator for IntoIter<T, CAP> {
790795
fn next_back(&mut self) -> Option<Self::Item> {
791-
if self.index == self.v.len {
796+
if self.index == self.v.len() {
792797
None
793798
} else {
794799
unsafe {
@@ -963,7 +968,7 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
963968
value: &mut self.len,
964969
data: len,
965970
f: move |&len, self_len| {
966-
**self_len = len;
971+
**self_len = len as LenUint;
967972
}
968973
};
969974
let mut iter = iterable.into_iter();

src/lib.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,26 @@
2323
//!
2424
#![doc(html_root_url="https://docs.rs/arrayvec/0.5/")]
2525
#![cfg_attr(not(feature="std"), no_std)]
26-
#![cfg_attr(feature="unstable-const-fn", feature(const_fn, const_maybe_uninit_assume_init))]
26+
#![cfg_attr(feature="unstable-const-fn", feature(const_fn, const_maybe_uninit_assume_init, const_panic))]
2727

2828
#[cfg(feature="serde")]
2929
extern crate serde;
3030

3131
#[cfg(not(feature="std"))]
3232
extern crate core as std;
3333

34+
pub(crate) type LenUint = u32;
35+
36+
macro_rules! assert_capacity_limit {
37+
($cap:expr) => {
38+
if std::mem::size_of::<usize>() > std::mem::size_of::<LenUint>() {
39+
if CAP > LenUint::MAX as usize {
40+
panic!("ArrayVec: largest supported capacity is u32::MAX")
41+
}
42+
}
43+
}
44+
}
45+
3446
mod arrayvec_impl;
3547
mod arrayvec;
3648
mod array_string;

tests/tests.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -295,17 +295,17 @@ fn test_compact_size() {
295295
// 4 bytes + padding + length
296296
type ByteArray = ArrayVec<u8, 4>;
297297
println!("{}", mem::size_of::<ByteArray>());
298-
assert!(mem::size_of::<ByteArray>() <= 2 * mem::size_of::<usize>());
298+
assert!(mem::size_of::<ByteArray>() <= 2 * mem::size_of::<u32>());
299299

300300
// just length
301301
type EmptyArray = ArrayVec<u8, 0>;
302302
println!("{}", mem::size_of::<EmptyArray>());
303-
assert!(mem::size_of::<EmptyArray>() <= mem::size_of::<usize>());
303+
assert!(mem::size_of::<EmptyArray>() <= mem::size_of::<u32>());
304304

305305
// 3 elements + padding + length
306306
type QuadArray = ArrayVec<u32, 3>;
307307
println!("{}", mem::size_of::<QuadArray>());
308-
assert!(mem::size_of::<QuadArray>() <= 4 * 4 + mem::size_of::<usize>());
308+
assert!(mem::size_of::<QuadArray>() <= 4 * 4 + mem::size_of::<u32>());
309309
}
310310

311311
#[test]
@@ -711,3 +711,19 @@ fn test_try_from_argument() {
711711
let v = ArrayString::<16>::try_from(format_args!("Hello {}", 123)).unwrap();
712712
assert_eq!(&v, "Hello 123");
713713
}
714+
715+
#[test]
716+
fn allow_max_capacity_arrayvec_type() {
717+
// this type is allowed to be used (but can't be constructed)
718+
let _v: ArrayVec<(), {usize::MAX}>;
719+
}
720+
721+
#[should_panic(expected="ArrayVec: largest supported")]
722+
#[test]
723+
fn deny_max_capacity_arrayvec_value() {
724+
if mem::size_of::<usize>() <= mem::size_of::<u32>() {
725+
panic!("This test does not work on this platform. 'ArrayVec: largest supported'");
726+
}
727+
// this type is allowed to be used (but can't be constructed)
728+
let _v: ArrayVec<(), {usize::MAX}> = ArrayVec::new();
729+
}

0 commit comments

Comments
 (0)