From 024d213e4073b9f3b180fe06b9d695ec0ed23ab7 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sun, 7 Nov 2021 11:49:38 +0100 Subject: [PATCH 1/2] Add from_str_const constructor for ArrayString --- src/array_string.rs | 37 ++++++++++++++++++++++++++++++++++++- src/lib.rs | 8 ++++++++ tests/tests.rs | 15 ++++++++++++++- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/array_string.rs b/src/array_string.rs index c4712a0c..010301b3 100644 --- a/src/array_string.rs +++ b/src/array_string.rs @@ -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 @@ -108,6 +107,42 @@ impl ArrayString 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. diff --git a/src/lib.rs b/src/lib.rs index 5dc0273a..a624692b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/tests/tests.rs b/tests/tests.rs index 2f8a5ef5..bb7baec1 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -784,10 +784,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); -} \ No newline at end of file +} From 5e2eea0a1b581117ba9261a6897ec27966391cb6 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sun, 7 Nov 2021 11:49:48 +0100 Subject: [PATCH 2/2] Add from_slice_const constructor for ArrayVec We can't make these generic, not even where T: Copy, so they are just templated out with macros for the integer types, quite limited. --- Cargo.toml | 8 +++++++- src/arrayvec.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/tests.rs | 13 +++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ea041136..1705f7a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/src/arrayvec.rs b/src/arrayvec.rs index e69e60c1..d116d633 100644 --- a/src/arrayvec.rs +++ b/src/arrayvec.rs @@ -760,6 +760,57 @@ impl From<[T; CAP]> for ArrayVec { } } +// Generic version of from_slice_const not possible at this time +macro_rules! impl_from_const { + ($($t:ty)+) => { +$( +impl 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 = ArrayVec::::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 = ArrayVec::::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. diff --git a/tests/tests.rs b/tests/tests.rs index bb7baec1..b1c5be3c 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -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 = ArrayVec::::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();