use core::char::{decode_utf16, REPLACEMENT_CHARACTER};
use core::ops::{Bound, RangeBounds};
use core::str::{
self, from_utf8, from_utf8_unchecked,
pattern::{Pattern, Searcher},
};
pub use self::string_errors::StringError;
use self::string_utils::{
encode_char_utf8_unchecked, is_char_boundary, is_inside_boundary, never, str_is_char_boundary,
truncate_str,
};
use crate::errors::CapacityError;
use crate::StaticVec;
#[cfg(all(feature = "std", rustdoc))]
use alloc::string::String;
mod string_errors;
mod string_trait_impls;
#[macro_use]
mod string_utils;
/// A fixed-capacity [`String`](alloc::string::String)-like struct built around an instance of
/// `StaticVec<u8, N>`.
///
/// ## Examples
///
/// ```
/// use staticvec::{StaticString, StringError};
///
/// #[derive(Debug)]
/// pub struct User {
/// pub username: StaticString<20>,
/// pub role: StaticString<5>,
/// }
///
/// fn main() -> Result<(), StringError> {
/// let user = User {
/// username: StaticString::try_from_str("user")?,
/// role: StaticString::try_from_str("admin")?,
/// };
/// println!("{:?}", user);
/// Ok(())
/// }
/// ```
pub struct StaticString<const N: usize> {
pub(crate) vec: StaticVec<u8, N>,
}
impl<const N: usize> StaticString<N> {
/// Returns a new StaticString instance.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let string = StaticString::<20>::new();
/// assert!(string.is_empty());
/// ```
#[inline(always)]
pub const fn new() -> Self {
Self {
vec: StaticVec::new(),
}
}
/// An internal "const constructor" helper function that exists to support the `staticstring!`
/// macro. This is only used in one place at the moment, where the input `StaticVec` is known to
/// have been built from a valid-UTF8 `&'static str` literal. Since this has to be accessible from
/// the macro when invoked from external crates, and so can't be `pub(crate)`, we hide it from the
/// docs and give it a two-underscore prefix as the next best thing.
#[doc(hidden)]
#[inline(always)]
pub const unsafe fn __new_from_staticvec(vec: StaticVec<u8, N>) -> Self {
Self { vec }
}
/// Creates a new StaticString instance from `string`, without doing any checking to ensure that
/// the length of `string` does not exceed the resulting StaticString's declared capacity.
///
/// # Safety
///
/// The length of `string` must not exceed the declared capacity of the StaticString being
/// created, as this would result in writing to an out-of-bounds memory region.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let string = unsafe { StaticString::<20>::from_str_unchecked("My String") };
/// assert_eq!(string, "My String");
/// ```
#[inline(always)]
pub const unsafe fn from_str_unchecked(string: &str) -> Self {
let mut res = Self::new();
res.push_str_unchecked(string);
res
}
/// Creates a new StaticString from `string`, truncating `string` as necessary if it has
/// a length greater than the StaticString's declared capacity.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let string = StaticString::<20>::from_str("My String");
/// assert_eq!(string, "My String");
/// let truncate = "0".repeat(21);
/// let truncated = "0".repeat(20);
/// let string = StaticString::<20>::from_str(&truncate);
/// assert_eq!(string, truncated.as_str());
/// ```
#[allow(clippy::should_implement_trait)]
#[inline(always)]
pub fn from_str<S: AsRef<str>>(string: S) -> Self {
let mut res = Self::new();
let string_ref = string.as_ref();
unsafe {
match string_ref.len() <= N {
false => res.push_str_unchecked(truncate_str(string_ref, N)),
true => res.push_str_unchecked(string_ref),
}
}
res
}
/// Creates a new StaticString from `string` if the length of `string` is less than or equal
/// to the StaticString's declared capacity, or returns a
/// [`CapacityError`](crate::errors::CapacityError) otherwise.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let string = StaticString::<20>::from("My String");
/// assert_eq!(string.as_str(), "My String");
/// assert_eq!(StaticString::<20>::try_from_str("").unwrap().as_str(), "");
/// let out_of_bounds = "0".repeat(21);
/// assert!(StaticString::<20>::try_from_str(out_of_bounds).is_err());
/// ```
#[inline(always)]
pub fn try_from_str<S: AsRef<str>>(string: S) -> Result<Self, CapacityError<N>> {
let mut res = Self::new();
res.try_push_str(string)?;
Ok(res)
}
/// Creates a new StaticString from the contents of an iterator, returning immediately if and when
/// the StaticString reaches maximum capacity regardless of whether or not the iterator still has
/// more items to yield.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let string = StaticString::<300>::from_iterator(&["My String", " Other String"][..]);
/// assert_eq!(string.as_str(), "My String Other String");
/// let out_of_bounds = (0..400).map(|_| "000");
/// let truncated = "0".repeat(18);
/// let truncate = StaticString::<20>::from_iterator(out_of_bounds);
/// assert_eq!(truncate.as_str(), truncated.as_str());
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn from_iterator<U: AsRef<str>, I: IntoIterator<Item = U>>(iter: I) -> Self {
let mut res = Self::new();
for s in iter {
let s_ref = s.as_ref();
match res.remaining_capacity() < s_ref.len() {
false => unsafe { res.push_str_unchecked(s_ref) },
true => break,
}
}
res
}
/// Creates a new StaticString from the contents of an iterator if the iterator has a length less
/// than or equal to the StaticString's declared capacity, or returns a
/// [`CapacityError`](crate::errors::CapacityError) otherwise.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let string = StaticString::<300>::try_from_iterator(
/// &["My String", " My Other String"][..]
/// ).unwrap();
/// assert_eq!(string.as_str(), "My String My Other String");
/// let out_of_bounds = (0..100).map(|_| "000");
/// assert!(StaticString::<20>::try_from_iterator(out_of_bounds).is_err());
/// ```
#[inline]
pub fn try_from_iterator<U: AsRef<str>, I: IntoIterator<Item = U>>(
iter: I,
) -> Result<Self, CapacityError<N>> {
let mut res = Self::new();
for s in iter {
res.try_push_str(s)?;
}
Ok(res)
}
/// Creates a new StaticString from the contents of a `char` iterator, returning immediately if
/// and when the StaticString reaches maximum capacity regardless of whether or not the iterator
/// still has more items to yield.
///
/// ```
/// # use staticvec::StaticString;
/// let string = StaticString::<20>::from_chars("My String".chars());
/// assert_eq!(string, "My String");
/// let out_of_bounds = "0".repeat(21);
/// let truncated = "0".repeat(20);
/// let truncate = StaticString::<20>::from_chars(out_of_bounds.chars());
/// assert_eq!(truncate.as_str(), truncated.as_str());
/// ```
#[inline]
pub fn from_chars<I: IntoIterator<Item = char>>(iter: I) -> Self {
let mut res = Self::new();
for c in iter {
match res.remaining_capacity() < c.len_utf8() {
false => unsafe { res.push_unchecked(c) },
true => break,
}
}
res
}
/// Creates a new StaticString from the contents of a `char` iterator if the iterator has a length
/// less than or equal to the StaticString's declared capacity, or returns
/// [`StringError::OutOfBounds`] otherwise.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let string = StaticString::<20>::try_from_chars("My String".chars())?;
/// assert_eq!(string.as_str(), "My String");
/// let out_of_bounds = "0".repeat(21);
/// assert!(StaticString::<20>::try_from_chars(out_of_bounds.chars()).is_err());
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn try_from_chars<I: IntoIterator<Item = char>>(iter: I) -> Result<Self, StringError> {
let mut res = Self::new();
for c in iter {
res.try_push(c)?;
}
Ok(res)
}
/// Creates a new StaticString instance from the provided byte slice, without doing any checking
/// to ensure that the slice contains valid UTF-8 data and has a length less than or equal to
/// the declared capacity of the StaticString.
///
/// # Safety
///
/// The length of the slice must not exceed the declared capacity of the StaticString being
/// created, as this would result in writing to an out-of-bounds memory region.
///
/// The slice must also contain strictly valid UTF-8 data, as if it does not, various assumptions
/// made in the internal implementation of StaticString will be silently invalidated, almost
/// certainly eventually resulting in undefined behavior.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let string = unsafe { StaticString::<20>::from_utf8_unchecked("My String") };
/// assert_eq!(string, "My String");
/// // Undefined behavior, don't do it:
/// // let out_of_bounds = "0".repeat(300);
/// // let ub = unsafe { StaticString::<20>::from_utf8_unchecked(out_of_bounds)) };
/// ```
#[inline(always)]
pub unsafe fn from_utf8_unchecked<B: AsRef<[u8]>>(slice: B) -> Self {
debug_assert!(from_utf8(slice.as_ref()).is_ok());
Self::from_str_unchecked(from_utf8_unchecked(slice.as_ref()))
}
/// Creates a new StaticString instance from the provided byte slice, returning
/// [`StringError::Utf8`] on invalid UTF-8 data, and truncating the input slice as necessary if
/// it has a length greater than the declared capacity of the StaticString being created.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let string = StaticString::<20>::from_utf8("My String")?;
/// assert_eq!(string, "My String");
/// let invalid_utf8 = [0, 159, 146, 150];
/// assert!(StaticString::<20>::from_utf8(invalid_utf8).unwrap_err().is_utf8());
/// let out_of_bounds = "0".repeat(300);
/// assert_eq!(StaticString::<20>::from_utf8(out_of_bounds.as_bytes())?.as_str(), "0".repeat(20).as_str());
/// # Ok(())
/// # }
/// ```
#[inline(always)]
pub fn from_utf8<B: AsRef<[u8]>>(slice: B) -> Result<Self, StringError> {
Ok(Self::from_str(from_utf8(slice.as_ref())?))
}
/// Creates a new StaticString from the provided byte slice, returning [`StringError::Utf8`] on
/// invalid UTF-8 data or [`StringError::OutOfBounds`] if the slice has a length greater than
/// the StaticString's declared capacity.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let string = StaticString::<20>::try_from_utf8("My String")?;
/// assert_eq!(string, "My String");
/// let invalid_utf8 = [0, 159, 146, 150];
/// assert!(StaticString::<20>::try_from_utf8(invalid_utf8).unwrap_err().is_utf8());
/// let out_of_bounds = "0000".repeat(400);
/// assert!(StaticString::<20>::try_from_utf8(out_of_bounds.as_bytes()).unwrap_err().is_out_of_bounds());
/// # Ok(())
/// # }
/// ```
#[inline(always)]
pub fn try_from_utf8<B: AsRef<[u8]>>(slice: B) -> Result<Self, StringError> {
Ok(Self::try_from_str(from_utf8(slice.as_ref())?)?)
}
/// Creates a new StaticString instance from the provided `u16` slice, replacing invalid UTF-16
/// data with `REPLACEMENT_CHARACTER` (�), and truncating the input slice as necessary if
/// it has a length greater than the declared capacity of the StaticString being created.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
/// let string = StaticString::<20>::from_utf16_lossy(music);
/// assert_eq!(string, "𝄞music");
/// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
/// assert_eq!(StaticString::<20>::from_utf16_lossy(invalid_utf16).as_str(), "𝄞mu\u{FFFD}ic");
/// let out_of_bounds: Vec<u16> = (0..300).map(|_| 0).collect();
/// assert_eq!(StaticString::<20>::from_utf16_lossy(&out_of_bounds).as_str(), "\0".repeat(20).as_str());
/// ```
#[inline(always)]
pub fn from_utf16_lossy<B: AsRef<[u16]>>(slice: B) -> Self {
let mut res = Self::new();
for c in decode_utf16(slice.as_ref().iter().copied()) {
if res.try_push(c.unwrap_or(REPLACEMENT_CHARACTER)).is_err() {
break;
}
}
res
}
/// Creates a new StaticString instance from the provided `u16` slice, returning
/// [`StringError::Utf16`] on invalid UTF-16 data, and truncating the input slice as necessary if
/// it has a length greater than the declared capacity of the StaticString being created.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
/// let string = StaticString::<20>::from_utf16(music)?;
/// assert_eq!(string.as_str(), "𝄞music");
/// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
/// assert!(StaticString::<20>::from_utf16(invalid_utf16).unwrap_err().is_utf16());
/// let out_of_bounds: Vec<u16> = (0..300).map(|_| 0).collect();
/// assert_eq!(StaticString::<20>::from_utf16(out_of_bounds)?.as_str(),
/// "\0".repeat(20).as_str());
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn from_utf16<B: AsRef<[u16]>>(slice: B) -> Result<Self, StringError> {
let mut res = Self::new();
for c in decode_utf16(slice.as_ref().iter().copied()) {
if res.try_push(c?).is_err() {
break;
}
}
Ok(res)
}
/// Creates a new StaticString from the provided `u16` slice, returning [`StringError::Utf16`] on
/// invalid UTF-16 data or [`StringError::OutOfBounds`] if the slice has a length greater than the
/// declared capacity of the StaticString being created.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticVec, StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
/// let string = StaticString::<20>::try_from_utf16(music)?;
/// assert_eq!(string.as_str(), "𝄞music");
/// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
/// assert!(StaticString::<20>::try_from_utf16(invalid_utf16).unwrap_err().is_utf16());
/// let out_of_bounds: StaticVec<u16, 300> = (0..300).map(|_| 0).collect();
/// assert!(StaticString::<20>::try_from_utf16(out_of_bounds).unwrap_err().is_out_of_bounds());
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn try_from_utf16<B: AsRef<[u16]>>(slice: B) -> Result<Self, StringError> {
let mut res = Self::new();
for c in decode_utf16(slice.as_ref().iter().copied()) {
res.try_push(c?)?;
}
Ok(res)
}
/// Extracts a `str` slice containing the entire contents of the StaticString.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let s = StaticString::<20>::from_str("My String");
/// assert_eq!(s.as_str(), "My String");
/// ```
#[inline(always)]
pub const fn as_str(&self) -> &str {
unsafe { &*(self.as_bytes() as *const [u8] as *const str) }
}
/// Extracts a mutable `str` slice containing the entire contents of the StaticString.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let mut s = StaticString::<20>::from_str("My String");
/// assert_eq!(s.as_mut_str(), "My String");
/// ```
#[inline(always)]
pub const fn as_mut_str(&mut self) -> &mut str {
unsafe { &mut *(self.as_mut_bytes() as *mut [u8] as *mut str) }
}
/// Extracts a `u8` slice containing the entire contents of the StaticString.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let s = StaticString::<20>::from_str("My String");
/// assert_eq!(s.as_bytes(), "My String".as_bytes());
/// ```
#[inline(always)]
pub const fn as_bytes(&self) -> &[u8] {
self.vec.as_slice()
}
/// Returns the StaticString's internal instance of `StaticVec<u8, N>`.
/// Note that using this function consumes the StaticString.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let s = StaticString::<5>::from("hello");
/// let bytes = s.into_bytes();
/// assert_eq!(&bytes[..], &[104, 101, 108, 108, 111][..]);
/// ```
#[inline(always)]
pub fn into_bytes(self) -> StaticVec<u8, N> {
self.vec
}
/// Extracts a mutable `u8` slice containing the entire contents of the StaticString.
///
/// # Safety
///
/// Care must be taken to ensure that the returned `u8` slice is not mutated in such a way that
/// it no longer amounts to valid UTF-8.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let mut s = StaticString::<20>::try_from_str("My String")?;
/// assert_eq!(unsafe { s.as_mut_bytes() }, "My String".as_bytes());
/// # Ok(())
/// # }
/// ```
#[inline(always)]
pub const unsafe fn as_mut_bytes(&mut self) -> &mut [u8] {
self.vec.as_mut_slice()
}
/// Returns a mutable reference to the StaticString's backing StaticVec.
///
/// # Safety
///
/// Care must be taken to ensure that the returned StaticVec reference is not mutated in such a
/// way that it no longer contains valid UTF-8.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let mut s = StaticString::<20>::try_from_str("My String")?;
/// assert_eq!(unsafe { s.as_mut_staticvec() }, "My String".as_bytes());
/// # Ok(())
/// # }
/// ```
#[inline(always)]
pub const unsafe fn as_mut_staticvec(&mut self) -> &mut StaticVec<u8, N> {
&mut self.vec
}
/// Returns the total capacity of the StaticString.
/// This is always equivalent to the generic `N` parameter it was declared with,
/// which determines the fixed size of the backing StaticVec instance.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// assert_eq!(StaticString::<32>::new().capacity(), 32);
/// ```
#[inline(always)]
pub const fn capacity(&self) -> usize {
self.vec.capacity()
}
/// Returns the remaining capacity (which is to say, `self.capacity() - self.len()`) of the
/// StaticString.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// assert_eq!(StaticString::<32>::from("abcd").remaining_capacity(), 28);
/// ```
#[inline(always)]
pub const fn remaining_capacity(&self) -> usize {
self.vec.remaining_capacity()
}
/// Pushes `string` to the StaticString without doing any checking to ensure that `self.len() +
/// string.len()` does not exceed the StaticString's total capacity.
///
/// # Safety
///
/// `self.len() + string.len()` must not exceed the total capacity of the StaticString
/// instance, as this would result in writing to an out-of-bounds memory region.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString};
/// let mut s = StaticString::<6>::from("foo");
/// unsafe { s.push_str_unchecked("bar") };
/// assert_eq!(s, "foobar");
/// ```
#[inline(always)]
pub const unsafe fn push_str_unchecked(&mut self, string: &str) {
let string_length = string.len();
debug_assert!(string_length <= self.remaining_capacity());
let old_length = self.len();
push_str_unchecked_internal!(self, string, old_length, string_length);
}
/// Attempts to push `string` to the StaticString, panicking if it is the case that `self.len() +
/// string.len()` exceeds the StaticString's total capacity.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString};
/// let mut s = StaticString::<6>::from("foo");
/// s.push_str("bar");
/// assert_eq!(s, "foobar");
/// ```
#[inline]
pub fn push_str<S: AsRef<str>>(&mut self, string: S) {
// Note that when calling this at runtime, the compiler still just sees the signature
// as `push_str<S: AsRef<str>>(&mut self, string: S)`. Adding new `~const` bounds is only
// a "breaking change" if you add them to something that was *already* a `const fn`. Adding
// them while turning something *into* a `const fn` is fully backwards compatible, though.
let string_ref = string.as_ref();
let string_length = string_ref.len();
let old_length = self.vec.length;
assert!(
string_length <= N - old_length,
"Insufficient remaining capacity!"
);
push_str_unchecked_internal!(self, string_ref, old_length, string_length);
}
/// Attempts to push `string` to the StaticString. Truncates `string` as necessary (or simply does
/// nothing at all) if it is the case that `self.len() + string.len()` exceeds the
/// StaticString's total capacity.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let mut s = StaticString::<300>::try_from_str("My String")?;
/// s.push_str_truncating(" My other String");
/// assert_eq!(s.as_str(), "My String My other String");
/// let mut s = StaticString::<20>::new();
/// s.push_str_truncating("0".repeat(21));
/// assert_eq!(s.as_str(), "0".repeat(20).as_str());
/// # Ok(())
/// # }
/// ```
#[inline(always)]
pub fn push_str_truncating<S: AsRef<str>>(&mut self, string: S) {
unsafe { self.push_str_unchecked(truncate_str(string.as_ref(), self.remaining_capacity())) };
}
/// Pushes `string` to the StaticString if `self.len() + string.len()` does not exceed
/// the StaticString's total capacity, or returns a
/// [`CapacityError`](crate::errors::CapacityError) otherwise.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let mut s = StaticString::<300>::from("My String");
/// s.try_push_str(" My other String").unwrap();
/// assert_eq!(s.as_str(), "My String My other String");
/// assert!(s.try_push_str("0".repeat(300)).is_err());
/// ```
#[inline(always)]
pub fn try_push_str<S: AsRef<str>>(&mut self, string: S) -> Result<(), CapacityError<N>> {
let string_ref = string.as_ref();
let string_length = string_ref.len();
let old_length = self.vec.length;
if N - old_length < string_length {
Err(CapacityError {})
} else {
push_str_unchecked_internal!(self, string_ref, old_length, string_length);
Ok(())
}
}
/// Appends the given char to the end of the StaticString without doing any checking to ensure
/// that `self.len() + character.len_utf8()` does not exceed the total capacity of the
/// StaticString instance.
///
/// # Safety
///
/// `self.len() + character.len_utf8()` must not exceed the total capacity of the StaticString
/// instance, as this would result in writing to an out-of-bounds memory region.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let mut s = StaticString::<20>::try_from_str("My String")?;
/// unsafe { s.push_unchecked('!') };
/// assert_eq!(s.as_str(), "My String!");
/// // Undefined behavior, don't do it:
/// // s = StaticString::<20>::try_from_str(&"0".repeat(20))?;
/// // s.push_unchecked('!');
/// # Ok(())
/// # }
/// ```
#[inline(always)]
pub const unsafe fn push_unchecked(&mut self, character: char) {
let char_len = character.len_utf8();
push_char_unchecked_internal!(self, character as u32, char_len);
}
/// Appends the given char to the end of the StaticString, panicking if the StaticString
/// is already at maximum capacity.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// let mut string = StaticString::<2>::new();
/// string.push('a');
/// string.push('b');
/// assert_eq!(&string[..], "ab");
/// ```
#[inline(always)]
pub const fn push(&mut self, character: char) {
let char_len = character.len_utf8();
assert!(
char_len <= self.remaining_capacity(),
"Insufficient remaining capacity!"
);
push_char_unchecked_internal!(self, character as u32, char_len);
}
/// Appends the given char to the end of the StaticString, returning [`StringError::OutOfBounds`]
/// if the StaticString is already at maximum capacity.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let mut s = StaticString::<20>::try_from_str("My String")?;
/// s.try_push('!')?;
/// assert_eq!(s.as_str(), "My String!");
/// let mut s = StaticString::<20>::try_from_str(&"0".repeat(20))?;
/// assert!(s.try_push('!').is_err());
/// # Ok(())
/// # }
/// ```
#[inline(always)]
pub const fn try_push(&mut self, character: char) -> Result<(), StringError> {
let char_len = character.len_utf8();
match self.remaining_capacity() < char_len {
false => {
push_char_unchecked_internal!(self, character as u32, char_len);
Ok(())
}
true => Err(StringError::OutOfBounds),
}
}
/// Truncates the StaticString to `new_len` if `new_len` is less than or equal to the
/// StaticString's current length, or does nothing otherwise. Panics if `new_len` does not lie
/// at a valid UTF-8 character boundary.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let mut s = StaticString::<20>::from("My String");
/// s.truncate(5);
/// assert_eq!(s, "My St");
/// // Does nothing
/// s.truncate(6);
/// assert_eq!(s, "My St");
/// // Would panic
/// // let mut s2 = StaticString::<20>::from("🤔");
/// // s2.truncate(1);
/// ```
#[inline(always)]
pub const fn truncate(&mut self, new_len: usize) {
if new_len <= self.len() {
assert!(str_is_char_boundary(self, new_len));
unsafe { self.vec.set_len(new_len) };
}
}
/// Returns the last character in the StaticString in `Some` if the StaticString's current length
/// is greater than zero, or `None` otherwise.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let mut s = StaticString::<20>::try_from_str("A🤔")?;
/// assert_eq!(s.pop(), Some('🤔'));
/// assert_eq!(s.pop(), Some('A'));
/// assert_eq!(s.pop(), None);
/// # Ok(())
/// # }
/// ```
#[inline(always)]
pub fn pop(&mut self) -> Option<char> {
self.as_str().chars().last().map(|character| {
unsafe { self.vec.set_len(self.len() - character.len_utf8()) };
character
})
}
/// Removes all whitespace from the beginning and end of the StaticString, if any is present.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let mut string = StaticString::<300>::try_from_str(" to be trimmed ")?;
/// string.trim();
/// assert_eq!(string.as_str(), "to be trimmed");
/// let mut string = StaticString::<20>::try_from_str(" 🤔")?;
/// string.trim();
/// assert_eq!(string.as_str(), "🤔");
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn trim(&mut self) {
let is_whitespace = |bytes: &[u8], index: usize| {
debug_assert!(index < bytes.len());
unsafe { bytes.get_unchecked(index) == &b' ' }
};
let (mut start, mut end, mut leave) = (0_usize, self.len(), 0_usize);
while start < end && leave < 2 {
leave = 0;
if is_whitespace(self.as_bytes(), start) {
start += 1;
if start >= end {
continue;
};
} else {
leave += 1;
}
if start < end && is_whitespace(self.as_bytes(), end - 1) {
end -= 1;
} else {
leave += 1;
}
}
unsafe {
shift_left_unchecked!(self, start, 0usize);
self.vec.set_len(end - start)
};
}
/// Removes the char at `index` from the StaticString if `index` is both less than `self.len()`
/// and also lies at a valid UTF-8 character boundary, or panics otherwise.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let mut s = StaticString::<20>::from("ABCD🤔");
/// assert_eq!(s.remove(0), 'A');
/// assert!(s == "BCD🤔");
/// assert_eq!(s.remove(2), 'D');
/// assert!(s == "BC🤔");
/// ```
#[inline]
pub fn remove(&mut self, index: usize) -> char {
assert!(
self.as_str().is_char_boundary(index),
"Out of bounds or invalid character boundary!"
);
let old_length = self.len();
let character = unsafe { self.as_str().get_unchecked(index..).chars().next() };
let character = character.unwrap_or_else(|| unsafe { never("Missing char") });
let char_length = character.len_utf8();
unsafe {
shift_left_unchecked!(self, index + char_length, index);
self.vec.set_len(old_length - char_length);
}
character
}
/// Removes all matches of pattern `pat` in the `StaticString`.
///
/// # Example usage:
/// ```
/// # use staticvec::{staticstring, StaticString};
/// let mut s = staticstring!("Trees are not green, the sky is not blue.");
/// s.remove_matches("not ");
/// assert_eq!("Trees are green, the sky is blue.", s.as_str());
/// ```
///
/// Matches will be detected and removed iteratively, so in cases where
/// patterns overlap, only the first pattern will be removed:
///
/// ```
/// # use staticvec::{staticstring, StaticString};
/// let mut s = staticstring!("banana");
/// s.remove_matches("ana");
/// assert_eq!("bna", s.as_str());
/// ```
#[inline]
pub fn remove_matches<'a, P: for<'x> Pattern<'x>>(&'a mut self, pat: P) {
let old_length = self.len();
if old_length == 0 {
return;
}
let mut matches = StaticVec::<(usize, usize), N>::new();
// Create `searcher` in a scope, so that it's dropped exactly when we want.
{
let mut searcher = pat.into_searcher(self);
while let Some(m) = searcher.next_match() {
// Safety: it is not possible for the number of matches to be greater than 'N', because
// 'N' is the number of individual bytes that the `StaticString` this function is being
// called through has guaranteed capacity for.
unsafe {
matches.push_unchecked(m);
}
}
}
let mut shrunk_by = 0;
let vec_ptr = self.vec.as_mut_ptr();
for (start, end) in &matches {
// Safety: `start` and `end` will be on UTF-8 byte boundaries per the `Searcher` docs.
unsafe {
vec_ptr
.add(end - shrunk_by)
.copy_to(vec_ptr.add(start - shrunk_by), old_length - end);
}
shrunk_by += end - start;
}
unsafe {
self.vec.set_len(old_length - shrunk_by);
}
}
/// Removes all characters from the StaticString except for those specified by the predicate
/// function.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let mut s = StaticString::<20>::from("ABCD🤔");
/// s.retain(|c| c != '🤔');
/// assert_eq!(s, "ABCD");
/// ```
#[inline(always)]
pub fn retain<F: FnMut(char) -> bool>(&mut self, mut f: F) {
// Having benched `retain` implemented both this way and exactly how `std::string::String`
// does, this way is faster believe it or not.
*self = Self::from_chars(self.as_str().chars().filter(|c| f(*c)));
}
/// Inserts `character` at `index`, shifting any values that exist in positions greater than
/// `index` to the right.
///
/// Does not do any checking to ensure that `character.len_utf8() + self.len()` does not exceed
/// the total capacity of the StaticString or that `index` lies at a valid UTF-8 character
/// boundary.
///
/// # Safety
///
/// The length of the StaticString prior to calling this function must be less than its total
/// capacity, as if this in not the case it will result in writing to an out-of-bounds memory
/// region.
///
/// `Index` must also lie at a valid UTF-8 character boundary, as if it does not, various
/// assumptions made in the internal implementation of StaticString will be silently
/// invalidated, almost certainly eventually resulting in undefined behavior.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let mut s = StaticString::<20>::try_from_str("ABCD🤔")?;
/// unsafe { s.insert_unchecked(1, 'A') };
/// unsafe { s.insert_unchecked(1, 'B') };
/// assert_eq!(s.as_str(), "ABABCD🤔");
/// // Undefined behavior, don't do it:
/// // s.insert(20, 'C');
/// // s.insert(8, 'D');
/// # Ok(())
/// # }
/// ```
#[inline(always)]
pub unsafe fn insert_unchecked(&mut self, index: usize, character: char) {
let char_length = character.len_utf8();
shift_right_unchecked!(self, index, index + char_length);
encode_char_utf8_unchecked(self, character, index);
}
/// Inserts `character` at `index`, shifting any values that exist in positions greater than
/// `index` to the right.
///
/// Returns [`StringError::OutOfBounds`] if `character.len_utf8() + self.len()` exceeds the total
/// capacity of the StaticString and [`StringError::NotCharBoundary`] if `index` does not lie at
/// a valid UTF-8 character boundary.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let mut s = StaticString::<20>::try_from_str("ABCD🤔")?;
/// s.try_insert(1, 'E')?;
/// s.try_insert(2, 'F')?;
/// assert_eq!(s.as_str(), "AEFBCD🤔");
/// assert!(s.try_insert(20, 'C').unwrap_err().is_not_char_boundary());
/// assert!(s.try_insert(8, 'D').unwrap_err().is_not_char_boundary());
/// let mut s = StaticString::<20>::try_from_str(&"0".repeat(20))?;
/// assert!(s.try_insert(0, 'C').unwrap_err().is_out_of_bounds());
/// # Ok(())
/// # }
/// ```
#[inline(always)]
pub fn try_insert(&mut self, index: usize, character: char) -> Result<(), StringError> {
is_inside_boundary(character.len_utf8() + self.len(), N)?;
is_char_boundary(self, index)?;
unsafe { self.insert_unchecked(index, character) };
Ok(())
}
/// Inserts `character` at `index`, shifting any values that exist in positions greater than
/// `index` to the right.
///
/// Panics if `character.len_utf8() + self.len()` exceeds the total capacity of the StaticString
/// or if `index` does not lie at a valid UTF-8 character boundary.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString};
/// let mut s = StaticString::<3>::new();
/// s.insert(0, 'f');
/// s.insert(1, 'o');
/// s.insert(2, 'o');
/// assert_eq!(s, "foo");
/// ```
#[inline(always)]
pub fn insert(&mut self, index: usize, character: char) {
self.try_insert(index, character).unwrap();
}
/// Inserts `string` at `index`, shifting any values that exist in positions greater than
/// `index` to the right.
///
/// Does not do any checking to ensure that `self.len() + string.len()` does not exceed
/// the total capacity of the StaticString or that `index` lies at a valid UTF-8
/// character boundary.
///
/// # Safety
///
/// `self.len() + string.len()` must not exceed the total capacity of the StaticString instance,
/// as this would result in writing to an out-of-bounds memory region.
///
/// `Index` must also lie at a valid UTF-8 character boundary, as if it does not, various
/// assumptions made in the internal implementation of StaticString will be silently
/// invalidated, almost certainly eventually resulting in undefined behavior.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let mut s = StaticString::<20>::from_str("ABCD🤔");
/// unsafe { s.insert_str_unchecked(1, "AB") };
/// unsafe { s.insert_str_unchecked(1, "BC") };
/// assert_eq!(s, "ABCABBCD🤔");
/// // Undefined behavior, don't do it:
/// // unsafe { s.insert_str_unchecked(20, "C") };
/// // unsafe { s.insert_str_unchecked(10, "D") };
/// // unsafe { s.insert_str_unchecked(1, "0".repeat(20)) };
/// ```
#[inline]
pub unsafe fn insert_str_unchecked<S: AsRef<str>>(&mut self, index: usize, string: S) {
let string_ref = string.as_ref();
let string_length = string_ref.len();
debug_assert!(string_length <= self.remaining_capacity());
let string_ptr = string_ref.as_ptr();
shift_right_unchecked!(self, index, index + string_length);
string_ptr.copy_to_nonoverlapping(self.vec.mut_ptr_at_unchecked(index), string_length);
self.vec.set_len(self.len() + string_length);
}
/// Inserts `string` at `index`, shifting any values that exist in positions greater than
/// `index` to the right.
///
/// Panics if `index` is greater than the length of the StaticString or if it does not lie
/// at a valid UTF-8 character boundary, as well as if `string.len() + self.len()` exceeds
/// the total capacity of the StaticString.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let mut s = StaticString::<20>::from("ABCD🤔");
/// s.insert_str(1, "AB");
/// s.insert_str(1, "BC");
/// assert_eq!(s.as_str(), "ABCABBCD🤔");
/// ```
#[inline(always)]
pub fn insert_str<S: AsRef<str>>(&mut self, index: usize, string: S) {
let string_ref = string.as_ref();
let string_length = string_ref.len();
assert!(
string_length <= self.remaining_capacity() && self.as_str().is_char_boundary(index),
"Insufficient remaining capacity or invalid character boundary!"
);
unsafe { self.insert_str_unchecked(index, string_ref) };
}
/// Inserts `string` at `index`, shifting any values that exist in positions greater than
/// `index` to the right.
///
/// Returns [`StringError::OutOfBounds`] if `self.len() + string.len()` exceeds the total
/// capacity of the StaticString and [`StringError::NotCharBoundary`] if `index` does not
/// lie at a valid UTF-8 character boundary.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let mut string = StaticString::<20>::try_from_str("ABCD🤔")?;
/// string.try_insert_str(1, "AB")?;
/// string.try_insert_str(1, "BC")?;
/// assert!(string.try_insert_str(1, "0".repeat(20)).unwrap_err().is_out_of_bounds());
/// assert_eq!(string.as_str(), "ABCABBCD🤔");
/// assert!(string.try_insert_str(20, "C").unwrap_err().is_not_char_boundary());
/// assert!(string.try_insert_str(10, "D").unwrap_err().is_not_char_boundary());
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn try_insert_str<S: AsRef<str>>(
&mut self,
index: usize,
string: S,
) -> Result<(), StringError> {
let string_ref = string.as_ref();
is_inside_boundary(self.len() + string_ref.len(), N)?;
is_char_boundary(self, index)?;
unsafe { self.insert_str_unchecked(index, string_ref) };
Ok(())
}
/// Returns the current length of the StaticString.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let mut s = StaticString::<20>::from("ABCD");
/// assert_eq!(s.len(), 4);
/// s.push('🤔');
/// assert_eq!(s.len(), 8);
/// ```
#[inline(always)]
pub const fn len(&self) -> usize {
self.vec.len()
}
/// Returns true if the StaticString has a current length of 0.
///
/// # Example usage:
/// ```
/// # use staticvec::{staticstring, StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let mut s = staticstring!("ABCD");
/// assert!(!s.is_empty());
/// s.clear();
/// assert!(s.is_empty());
/// # Ok(())
/// # }
/// ```
#[inline(always)]
pub const fn is_empty(&self) -> bool {
self.vec.is_empty()
}
/// Returns true if the StaticString's length is equal to its capacity.
///
/// # Example usage:
/// ```
/// # use staticvec::{staticstring, StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let mut s = staticstring!("ABCD");
/// assert!(s.is_full());
/// s.clear();
/// assert!(!s.is_full());
/// # Ok(())
/// # }
/// ```
#[inline(always)]
pub const fn is_full(&self) -> bool {
self.vec.is_full()
}
/// Splits the StaticString in two if `at` is less than the its current length.
///
/// The original StaticString will contain elements `0..at`, and the new one will contain
/// elements `at..self.len()`.
///
/// Panics if `at` is greater than the length of the StaticString or if it does not
/// lie at a valid UTF-8 character boundary.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let mut ab = StaticString::<4>::from("ABCD");
/// let cd = ab.split_off(2);
/// assert_eq!(ab, "AB");
/// assert_eq!(cd, "CD");
/// ```
#[inline(always)]
pub fn split_off(&mut self, at: usize) -> Self {
assert!(
at <= self.len() && str_is_char_boundary(self.as_str(), at),
"Out of bounds or invalid character boundary!"
);
unsafe {
let res = Self::from_utf8_unchecked(self.as_str().get_unchecked(at..));
self.vec.set_len(at);
res
}
}
/// Removes all contents from the StaticString and sets its length back to zero.
///
/// # Example usage:
/// ```
/// # use staticvec::{StaticString, StringError};
/// # fn main() -> Result<(), StringError> {
/// let mut s = StaticString::<20>::try_from_str("ABCD")?;
/// assert!(!s.is_empty());
/// s.clear();
/// assert!(s.is_empty());
/// # Ok(())
/// # }
/// ```
#[inline(always)]
pub const fn clear(&mut self) {
unsafe { self.vec.set_len(0) };
}
/// Removes the specified range from the StaticString and replaces it with the provided input
/// (which does not need to have the same length as the range being removed), panicking if
/// either the high or low bounds of the range exceed `self.len()` or do not lie at valid UTF-8
/// character boundaries.
///
/// # Example usage:
/// ```
/// # use staticvec::StaticString;
/// let mut s = StaticString::<20>::from("ABCD🤔");
/// s.replace_range(2..4, "EFGHI");
/// assert_eq!(s.as_str(), "ABEFGHI🤔");
/// ```
#[inline]
pub fn replace_range<S: AsRef<str>, R: RangeBounds<usize>>(&mut self, range: R, with: S) {
let replace_with = with.as_ref();
let old_length = self.len();
let start = match range.start_bound() {
Bound::Included(t) => *t,
Bound::Excluded(t) => t + 1,
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(t) => t + 1,
Bound::Excluded(t) => *t,
Bound::Unbounded => old_length,
};
let replace_length = replace_with.len();
assert!(
start <= end && end <= old_length,
"Invalid range or out of bounds!"
);
let replaced = end.saturating_sub(start);
assert!(
replaced + replace_length <= N
&& str_is_char_boundary(self, start)
&& str_is_char_boundary(self, end),
"Out of bounds or invalid character boundary!"
);
if replace_length == 0 {
unsafe {
let mp = self.vec.as_mut_ptr();
mp.add(end)
.copy_to(mp.add(start), old_length.saturating_sub(end));
self.vec.set_len(old_length.saturating_sub(replaced));
}
} else {
if start + replace_length > end {
unsafe {
shift_right_unchecked!(self, end, start + replace_length);
}
} else {
unsafe {
shift_left_unchecked!(self, end, start + replace_length);
}
}
let ptr = replace_with.as_ptr();
let dest = unsafe { self.vec.as_mut_ptr().add(start) };
unsafe { ptr.copy_to_nonoverlapping(dest, replace_length) };
let grow: isize = replace_length as isize - replaced as isize;
unsafe { self.vec.set_len((old_length as isize + grow) as usize) };
}
}
}