Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Const constructors from str, from slice for ArrayString, ArrayVec #205

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion Cargo.toml
Expand Up @@ -35,9 +35,15 @@ name = "arraystring"
harness = false

[features]
default = ["std"]
default = ["std", "u128", "floats"]
# enable stdlib
std = []

# enable i128, u128 specific impls
u128 = []
# enable f32, f64 specific impls
floats = []

[profile.bench]
debug = true
[profile.release]
Expand Down
37 changes: 36 additions & 1 deletion src/array_string.rs
Expand Up @@ -19,7 +19,6 @@ use crate::utils::MakeMaybeUninit;
#[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
Expand Down Expand Up @@ -108,6 +107,42 @@ impl<const CAP: usize> ArrayString<CAP>
Ok(arraystr)
}

/// Create a new `ArrayString` from a `str`, suitable for const context
///
/// Capacity is inferred from the type parameter.
///
/// **Panics** or causes a **const error** if the backing array is not large enough to fit the
/// string.
///
/// ```
/// use arrayvec::ArrayString;
///
/// const S: ArrayString<3> = ArrayString::from_str_const("");
/// ```
///
/// A compile-time error will occur - in constants - if the input is too long:
///
/// ```compile_fail
/// # use arrayvec::ArrayString;
/// const S1: ArrayString<3> = ArrayString::from_str_const("too long for the capacity");
/// ```
pub const fn from_str_const(s: &str) -> Self {
let bytes = s.as_bytes();
let len = bytes.len();
assert_length_lt_capacity_const!(len, CAP);

let mut vec = Self::new_const();
let mut i = 0;
while i < len {
vec.xs[i] = MaybeUninit::new(bytes[i]);
i += 1;
}

// Safety: we know len <= CAP and elements < len are initialized
vec.len = len as u32;
vec
}

/// Create a new `ArrayString` from a byte string literal.
///
/// **Errors** if the byte string literal is not valid UTF-8.
Expand Down
51 changes: 51 additions & 0 deletions src/arrayvec.rs
Expand Up @@ -760,6 +760,57 @@ impl<T, const CAP: usize> From<[T; CAP]> for ArrayVec<T, CAP> {
}
}

// Generic version of from_slice_const not possible at this time
macro_rules! impl_from_const {
($($t:ty)+) => {
$(
impl<const CAP: usize> ArrayVec<$t, CAP> {
/// Create a new `ArrayVec` from a slice, suitable for const context
///
/// Capacity is inferred from the type parameter.
///
/// **Panics** or causes a **const error** if the backing array is not large enough to fit the
/// slice.
///
/// ```
/// use arrayvec::ArrayVec;
///
/// const V: ArrayVec<u8, 3> = ArrayVec::<u8, 3>::from_slice_const(&[1, 2, 3]);
/// ```
///
/// A compile-time error will occur - in constants - if the input is too long:
///
/// ```compile_fail
/// # use arrayvec::ArrayVec;
/// const S1: ArrayVec<u8, 3> = ArrayVec::<u8, 3>::from_slice_const(&[1, 2, 3, 4]);
/// ```
pub const fn from_slice_const(values: &[$t]) -> Self {
let len = values.len();
assert_length_lt_capacity_const!(len, CAP);

let mut vec = Self::new_const();
let mut i = 0;
while i < len {
vec.xs[i] = MaybeUninit::new(values[i]);
i += 1;
}

// Safety: we know len <= CAP and elements < len are initialized
vec.len = len as u32;
vec
}
}
)+

};
}

impl_from_const!(u8 u16 u32 u64 usize i8 i16 i32 i64 isize char);
#[cfg(feature = "floats")]
impl_from_const!(f32 f64);
#[cfg(feature = "u128")]
impl_from_const!(u128 i128);


/// Try to create an `ArrayVec` from a slice. This will return an error if the slice was too big to
/// fit.
Expand Down
8 changes: 8 additions & 0 deletions src/lib.rs
Expand Up @@ -46,6 +46,14 @@ macro_rules! assert_capacity_limit_const {
}
}

macro_rules! assert_length_lt_capacity_const {
($len:expr, $cap:expr) => {
if $len > 0 {
let _len_check = [(); $cap /*ArrayVec/ArrayString: insufficient capacity */][$len - 1];
}
}
}

mod arrayvec_impl;
mod arrayvec;
mod array_string;
Expand Down
28 changes: 27 additions & 1 deletion tests/tests.rs
Expand Up @@ -773,6 +773,19 @@ fn test_arrayvec_const_constructible() {
assert_eq!(var[..], [vec![3, 5, 8]]);
}

#[test]
fn test_arrayvec_from_slice_const() {
const V: ArrayVec<u8, 10> = ArrayVec::<u8, 10>::from_slice_const(b"0123456789");

let mut var = V;
assert_eq!(&*var, b"0123456789");
assert!(var.try_push(0).is_err());
var.clear();
var.push(1);
var.push(2);
assert_eq!(&*var, &[1, 2]);
}

#[test]
fn test_arraystring_const_constructible() {
const AS: ArrayString<10> = ArrayString::new_const();
Expand All @@ -784,10 +797,23 @@ fn test_arraystring_const_constructible() {
assert_eq!(var, *"hello");
}

#[test]
fn test_arraystring_from_str_const() {
const AS: ArrayString<10> = ArrayString::from_str_const("0123456789");

let mut var = AS;
assert_eq!(&*var, "0123456789");
assert!(var.try_push_str("1").is_err());

var.clear();
var.push_str("9876543210");
assert_eq!(&*var, "9876543210");
}


#[test]
fn test_arraystring_zero_filled_has_some_sanity_checks() {
let string = ArrayString::<4>::zero_filled();
assert_eq!(string.as_str(), "\0\0\0\0");
assert_eq!(string.len(), 4);
}
}