Compare commits

...

10 Commits

Author SHA1 Message Date
wub 77041876ee Add len_u16 and len_u8 features. 2026-05-24 10:31:01 -07:00
Niklas Fiekas 1bc606d8c8 Fix warning about hidden elided lifetime in Drain 2026-05-03 14:24:03 +02:00
Sergey "Shnatsel" Davidoff 29daea95b9 Fix double-free for ZSTs with Drop in .extend() 2026-05-03 14:23:52 +02:00
Sergey "Shnatsel" Davidoff 0ff49b9614 add a test demonstrating double-free in extend() for ZSTs 2026-05-03 14:23:52 +02:00
Kornel 812c83a2b1 Use 16-bit lengths on 16-bit targets 2024-10-17 10:55:15 +02:00
Tobias Bucher 4ef0e89028 Add ArrayVec::spare_capacity_mut
This mirrors `Vec::spare_capacity_mut`. Description and example are
taken from this function, too.

CC #278
2024-10-16 21:18:15 +02:00
Ulrik Sverdrup 0aede877fe 0.7.6 2024-08-17 14:13:49 +02:00
Ulrik Sverdrup 909c766e90 Exclude AsRef<Path> for no-std builds
And test no-std in CI.
2024-08-17 15:12:44 +03:00
Ulrik Sverdrup f3732a0b4c 0.7.5 2024-08-17 14:40:27 +03:00
Ulrik Sverdrup b629f5220a Update ci actions 2024-07-31 15:22:35 +02:00
7 changed files with 147 additions and 29 deletions
+33 -8
View File
@@ -20,6 +20,8 @@ jobs:
- rust: 1.51.0 # MSRV - rust: 1.51.0 # MSRV
features: serde features: serde
experimental: false experimental: false
# doctest of `ArrayVec::spare_capacity_mut` has MSRV 1.55
test-args: --skip spare_capacity_mut
- rust: 1.70.0 - rust: 1.70.0
features: serde features: serde
experimental: false experimental: false
@@ -35,31 +37,54 @@ jobs:
experimental: false experimental: false
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1 - uses: dtolnay/rust-toolchain@master
with: with:
profile: minimal
toolchain: ${{ matrix.rust }} toolchain: ${{ matrix.rust }}
override: true
- name: Pin versions for MSRV - name: Pin versions for MSRV
if: "${{ matrix.rust == '1.51.0' }}" if: "${{ matrix.rust == '1.51.0' }}"
run: | run: |
cargo update -p serde_test --precise 1.0.163 cargo update -p serde_test --precise 1.0.163
cargo update -p serde --precise 1.0.69 cargo update -p serde --precise 1.0.69
- name: Build
run: |
cargo build -v --no-default-features
cargo build --verbose --features "${{ matrix.features }}"
- name: Tests - name: Tests
run: | run: |
cargo build --verbose --features "${{ matrix.features }}"
cargo doc --verbose --features "${{ matrix.features }}" --no-deps cargo doc --verbose --features "${{ matrix.features }}" --no-deps
cargo test --verbose --features "${{ matrix.features }}" cargo test --verbose --features "${{ matrix.features }}" -- ${{ matrix.test-args }}
cargo test --release --verbose --features "${{ matrix.features }}" cargo test --release --verbose --features "${{ matrix.features }}" -- ${{ matrix.test-args }}
- name: Test run benchmarks - name: Test run benchmarks
if: matrix.bench != '' if: matrix.bench != ''
run: cargo test -v --benches run: cargo test -v --benches
nostd:
runs-on: ubuntu-latest
continue-on-error: false
strategy:
matrix:
include:
- rust: stable
target: thumbv6m-none-eabi
features: zeroize
name: nostd/${{ matrix.target }}/${{ matrix.rust }}
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
targets: ${{ matrix.target }}
- name: Tests
run: |
cargo rustc "--target=${{ matrix.target }}" --no-default-features --features "${{ matrix.features }}"
miri: miri:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Install Miri - name: Install Miri
run: | run: |
rustup toolchain install nightly --component miri rustup toolchain install nightly --component miri
+11 -3
View File
@@ -1,12 +1,20 @@
Recent Changes (arrayvec) Recent Changes (arrayvec)
========================= =========================
## 0.7.6
- Fix no-std build [#274](https://github.com/bluss/arrayvec/pull/274)
## 0.7.5 ## 0.7.5
- Add `as_ptr` and `as_mut_ptr` to `ArrayString` by @YuhanLiin - Add `as_ptr` and `as_mut_ptr` to `ArrayString` [@YuhanLiin](https://github.com/YuhanLiin) [#260](https://github.com/bluss/arrayvec/pull/260)
- Add borsh serialization support by @honzasp and @Fuuzetsu - Add borsh serialization support by @honzasp and @Fuuzetsu [#259](https://github.com/bluss/arrayvec/pull/259)
- Move length field before before data in ArrayVec and ArrayString by @JakkuSakura - Move length field before before data in ArrayVec and ArrayString by @JakkuSakura [#255](https://github.com/bluss/arrayvec/pull/255)
- Fix miri error for ZST case in extend by @bluss - Fix miri error for ZST case in extend by @bluss
- implement AsRef<Path> for ArrayString by [@Zoybean](https://github.com/Zoybean) [#218](https://github.com/bluss/arrayvec/pull/218)
- Fix typos in changelog by [@striezel](https://github.com/striezel) [#241](https://github.com/bluss/arrayvec/pull/241)
- Add `as_slice`, `as_mut_slice` methods to `IntoIter` by [@clarfonthey](https://github.com/clarfonthey) [#224](https://github.com/bluss/arrayvec/pull/224)
## 0.7.4 ## 0.7.4
+3 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "arrayvec" name = "arrayvec"
version = "0.7.4" version = "0.7.6"
authors = ["bluss"] authors = ["bluss"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
edition = "2018" edition = "2018"
@@ -48,6 +48,8 @@ harness = false
[features] [features]
default = ["std"] default = ["std"]
std = [] std = []
len_u16 = []
len_u8 = []
[profile.bench] [profile.bench]
debug = true debug = true
+6 -3
View File
@@ -5,6 +5,7 @@ use std::fmt;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
#[cfg(feature="std")]
use std::path::Path; use std::path::Path;
use std::ptr; use std::ptr;
use std::slice; use std::slice;
@@ -26,8 +27,9 @@ use serde::{Serialize, Deserialize, Serializer, Deserializer};
/// The `ArrayString` is a string backed by a fixed size array. It keeps track /// The `ArrayString` is a string backed by a fixed size array. It keeps track
/// of its length, and is parameterized by `CAP` for the maximum capacity. /// 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 /// The length is stored in `u32` by default, and can be changed to `u16` or `u8`
/// arrayvecs with larger capacity will panic. /// by `len_u16` or `len_u8` feature. Attempting to create arrayvecs with larger
/// capacity will panic.
/// ///
/// The string is a contiguous value that you can store directly on the stack /// The string is a contiguous value that you can store directly on the stack
/// if needed. /// if needed.
@@ -75,7 +77,7 @@ impl<const CAP: usize> ArrayString<CAP>
/// ``` /// ```
/// use arrayvec::ArrayString; /// use arrayvec::ArrayString;
/// ///
/// static ARRAY: ArrayString<1024> = ArrayString::new_const(); /// static ARRAY: ArrayString<255> = ArrayString::new_const();
/// ``` /// ```
pub const fn new_const() -> ArrayString<CAP> { pub const fn new_const() -> ArrayString<CAP> {
assert_capacity_limit_const!(CAP); assert_capacity_limit_const!(CAP);
@@ -502,6 +504,7 @@ impl<const CAP: usize> fmt::Debug for ArrayString<CAP>
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) }
} }
#[cfg(feature="std")]
impl<const CAP: usize> AsRef<Path> for ArrayString<CAP> { impl<const CAP: usize> AsRef<Path> for ArrayString<CAP> {
fn as_ref(&self) -> &Path { fn as_ref(&self) -> &Path {
self.as_str().as_ref() self.as_str().as_ref()
+46 -5
View File
@@ -31,8 +31,9 @@ use crate::utils::MakeMaybeUninit;
/// the number of initialized elements. The `ArrayVec<T, CAP>` is parameterized /// the number of initialized elements. The `ArrayVec<T, CAP>` is parameterized
/// by `T` for the element type and `CAP` for the maximum capacity. /// 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 /// The length is stored in `u32` by default, and can be changed to `u16` or `u8`
/// arrayvecs with larger capacity will panic. /// by `len_u16` or `len_u8` feature. Attempting to create arrayvecs with larger
/// capacity will panic.
/// ///
/// The vector is a contiguous value (storing the elements inline) that you can store directly on /// The vector is a contiguous value (storing the elements inline) that you can store directly on
/// the stack if needed. /// the stack if needed.
@@ -94,7 +95,7 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
/// ``` /// ```
/// use arrayvec::ArrayVec; /// use arrayvec::ArrayVec;
/// ///
/// static ARRAY: ArrayVec<u8, 1024> = ArrayVec::new_const(); /// static ARRAY: ArrayVec<u8, 255> = ArrayVec::new_const();
/// ``` /// ```
pub const fn new_const() -> ArrayVec<T, CAP> { pub const fn new_const() -> ArrayVec<T, CAP> {
assert_capacity_limit_const!(CAP); assert_capacity_limit_const!(CAP);
@@ -535,6 +536,41 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
drop(g); drop(g);
} }
/// Returns the remaining spare capacity of the vector as a slice of
/// `MaybeUninit<T>`.
///
/// The returned slice can be used to fill the vector with data (e.g. by
/// reading from a file) before marking the data as initialized using the
/// [`set_len`] method.
///
/// [`set_len`]: ArrayVec::set_len
///
/// # Examples
///
/// ```
/// use arrayvec::ArrayVec;
///
/// // Allocate vector big enough for 10 elements.
/// let mut v: ArrayVec<i32, 10> = ArrayVec::new();
///
/// // Fill in the first 3 elements.
/// let uninit = v.spare_capacity_mut();
/// uninit[0].write(0);
/// uninit[1].write(1);
/// uninit[2].write(2);
///
/// // Mark the first 3 elements of the vector as being initialized.
/// unsafe {
/// v.set_len(3);
/// }
///
/// assert_eq!(&v[..], &[0, 1, 2]);
/// ```
pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit<T>] {
let len = self.len();
&mut self.xs[len..]
}
/// Set the vectors length without dropping or moving out elements /// Set the vectors length without dropping or moving out elements
/// ///
/// This method is `unsafe` because it changes the notion of the /// This method is `unsafe` because it changes the notion of the
@@ -602,7 +638,7 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
/// assert_eq!(&v1[..], &[3]); /// assert_eq!(&v1[..], &[3]);
/// assert_eq!(&v2[..], &[1, 2]); /// assert_eq!(&v2[..], &[1, 2]);
/// ``` /// ```
pub fn drain<R>(&mut self, range: R) -> Drain<T, CAP> pub fn drain<R>(&mut self, range: R) -> Drain<'_, T, CAP>
where R: RangeBounds<usize> where R: RangeBounds<usize>
{ {
// Memory safety // Memory safety
@@ -629,7 +665,7 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
self.drain_range(start, end) self.drain_range(start, end)
} }
fn drain_range(&mut self, start: usize, end: usize) -> Drain<T, CAP> fn drain_range(&mut self, start: usize, end: usize) -> Drain<'_, T, CAP>
{ {
let len = self.len(); let len = self.len();
@@ -1102,6 +1138,11 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
debug_assert_ne!(ptr, end_ptr); debug_assert_ne!(ptr, end_ptr);
if mem::size_of::<T>() != 0 { if mem::size_of::<T>() != 0 {
ptr.write(elt); ptr.write(elt);
} else {
// The ZST element has logically been moved into the vector.
// There is no memory to write, but dropping `elt` here would
// drop it once now and once again when the vector is dropped.
mem::forget(elt);
} }
ptr = raw_ptr_add(ptr, 1); ptr = raw_ptr_add(ptr, 1);
guard.data += 1; guard.data += 1;
+17 -2
View File
@@ -7,6 +7,14 @@
//! - Optional, enabled by default //! - Optional, enabled by default
//! - Use libstd; disable to use `no_std` instead. //! - Use libstd; disable to use `no_std` instead.
//! //!
//! - `len_u16`
//! - Optional.
//! - Use `u16` as length type.
//!
//! - `len_u8`
//! - Optional.
//! - Use `u8` as length type.
//!
//! - `serde` //! - `serde`
//! - Optional //! - Optional
//! - Enable serialization for ArrayVec and ArrayString using serde 1.x //! - Enable serialization for ArrayVec and ArrayString using serde 1.x
@@ -28,13 +36,20 @@ extern crate serde;
#[cfg(not(feature="std"))] #[cfg(not(feature="std"))]
extern crate core as std; extern crate core as std;
#[cfg(all(not(feature="len_u8"), not(feature="len_u16")))]
pub(crate) type LenUint = u32; pub(crate) type LenUint = u32;
#[cfg(feature="len_u16")]
pub(crate) type LenUint = u16;
#[cfg(feature="len_u8")]
pub(crate) type LenUint = u8;
macro_rules! assert_capacity_limit { macro_rules! assert_capacity_limit {
($cap:expr) => { ($cap:expr) => {
if std::mem::size_of::<usize>() > std::mem::size_of::<LenUint>() { if std::mem::size_of::<usize>() > std::mem::size_of::<LenUint>() {
if $cap > LenUint::MAX as usize { if $cap > LenUint::MAX as usize {
panic!("ArrayVec: largest supported capacity is u32::MAX") panic!("ArrayVec: largest supported capacity is {}", LenUint::MAX)
} }
} }
} }
@@ -44,7 +59,7 @@ macro_rules! assert_capacity_limit_const {
($cap:expr) => { ($cap:expr) => {
if std::mem::size_of::<usize>() > std::mem::size_of::<LenUint>() { if std::mem::size_of::<usize>() > std::mem::size_of::<LenUint>() {
if $cap > LenUint::MAX as usize { if $cap > LenUint::MAX as usize {
[/*ArrayVec: largest supported capacity is u32::MAX*/][$cap] [/*ArrayVec: largest supported capacity is LenUint::MAX*/][$cap]
} }
} }
} }
+31 -7
View File
@@ -75,6 +75,7 @@ fn test_try_from_slice_error() {
} }
#[test] #[test]
#[cfg(feature="len_u16")]
fn test_u16_index() { fn test_u16_index() {
const N: usize = 4096; const N: usize = 4096;
let mut vec: ArrayVec<_, N> = ArrayVec::new(); let mut vec: ArrayVec<_, N> = ArrayVec::new();
@@ -680,8 +681,9 @@ fn test_pop_at() {
} }
#[test] #[test]
#[cfg(not(target_pointer_width = "16"))]
fn test_sizes() { fn test_sizes() {
let v = ArrayVec::from([0u8; 1 << 16]); let v = ArrayVec::from([0u8; 255]);
assert_eq!(vec![0u8; v.len()], &v[..]); assert_eq!(vec![0u8; v.len()], &v[..]);
} }
@@ -715,6 +717,32 @@ fn test_extend_zst() {
assert_eq!(array.len(), 5); assert_eq!(array.len(), 5);
} }
#[test]
fn test_extend_zst_with_drop_is_not_dropped_twice_via_safe_api() {
use std::sync::atomic::{AtomicUsize, Ordering};
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
struct ZstWithSafeDrop;
impl Drop for ZstWithSafeDrop {
fn drop(&mut self) {
let previous = DROP_COUNT.fetch_add(1, Ordering::SeqCst);
// Extending with a single ZST moves one logical value into the ArrayVec.
// Its Drop implementation must run exactly once, when the ArrayVec drops.
if previous != 0 {
panic!("ZST value dropped more than once");
}
}
}
DROP_COUNT.store(0, Ordering::SeqCst);
let mut vec = ArrayVec::<ZstWithSafeDrop, 1>::new();
vec.extend(std::iter::once(ZstWithSafeDrop));
drop(vec);
}
#[test] #[test]
fn test_try_from_argument() { fn test_try_from_argument() {
use core::convert::TryFrom; use core::convert::TryFrom;
@@ -729,21 +757,17 @@ fn allow_max_capacity_arrayvec_type() {
} }
#[should_panic(expected="largest supported capacity")] #[should_panic(expected="largest supported capacity")]
#[cfg(not(target_pointer_width = "16"))]
#[test] #[test]
fn deny_max_capacity_arrayvec_value() { fn deny_max_capacity_arrayvec_value() {
if mem::size_of::<usize>() <= mem::size_of::<u32>() {
panic!("This test does not work on this platform. 'largest supported capacity'");
}
// this type is allowed to be used (but can't be constructed) // this type is allowed to be used (but can't be constructed)
let _v: ArrayVec<(), {usize::MAX}> = ArrayVec::new(); let _v: ArrayVec<(), {usize::MAX}> = ArrayVec::new();
} }
#[should_panic(expected="index out of bounds")] #[should_panic(expected="index out of bounds")]
#[cfg(not(target_pointer_width = "16"))]
#[test] #[test]
fn deny_max_capacity_arrayvec_value_const() { fn deny_max_capacity_arrayvec_value_const() {
if mem::size_of::<usize>() <= mem::size_of::<u32>() {
panic!("This test does not work on this platform. 'index out of bounds'");
}
// this type is allowed to be used (but can't be constructed) // this type is allowed to be used (but can't be constructed)
let _v: ArrayVec<(), {usize::MAX}> = ArrayVec::new_const(); let _v: ArrayVec<(), {usize::MAX}> = ArrayVec::new_const();
} }