Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ harness = false
[features]
default = ["std"]
std = []
# For the const_helper feature MSRV 1.63.0 is required
const_helper = []

[profile.bench]
debug = true
Expand Down
12 changes: 12 additions & 0 deletions src/array_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ impl<const CAP: usize> ArrayString<CAP>
ArrayString { xs: MakeMaybeUninit::ARRAY, len: 0 }
}


/// Create a new `ArrayString` from raw parts (const fn).
///
/// # Safety
/// The caller must ensure that the provided `len` is valid,
/// i.e. that `len <= CAP` and that the first `len` bytes of `xs` form a valid UTF-8 string.
pub const unsafe fn from_raw_parts(xs: [MaybeUninit<u8>; CAP], len: usize) -> Self {
assert_capacity_limit_const!(CAP);
assert_capacity_len_const!(CAP, len);
ArrayString { xs, len: len as _ }
}

/// Return the length of the string.
#[inline]
pub const fn len(&self) -> usize { self.len as usize }
Expand Down
17 changes: 14 additions & 3 deletions src/arrayvec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
ArrayVec { xs: MakeMaybeUninit::ARRAY, len: 0 }
}

/// Create an `ArrayVec` from raw parts.
///
/// # Safety
/// The caller must ensure that the first `len` elements of `xs` are
/// properly initialized and that len is less than or equal to `CAP`.
pub const unsafe fn from_raw_parts(xs: [MaybeUninit<T>; CAP], len: usize) -> Self {
assert_capacity_limit_const!(CAP);
assert_capacity_len_const!(CAP, len);
ArrayVec { xs, len: len as LenUint }
}

/// Return the number of elements in the `ArrayVec`.
///
/// ```
Expand Down Expand Up @@ -637,7 +648,7 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
/// assert_eq!(&v1[..], &[3]);
/// 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>
{
// Memory safety
Expand All @@ -664,7 +675,7 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
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();

Expand Down Expand Up @@ -1126,7 +1137,7 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
let mut guard = ScopeExitGuard {
value: &mut self.len,
data: len,
f: move |&len, self_len| {
f: move |&len, self_len: &mut &mut LenUint| {
**self_len = len as LenUint;
}
};
Expand Down
149 changes: 149 additions & 0 deletions src/const_helper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
use core::mem::MaybeUninit;

use crate::ArrayString;

/// Creates a const ArrayString from a str slice.
pub const fn str<const CAP: usize>(s: &str) -> ArrayString<CAP> {
assert_capacity_limit_const!(CAP);

let bytes = s.as_bytes();
let len = bytes.len();

// Check that capacity is not exceeded
if len > CAP {
panic!("ArrayString: capacity exceeded in const_str");
}

let mut xs = [MaybeUninit::<u8>::uninit(); CAP];
let mut i = 0;
while i < len {
xs[i] = MaybeUninit::new(bytes[i]);
i += 1;
}

// Safety: We have initialized `len` bytes in `xs`
// and ensured that `len <= CAP` before
// and s is a valid UTF-8 string.
unsafe { ArrayString::from_raw_parts(xs, len) }
}

/// Create a const ArrayString from a byte slice.
pub const fn byte_str<const CAP: usize>(bytes: &[u8]) -> ArrayString<CAP> {
// for the const_helper feature MSRV 1.63.0 is required
#[allow(clippy::incompatible_msrv)]
let Ok(str) = core::str::from_utf8(bytes) else {
panic!("ArrayString: invalid UTF-8 in const_byte_str");
};
crate::const_helper::str(str)
}

/// Creates a const ArrayString from a str slice.
///
/// # Examples
/// ```rust
/// use arrayvec::array_str;
/// // With inferred capacity
/// const S: arrayvec::ArrayString<5> = array_str!("hello");
/// assert_eq!(&S, "hello");
/// // With specified capacity
/// const S2: arrayvec::ArrayString<10> = array_str!("hello", 10);
/// assert_eq!(&S2, "hello");
/// assert_eq!(S2.capacity(), 10);
/// ```
#[macro_export]
macro_rules! array_str {
($str:expr) => {
$crate::const_helper::str::<{ $str.len() }>($str)
};
($str:expr, $cap:expr) => {
$crate::const_helper::str::<$cap>($str)
};
}

/// Creates a const ArrayString from a byte slice.
///
/// # Examples
/// ```rust
/// use arrayvec::array_bstr;
/// // With inferred capacity
/// const B: arrayvec::ArrayString<5> = array_bstr!(b"hello");
/// assert_eq!(&B, "hello");
/// // With specified capacity
/// const B2: arrayvec::ArrayString<10> = array_bstr!(b"hello", 10);
/// assert_eq!(&B2, "hello");
/// assert_eq!(B2.capacity(), 10);
/// ```
#[macro_export]
macro_rules! array_bstr {
($bstr:expr) => {
$crate::const_helper::byte_str::<{ $bstr.len() }>($bstr)
};
($bstr:expr, $cap:expr) => {
$crate::const_helper::byte_str::<$cap>($bstr)
};
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn array_str() {
const S_EMPTY: ArrayString<0> = array_str!("");
assert_eq!(&S_EMPTY, "");

const S_EMPTY_CAP5: ArrayString<5> = array_str!("", 5);
assert_eq!(&S_EMPTY_CAP5, "");

const S1: ArrayString<5> = array_str!("hello");
assert_eq!(&S1, "hello");

const S2: ArrayString<10> = array_str!("hello", 10);
assert_eq!(&S2, "hello");
}

#[test]
fn array_bstr() {
const B_EMPTY: ArrayString<0> = array_bstr!(b"");
assert_eq!(&B_EMPTY, "");

const B_EMPTY_CAP5: ArrayString<5> = array_bstr!(b"", 5);
assert_eq!(&B_EMPTY_CAP5, "");

const B1: ArrayString<5> = array_bstr!(b"hello");
assert_eq!(&B1, "hello");

const B2: ArrayString<10> = array_bstr!(b"hello", 10);
assert_eq!(&B2, "hello");
}

#[test]
#[should_panic]
fn fail_empty() {
let _bad_empty = array_str!("hello", 0);
}

#[test]
#[should_panic]
fn fail_bempty() {
let _bad_bempty = array_bstr!(b"hello", 0);
}

#[test]
#[should_panic]
fn fail_cap() {
let _bad_small = array_str!("hello", 4);
}

#[test]
#[should_panic]
fn fail_bcap() {
let _bad_bsmall = array_bstr!(b"hello", 4);
}

#[test]
#[should_panic]
fn fail_utf8() {
let _bad_utf8 = array_bstr!(b"\xFF\xFF\xFF", 4);
}
}
18 changes: 14 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ pub(crate) type LenUint = u16;

macro_rules! assert_capacity_limit {
($cap:expr) => {
if std::mem::size_of::<usize>() > std::mem::size_of::<LenUint>() {
if $cap > LenUint::MAX as usize {
if std::mem::size_of::<usize>() > std::mem::size_of::<$crate::LenUint>() {
if $cap > $crate::LenUint::MAX as usize {
#[cfg(not(target_pointer_width = "16"))]
panic!("ArrayVec: largest supported capacity is u32::MAX");
#[cfg(target_pointer_width = "16")]
Expand All @@ -49,20 +49,30 @@ macro_rules! assert_capacity_limit {

macro_rules! assert_capacity_limit_const {
($cap:expr) => {
if std::mem::size_of::<usize>() > std::mem::size_of::<LenUint>() {
if $cap > LenUint::MAX as usize {
if std::mem::size_of::<usize>() > std::mem::size_of::<$crate::LenUint>() {
if $cap > $crate::LenUint::MAX as usize {
[/*ArrayVec: largest supported capacity is u32::MAX*/][$cap]
}
}
}
}

macro_rules! assert_capacity_len_const {
($cap:expr, $len:expr) => {
if $len > $cap {
[/*ArrayVec/ArrayString: len over capacity*/][$len]
}
};
}

mod arrayvec_impl;
mod arrayvec;
mod array_string;
mod char;
mod errors;
mod utils;
#[cfg(feature = "const_helper")]
pub mod const_helper;

pub use crate::array_string::ArrayString;
pub use crate::errors::CapacityError;
Expand Down
26 changes: 26 additions & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ extern crate arrayvec;
use arrayvec::ArrayVec;
use arrayvec::ArrayString;
use std::mem;
use std::mem::MaybeUninit;
use arrayvec::CapacityError;

use std::collections::HashMap;
Expand Down Expand Up @@ -774,3 +775,28 @@ fn test_arraystring_zero_filled_has_some_sanity_checks() {
assert_eq!(string.as_str(), "\0\0\0\0");
assert_eq!(string.len(), 4);
}

#[test]
fn test_array_vec_from_parts() {
let mut xs = [MaybeUninit::<u8>::uninit(); 4];
xs[0] = MaybeUninit::new(1);
xs[1] = MaybeUninit::new(2);

// # Safety - we have initialized 2 elements in xs
let array = unsafe { ArrayVec::from_raw_parts(xs, 2) };
assert_eq!(&array[..], &[1, 2]);
assert_eq!(array.len(), 2);
}

#[test]
fn test_array_str_from_parts() {
let mut xs = [MaybeUninit::<u8>::uninit(); 4];
xs[0] = MaybeUninit::new(b'a');
xs[1] = MaybeUninit::new(b'b');
xs[2] = MaybeUninit::new(b'c');

// # Safety - we have initialized 3 elements in xs
let array = unsafe { ArrayString::from_raw_parts(xs, 3) };
assert_eq!(&array[..], "abc");
assert_eq!(array.len(), 3);
}