Skip to content

feat+refactor: Bump MSRV to 1.57 and maximize const #296

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

Open
wants to merge 7 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,21 @@ jobs:
strategy:
matrix:
include:
- rust: 1.51.0 # MSRV
- rust: 1.57.0 # MSRV
features: serde
experimental: false
# doctest of `ArrayVec::spare_capacity_mut` has MSRV 1.55
test-args: --skip spare_capacity_mut
- rust: 1.70.0
features: serde
experimental: false
- rust: stable
features:
features: const
bench: true
experimental: false
- rust: beta
features: serde
features: serde, const
experimental: false
- rust: nightly
features: serde, borsh, zeroize
features: serde, borsh, zeroize, const
experimental: false

steps:
Expand All @@ -42,7 +40,7 @@ jobs:
with:
toolchain: ${{ matrix.rust }}
- name: Pin versions for MSRV
if: "${{ matrix.rust == '1.51.0' }}"
if: "${{ matrix.rust == '1.57.0' }}"
run: |
cargo update -p serde_test --precise 1.0.163
cargo update -p serde --precise 1.0.69
Expand Down
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.7.6"
authors = ["bluss"]
license = "MIT OR Apache-2.0"
edition = "2018"
rust-version = "1.51"
rust-version = "1.57"

description = "A vector with fixed capacity, backed by an array (it can be stored on the stack too). Implements fixed capacity ArrayVec and ArrayString."
documentation = "https://docs.rs/arrayvec/"
Expand Down Expand Up @@ -48,14 +48,15 @@ harness = false
[features]
default = ["std"]
std = []
const = []

[profile.bench]
debug = true
[profile.release]
debug = true

[package.metadata.docs.rs]
features = ["borsh", "serde", "zeroize"]
features = ["borsh", "serde", "zeroize", "const"]

[package.metadata.release]
no-dev-version = true
Expand Down
96 changes: 57 additions & 39 deletions src/array_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::CapacityError;
use crate::LenUint;
use crate::char::encode_utf8;
use crate::utils::MakeMaybeUninit;
use crate::const_fn;

#[cfg(feature="serde")]
use serde::{Serialize, Deserialize, Serializer, Deserializer};
Expand Down Expand Up @@ -50,6 +51,8 @@ impl<const CAP: usize> Default for ArrayString<CAP>

impl<const CAP: usize> ArrayString<CAP>
{

const_fn!{
/// Create a new empty `ArrayString`.
///
/// Capacity is inferred from the type parameter.
Expand All @@ -62,12 +65,12 @@ impl<const CAP: usize> ArrayString<CAP>
/// assert_eq!(&string[..], "foo");
/// assert_eq!(string.capacity(), 16);
/// ```
pub fn new() -> ArrayString<CAP> {
pub const fn new() -> ArrayString<CAP> {
assert_capacity_limit!(CAP);
unsafe {
ArrayString { xs: MaybeUninit::uninit().assume_init(), len: 0 }
}
}
}}

/// Create a new empty `ArrayString` (const fn).
///
Expand All @@ -91,6 +94,7 @@ impl<const CAP: usize> ArrayString<CAP>
#[inline]
pub const fn is_empty(&self) -> bool { self.len() == 0 }

const_fn!{
/// Create a new `ArrayString` from a `str`.
///
/// Capacity is inferred from the type parameter.
Expand All @@ -105,12 +109,15 @@ impl<const CAP: usize> ArrayString<CAP>
/// assert_eq!(string.len(), 3);
/// assert_eq!(string.capacity(), 3);
/// ```
pub fn from(s: &str) -> Result<Self, CapacityError<&str>> {
pub const fn from(s: &str) -> Result<Self, CapacityError<&str>> {
let mut arraystr = Self::new();
arraystr.try_push_str(s)?;
Ok(arraystr)
}
match arraystr.try_push_str(s) {
Ok(()) => Ok(arraystr),
Err(e) => Err(e),
}
}}

const_fn!{
/// Create a new `ArrayString` from a byte string literal.
///
/// **Errors** if the byte string literal is not valid UTF-8.
Expand All @@ -120,18 +127,22 @@ impl<const CAP: usize> ArrayString<CAP>
///
/// let string = ArrayString::from_byte_string(b"hello world").unwrap();
/// ```
pub fn from_byte_string(b: &[u8; CAP]) -> Result<Self, Utf8Error> {
let len = str::from_utf8(b)?.len();
debug_assert_eq!(len, CAP);
pub const fn from_byte_string(b: &[u8; CAP]) -> Result<Self, Utf8Error> {
let len = match str::from_utf8(b) {
Ok(str) => str.len(),
Err(e) => return Err(e),
};
debug_assert!(len == CAP);
let mut vec = Self::new();
unsafe {
(b as *const [u8; CAP] as *const [MaybeUninit<u8>; CAP])
.copy_to_nonoverlapping(&mut vec.xs as *mut [MaybeUninit<u8>; CAP], 1);
vec.set_len(CAP);
}
Ok(vec)
}
}}

const_fn!{
/// Create a new `ArrayString` value fully filled with ASCII NULL characters (`\0`). Useful
/// to be used as a buffer to collect external data or as a buffer for intermediate processing.
///
Expand All @@ -142,7 +153,7 @@ impl<const CAP: usize> ArrayString<CAP>
/// assert_eq!(string.len(), 16);
/// ```
#[inline]
pub fn zero_filled() -> Self {
pub const fn zero_filled() -> Self {
assert_capacity_limit!(CAP);
// SAFETY: `assert_capacity_limit` asserts that `len` won't overflow and
// `zeroed` fully fills the array with nulls.
Expand All @@ -152,7 +163,7 @@ impl<const CAP: usize> ArrayString<CAP>
len: CAP as _
}
}
}
}}

/// Return the capacity of the `ArrayString`.
///
Expand Down Expand Up @@ -209,6 +220,7 @@ impl<const CAP: usize> ArrayString<CAP>
self.try_push(c).unwrap();
}

const_fn!{
/// Adds the given char to the end of the string.
///
/// Returns `Ok` if the push succeeds.
Expand All @@ -227,7 +239,7 @@ impl<const CAP: usize> ArrayString<CAP>
/// assert_eq!(&string[..], "ab");
/// assert_eq!(overflow.unwrap_err().element(), 'c');
/// ```
pub fn try_push(&mut self, c: char) -> Result<(), CapacityError<char>> {
pub const fn try_push(&mut self, c: char) -> Result<(), CapacityError<char>> {
let len = self.len();
unsafe {
let ptr = self.as_mut_ptr().add(len);
Expand All @@ -240,7 +252,7 @@ impl<const CAP: usize> ArrayString<CAP>
Err(_) => Err(CapacityError::new(c)),
}
}
}
}}

/// Adds the given string slice to the end of the string.
///
Expand All @@ -261,6 +273,7 @@ impl<const CAP: usize> ArrayString<CAP>
self.try_push_str(s).unwrap()
}

const_fn!{
/// Adds the given string slice to the end of the string.
///
/// Returns `Ok` if the push succeeds.
Expand All @@ -281,7 +294,7 @@ impl<const CAP: usize> ArrayString<CAP>
/// assert_eq!(overflow1.unwrap_err().element(), "bc");
/// assert_eq!(overflow2.unwrap_err().element(), "ef");
/// ```
pub fn try_push_str<'a>(&mut self, s: &'a str) -> Result<(), CapacityError<&'a str>> {
pub const fn try_push_str<'a>(&mut self, s: &'a str) -> Result<(), CapacityError<&'a str>> {
if s.len() > self.capacity() - self.len() {
return Err(CapacityError::new(s));
}
Expand All @@ -293,7 +306,7 @@ impl<const CAP: usize> ArrayString<CAP>
self.set_len(newl);
}
Ok(())
}
}}

/// Removes the last character from the string and returns it.
///
Expand Down Expand Up @@ -340,7 +353,7 @@ impl<const CAP: usize> ArrayString<CAP>
/// ```
pub fn truncate(&mut self, new_len: usize) {
if new_len <= self.len() {
assert!(self.is_char_boundary(new_len));
assert!(self.as_str().is_char_boundary(new_len));
unsafe {
// In libstd truncate is called on the underlying vector,
// which in turns drops each element.
Expand Down Expand Up @@ -387,68 +400,73 @@ impl<const CAP: usize> ArrayString<CAP>
ch
}

const_fn!{
/// Make the string empty.
pub fn clear(&mut self) {
pub const fn clear(&mut self) {
unsafe {
self.set_len(0);
}
}
}}

const_fn!{
/// Set the strings’s length.
///
/// This function is `unsafe` because it changes the notion of the
/// number of “valid” bytes in the string. Use with care.
///
/// This method uses *debug assertions* to check the validity of `length`
/// and may use other debug assertions.
pub unsafe fn set_len(&mut self, length: usize) {
pub const unsafe fn set_len(&mut self, length: usize) {
// type invariant that capacity always fits in LenUint
debug_assert!(length <= self.capacity());
self.len = length as LenUint;
}
}}

const_fn!{
/// Return a string slice of the whole `ArrayString`.
pub fn as_str(&self) -> &str {
self
}
pub const fn as_str(&self) -> &str {
unsafe {
let sl = slice::from_raw_parts(self.as_ptr(), self.len());
str::from_utf8_unchecked(sl)
}
}}

const_fn!{
/// Return a mutable string slice of the whole `ArrayString`.
pub fn as_mut_str(&mut self) -> &mut str {
self
}
pub const fn as_mut_str(&mut self) -> &mut str {
unsafe {
let len = self.len();
let sl = slice::from_raw_parts_mut(self.as_mut_ptr(), len);
str::from_utf8_unchecked_mut(sl)
}
}}

/// Return a raw pointer to the string's buffer.
pub fn as_ptr(&self) -> *const u8 {
pub const fn as_ptr(&self) -> *const u8 {
self.xs.as_ptr() as *const u8
}

const_fn!{
/// Return a raw mutable pointer to the string's buffer.
pub fn as_mut_ptr(&mut self) -> *mut u8 {
pub const fn as_mut_ptr(&mut self) -> *mut u8 {
self.xs.as_mut_ptr() as *mut u8
}
}}
}

impl<const CAP: usize> Deref for ArrayString<CAP>
{
type Target = str;
#[inline]
fn deref(&self) -> &str {
unsafe {
let sl = slice::from_raw_parts(self.as_ptr(), self.len());
str::from_utf8_unchecked(sl)
}
self.as_str()
}
}

impl<const CAP: usize> DerefMut for ArrayString<CAP>
{
#[inline]
fn deref_mut(&mut self) -> &mut str {
unsafe {
let len = self.len();
let sl = slice::from_raw_parts_mut(self.as_mut_ptr(), len);
str::from_utf8_unchecked_mut(sl)
}
self.as_mut_str()
}
}

Expand Down
Loading