Skip to content

Commit 6409afb

Browse files
committed
Introduce from parts constructor function, add optional const_helper module hidden behind a feature flag to allow constructions of strings in const context
1 parent 9f4e8f5 commit 6409afb

6 files changed

Lines changed: 208 additions & 4 deletions

File tree

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ harness = false
4848
[features]
4949
default = ["std"]
5050
std = []
51+
# For the const_helper feature MSRV 1.63.0 is required
52+
const_helper = []
5153

5254
[profile.bench]
5355
debug = true

src/array_string.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,18 @@ impl<const CAP: usize> ArrayString<CAP>
8383
ArrayString { xs: MakeMaybeUninit::ARRAY, len: 0 }
8484
}
8585

86+
87+
/// Create a new `ArrayString` from raw parts (const fn).
88+
///
89+
/// # Safety
90+
/// The caller must ensure that the provided `len` is valid,
91+
/// i.e. that `len <= CAP` and that the first `len` bytes of `xs` form a valid UTF-8 string.
92+
pub const unsafe fn from_raw_parts(xs: [MaybeUninit<u8>; CAP], len: usize) -> Self {
93+
assert_capacity_limit_const!(CAP);
94+
debug_assert!(len <= CAP);
95+
ArrayString { xs, len: len as _ }
96+
}
97+
8698
/// Return the length of the string.
8799
#[inline]
88100
pub const fn len(&self) -> usize { self.len as usize }

src/arrayvec.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,17 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
101101
ArrayVec { xs: MakeMaybeUninit::ARRAY, len: 0 }
102102
}
103103

104+
/// Create an `ArrayVec` from raw parts.
105+
///
106+
/// # Safety
107+
/// The caller must ensure that the first `len` elements of `xs` are
108+
/// properly initialized.
109+
pub const unsafe fn from_raw_parts(xs: [MaybeUninit<T>; CAP], len: usize) -> Self {
110+
assert_capacity_limit_const!(CAP);
111+
debug_assert!(len <= CAP);
112+
ArrayVec { xs, len: len as LenUint }
113+
}
114+
104115
/// Return the number of elements in the `ArrayVec`.
105116
///
106117
/// ```

src/const_helper.rs

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
use core::mem::MaybeUninit;
2+
3+
use crate::ArrayString;
4+
5+
6+
7+
/// Creates a const ArrayString from a str slice.
8+
pub const fn str<const CAP: usize>(s: &str) -> ArrayString<CAP> {
9+
assert_capacity_limit_const!(CAP);
10+
11+
let bytes = s.as_bytes();
12+
let len = bytes.len();
13+
14+
// Check that capacity is not exceeded
15+
if len > CAP {
16+
panic!("ArrayString: capacity exceeded in const_str");
17+
}
18+
19+
let mut xs = [MaybeUninit::<u8>::uninit(); CAP];
20+
let mut i = 0;
21+
while i < len {
22+
xs[i] = MaybeUninit::new(bytes[i]);
23+
i += 1;
24+
}
25+
26+
// Safety: We have initialized `len` bytes in `xs`
27+
// and ensured that `len <= CAP` before
28+
// and s is a valid UTF-8 string.
29+
unsafe { ArrayString::from_raw_parts(xs, len) }
30+
}
31+
32+
/// Create a const ArrayString from a byte slice.
33+
pub const fn byte_str<const CAP: usize>(bytes: &[u8]) -> ArrayString<CAP> {
34+
// for the const_helper feature MSRV 1.63.0 is required
35+
#[allow(clippy::incompatible_msrv)]
36+
let Ok(str) = core::str::from_utf8(bytes) else {
37+
panic!("ArrayString: invalid UTF-8 in const_byte_str");
38+
};
39+
crate::const_helper::str(str)
40+
}
41+
42+
/// Creates a const ArrayString from a str slice.
43+
///
44+
/// # Examples
45+
/// ```rust
46+
/// use arrayvec::array_str;
47+
/// // With inferred capacity
48+
/// const S: arrayvec::ArrayString<5> = array_str!("hello");
49+
/// assert_eq!(&S, "hello");
50+
/// // With specified capacity
51+
/// const S2: arrayvec::ArrayString<10> = array_str!("hello", 10);
52+
/// assert_eq!(&S2, "hello");
53+
/// assert_eq!(S2.capacity(), 10);
54+
/// ```
55+
#[macro_export]
56+
macro_rules! array_str {
57+
($str:expr) => {
58+
$crate::const_helper::str::<{ $str.len() }>($str)
59+
};
60+
($str:expr, $cap:expr) => {
61+
$crate::const_helper::str::<$cap>($str)
62+
};
63+
}
64+
65+
/// Creates a const ArrayString from a byte slice.
66+
///
67+
/// # Examples
68+
/// ```rust
69+
/// use arrayvec::array_bstr;
70+
/// // With inferred capacity
71+
/// const B: arrayvec::ArrayString<5> = array_bstr!(b"hello");
72+
/// assert_eq!(&B, "hello");
73+
/// // With specified capacity
74+
/// const B2: arrayvec::ArrayString<10> = array_bstr!(b"hello", 10);
75+
/// assert_eq!(&B2, "hello");
76+
/// assert_eq!(B2.capacity(), 10);
77+
/// ```
78+
#[macro_export]
79+
macro_rules! array_bstr {
80+
($bstr:expr) => {
81+
$crate::const_helper::byte_str::<{ $bstr.len() }>($bstr)
82+
};
83+
($bstr:expr, $cap:expr) => {
84+
$crate::const_helper::byte_str::<$cap>($bstr)
85+
};
86+
}
87+
88+
#[cfg(test)]
89+
mod tests {
90+
use super::*;
91+
92+
#[test]
93+
fn array_str() {
94+
const S_EMPTY: ArrayString<0> = array_str!("");
95+
assert_eq!(&S_EMPTY, "");
96+
97+
const S_EMPTY_CAP5: ArrayString<5> = array_str!("", 5);
98+
assert_eq!(&S_EMPTY_CAP5, "");
99+
100+
const S1: ArrayString<5> = array_str!("hello");
101+
assert_eq!(&S1, "hello");
102+
103+
const S2: ArrayString<10> = array_str!("hello", 10);
104+
assert_eq!(&S2, "hello");
105+
}
106+
107+
#[test]
108+
fn array_bstr() {
109+
const B_EMPTY: ArrayString<0> = array_bstr!(b"");
110+
assert_eq!(&B_EMPTY, "");
111+
112+
const B_EMPTY_CAP5: ArrayString<5> = array_bstr!(b"", 5);
113+
assert_eq!(&B_EMPTY_CAP5, "");
114+
115+
const B1: ArrayString<5> = array_bstr!(b"hello");
116+
assert_eq!(&B1, "hello");
117+
118+
const B2: ArrayString<10> = array_bstr!(b"hello", 10);
119+
assert_eq!(&B2, "hello");
120+
}
121+
122+
#[test]
123+
#[should_panic]
124+
fn fail_empty() {
125+
let _bad_empty = array_str!("hello", 0);
126+
}
127+
128+
#[test]
129+
#[should_panic]
130+
fn fail_bempty() {
131+
let _bad_bempty = array_bstr!(b"hello", 0);
132+
}
133+
134+
#[test]
135+
#[should_panic]
136+
fn fail_cap() {
137+
let _bad_small = array_str!("hello", 4);
138+
}
139+
140+
#[test]
141+
#[should_panic]
142+
fn fail_bcap() {
143+
let _bad_bsmall = array_bstr!(b"hello", 4);
144+
}
145+
146+
#[test]
147+
#[should_panic]
148+
fn fail_utf8() {
149+
let _bad_utf8 = array_bstr!(b"\xFF\xFF\xFF", 4);
150+
}
151+
}

src/lib.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ pub(crate) type LenUint = u16;
3636

3737
macro_rules! assert_capacity_limit {
3838
($cap:expr) => {
39-
if std::mem::size_of::<usize>() > std::mem::size_of::<LenUint>() {
40-
if $cap > LenUint::MAX as usize {
39+
if std::mem::size_of::<usize>() > std::mem::size_of::<$crate::LenUint>() {
40+
if $cap > $crate::LenUint::MAX as usize {
4141
#[cfg(not(target_pointer_width = "16"))]
4242
panic!("ArrayVec: largest supported capacity is u32::MAX");
4343
#[cfg(target_pointer_width = "16")]
@@ -49,8 +49,8 @@ macro_rules! assert_capacity_limit {
4949

5050
macro_rules! assert_capacity_limit_const {
5151
($cap:expr) => {
52-
if std::mem::size_of::<usize>() > std::mem::size_of::<LenUint>() {
53-
if $cap > LenUint::MAX as usize {
52+
if std::mem::size_of::<usize>() > std::mem::size_of::<$crate::LenUint>() {
53+
if $cap > $crate::LenUint::MAX as usize {
5454
[/*ArrayVec: largest supported capacity is u32::MAX*/][$cap]
5555
}
5656
}
@@ -63,6 +63,8 @@ mod array_string;
6363
mod char;
6464
mod errors;
6565
mod utils;
66+
#[cfg(feature = "const_helper")]
67+
pub mod const_helper;
6668

6769
pub use crate::array_string::ArrayString;
6870
pub use crate::errors::CapacityError;

tests/tests.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ extern crate arrayvec;
44
use arrayvec::ArrayVec;
55
use arrayvec::ArrayString;
66
use std::mem;
7+
use std::mem::MaybeUninit;
78
use arrayvec::CapacityError;
89

910
use std::collections::HashMap;
@@ -774,3 +775,28 @@ fn test_arraystring_zero_filled_has_some_sanity_checks() {
774775
assert_eq!(string.as_str(), "\0\0\0\0");
775776
assert_eq!(string.len(), 4);
776777
}
778+
779+
#[test]
780+
fn test_array_vec_from_parts() {
781+
let mut xs = [MaybeUninit::<u8>::uninit(); 4];
782+
xs[0].write(1);
783+
xs[1].write(2);
784+
785+
// # Safety - we have initialized 2 elements in xs
786+
let array = unsafe { ArrayVec::from_raw_parts(xs, 2) };
787+
assert_eq!(&array[..], &[1, 2]);
788+
assert_eq!(array.len(), 2);
789+
}
790+
791+
#[test]
792+
fn test_array_str_from_parts() {
793+
let mut xs = [MaybeUninit::<u8>::uninit(); 4];
794+
xs[0].write(b'a');
795+
xs[1].write(b'b');
796+
xs[2].write(b'c');
797+
798+
// # Safety - we have initialized 3 elements in xs
799+
let array = unsafe { ArrayString::from_raw_parts(xs, 3) };
800+
assert_eq!(&array[..], "abc");
801+
assert_eq!(array.len(), 3);
802+
}

0 commit comments

Comments
 (0)