//! `ArrayString` definition and Api implementation
usecrate::utils::{encode_char_utf8_unchecked, is_char_boundary, is_inside_boundary, never}; usecrate::utils::{shift_left_unchecked, shift_right_unchecked, truncate_str, IntoLossy}; usecrate::{error::Error, generic::ArraySlice, prelude::*}; use core::char::{decode_utf16, REPLACEMENT_CHARACTER}; use core::str::{from_utf8, from_utf8_unchecked}; use core::{cmp::min, ops::*, ptr::copy_nonoverlapping}; #[cfg(feature = "logs")] use log::{debug, trace};
usecrate::generic::Capacity;
/// String based on a generic array (size defined at compile time through `typenum`) /// /// Can't outgrow capacity (defined at compile time), always occupies [`capacity`] `+ 1` bytes of memory /// /// *Doesn't allocate memory on the heap and never panics (all panic branches are stripped at compile time)* /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity #[derive(Clone)] pubstruct ArrayString<SIZE: Capacity> { /// Array type corresponding to specified `SIZE` pub(crate) array: SIZE::Array, /// Current string size pub(crate) size: u8,
}
/// Creates new `ArrayString` from string slice if length is lower or equal to [`capacity`], otherwise returns an error. /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let string = SmallString::try_from_str("My String")?; /// assert_eq!(string.as_str(), "My String"); /// /// assert_eq!(SmallString::try_from_str("")?.as_str(), ""); /// /// let out_of_bounds = "0".repeat(SmallString::capacity() as usize + 1); /// assert!(SmallString::try_from_str(out_of_bounds).is_err()); /// # Ok(()) /// # } /// ``` #[inline] pubfn try_from_str<S>(s: S) -> Result<Self, OutOfBounds> where
S: AsRef<str>,
{
trace!("Try from str: {:?}", s.as_ref());
is_inside_boundary(s.as_ref().len(), Self::capacity())?; unsafe { Ok(Self::from_str_unchecked(s.as_ref())) }
}
/// Creates new `ArrayString` from string slice truncating size if bigger than [`capacity`]. /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::prelude::*; /// # let _ = env_logger::try_init(); /// let string = SmallString::from_str_truncate("My String"); /// # assert_eq!(string.as_str(), "My String"); /// println!("{}", string); /// /// let truncate = "0".repeat(SmallString::capacity() as usize + 1); /// let truncated = "0".repeat(SmallString::capacity().into()); /// let string = SmallString::from_str_truncate(&truncate); /// assert_eq!(string.as_str(), truncated); /// ``` #[inline] pubfn from_str_truncate<S>(string: S) -> Self where
S: AsRef<str>,
{
trace!("FromStr truncate"); unsafe { Self::from_str_unchecked(truncate_str(string.as_ref(), Self::capacity())) }
}
/// Creates new `ArrayString` from string slice assuming length is appropriate. /// /// # Safety /// /// It's UB if `string.len()` > [`capacity`]. /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::prelude::*; /// let filled = "0".repeat(SmallString::capacity().into()); /// let string = unsafe { /// SmallString::from_str_unchecked(&filled) /// }; /// assert_eq!(string.as_str(), filled.as_str()); /// /// // Undefined behavior, don't do it /// // let out_of_bounds = "0".repeat(SmallString::capacity().into() + 1); /// // let ub = unsafe { SmallString::from_str_unchecked(out_of_bounds) }; /// ``` #[inline] pubunsafefn from_str_unchecked<S>(string: S) -> Self where
S: AsRef<str>,
{
trace!("FromStr unchecked"); letmut out = Self::default();
out.push_str_unchecked(string);
out
}
/// Creates new `ArrayString` from string slice iterator if total length is lower or equal to [`capacity`], otherwise returns an error. /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::prelude::*; /// # fn main() -> Result<(), OutOfBounds> { /// let string = MaxString::try_from_iterator(&["My String", " My Other String"][..])?; /// assert_eq!(string.as_str(), "My String My Other String"); /// /// let out_of_bounds = (0..100).map(|_| "000"); /// assert!(SmallString::try_from_iterator(out_of_bounds).is_err()); /// # Ok(()) /// # } /// ``` #[inline] pubfn try_from_iterator<U, I>(iter: I) -> Result<Self, OutOfBounds> where
U: AsRef<str>,
I: IntoIterator<Item = U>,
{
trace!("FromIterator"); letmut out = Self::default(); for s in iter {
out.try_push_str(s)?;
}
Ok(out)
}
/// Creates new `ArrayString` from string slice iterator truncating size if bigger than [`capacity`]. /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::prelude::*; /// # fn main() -> Result<(), OutOfBounds> { /// # let _ = env_logger::try_init(); /// let string = MaxString::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(SmallString::capacity().into()); /// /// let truncate = SmallString::from_iterator(out_of_bounds); /// assert_eq!(truncate.as_str(), truncated.as_str()); /// # Ok(()) /// # } /// ``` #[inline] pubfn from_iterator<U, I>(iter: I) -> Self where
U: AsRef<str>,
I: IntoIterator<Item = U>,
{
trace!("FromIterator truncate"); letmut out = Self::default(); for s in iter { if out.try_push_str(s.as_ref()).is_err() {
out.push_str(s); break;
}
}
out
}
/// Creates new `ArrayString` from string slice iterator assuming length is appropriate. /// /// # Safety /// /// It's UB if `iter.map(|c| c.len()).sum()` > [`capacity`]. /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::prelude::*; /// let string = unsafe { /// MaxString::from_iterator_unchecked(&["My String", " My Other String"][..]) /// }; /// assert_eq!(string.as_str(), "My String My Other String"); /// /// // Undefined behavior, don't do it /// // let out_of_bounds = (0..400).map(|_| "000"); /// // let undefined_behavior = unsafe { /// // SmallString::from_iterator_unchecked(out_of_bounds) /// // }; /// ``` #[inline] pubunsafefn from_iterator_unchecked<U, I>(iter: I) -> Self where
U: AsRef<str>,
I: IntoIterator<Item = U>,
{
trace!("FromIterator unchecked"); letmut out = Self::default(); for s in iter {
out.push_str_unchecked(s);
}
out
}
/// Creates new `ArrayString` from char iterator if total length is lower or equal to [`capacity`], otherwise returns an error. /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let string = SmallString::try_from_chars("My String".chars())?; /// assert_eq!(string.as_str(), "My String"); /// /// let out_of_bounds = "0".repeat(SmallString::capacity() as usize + 1); /// assert!(SmallString::try_from_chars(out_of_bounds.chars()).is_err()); /// # Ok(()) /// # } /// ``` #[inline] pubfn try_from_chars<I>(iter: I) -> Result<Self, OutOfBounds> where
I: IntoIterator<Item = char>,
{
trace!("TryFrom chars"); letmut out = Self::default(); for c in iter {
out.try_push(c)?;
}
Ok(out)
}
/// Creates new `ArrayString` from char iterator truncating size if bigger than [`capacity`]. /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::prelude::*; /// # let _ = env_logger::try_init(); /// let string = SmallString::from_chars("My String".chars()); /// assert_eq!(string.as_str(), "My String"); /// /// let out_of_bounds = "0".repeat(SmallString::capacity() as usize + 1); /// let truncated = "0".repeat(SmallString::capacity().into()); /// /// let truncate = SmallString::from_chars(out_of_bounds.chars()); /// assert_eq!(truncate.as_str(), truncated.as_str()); /// ``` #[inline] pubfn from_chars<I>(iter: I) -> Self where
I: IntoIterator<Item = char>,
{
trace!("From chars truncate"); letmut out = Self::default(); for c in iter { if out.try_push(c).is_err() { break;
}
}
out
}
/// Creates new `ArrayString` from char iterator assuming length is appropriate. /// /// # Safety /// /// It's UB if `iter.map(|c| c.len_utf8()).sum()` > [`capacity`]. /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::prelude::*; /// let string = unsafe { SmallString::from_chars_unchecked("My String".chars()) }; /// assert_eq!(string.as_str(), "My String"); /// /// // Undefined behavior, don't do it /// // let out_of_bounds = "000".repeat(400); /// // let undefined_behavior = unsafe { SmallString::from_chars_unchecked(out_of_bounds.chars()) }; /// ``` #[inline] pubunsafefn from_chars_unchecked<I>(iter: I) -> Self where
I: IntoIterator<Item = char>,
{
trace!("From chars unchecked"); letmut out = Self::default(); for c in iter {
out.push_unchecked(c)
}
out
}
/// Creates new `ArrayString` from byte slice, returning [`Utf8`] on invalid utf-8 data or [`OutOfBounds`] if bigger than [`capacity`] /// /// [`Utf8`]: ./error/enum.Error.html#variant.Utf8 /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let string = SmallString::try_from_utf8("My String")?; /// assert_eq!(string.as_str(), "My String"); /// /// let invalid_utf8 = [0, 159, 146, 150]; /// assert_eq!(SmallString::try_from_utf8(invalid_utf8), Err(Error::Utf8)); /// /// let out_of_bounds = "0000".repeat(400); /// assert_eq!(SmallString::try_from_utf8(out_of_bounds.as_bytes()), Err(Error::OutOfBounds)); /// # Ok(()) /// # } /// ``` #[inline] pubfn try_from_utf8<B>(slice: B) -> Result<Self, Error> where
B: AsRef<[u8]>,
{
debug!("From utf8: {:?}", slice.as_ref());
Ok(Self::try_from_str(from_utf8(slice.as_ref())?)?)
}
/// Creates new `ArrayString` from byte slice, returning [`Utf8`] on invalid utf-8 data, truncating if bigger than [`capacity`]. /// /// [`Utf8`]: ./error/struct.Utf8.html /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let string = SmallString::from_utf8("My String")?; /// assert_eq!(string.as_str(), "My String"); /// /// let invalid_utf8 = [0, 159, 146, 150]; /// assert_eq!(SmallString::from_utf8(invalid_utf8), Err(Utf8)); /// /// let out_of_bounds = "0".repeat(300); /// assert_eq!(SmallString::from_utf8(out_of_bounds.as_bytes())?.as_str(), /// "0".repeat(SmallString::capacity().into()).as_str()); /// # Ok(()) /// # } /// ``` #[inline] pubfn from_utf8<B>(slice: B) -> Result<Self, Utf8> where
B: AsRef<[u8]>,
{
debug!("From utf8: {:?}", slice.as_ref());
Ok(Self::from_str_truncate(from_utf8(slice.as_ref())?))
}
/// Creates new `ArrayString` from byte slice assuming it's utf-8 and of a appropriate size. /// /// # Safety /// /// It's UB if `slice` is not a valid utf-8 string or `slice.len()` > [`capacity`]. /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::prelude::*; /// let string = unsafe { SmallString::from_utf8_unchecked("My String") }; /// assert_eq!(string.as_str(), "My String"); /// /// // Undefined behavior, don't do it /// // let out_of_bounds = "0".repeat(300); /// // let ub = unsafe { SmallString::from_utf8_unchecked(out_of_bounds)) }; /// ``` #[inline] pubunsafefn from_utf8_unchecked<B>(slice: B) -> Self where
B: AsRef<[u8]>,
{
trace!("From utf8 unchecked");
debug_assert!(from_utf8(slice.as_ref()).is_ok()); Self::from_str_unchecked(from_utf8_unchecked(slice.as_ref()))
}
/// Creates new `ArrayString` from `u16` slice, returning [`Utf16`] on invalid utf-16 data or [`OutOfBounds`] if bigger than [`capacity`] /// /// [`Utf16`]: ./error/enum.Error.html#variant.Utf16 /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063]; /// let string = SmallString::try_from_utf16(music)?; /// assert_eq!(string.as_str(), "music"); /// /// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063]; /// assert_eq!(SmallString::try_from_utf16(invalid_utf16), Err(Error::Utf16)); /// /// let out_of_bounds: Vec<_> = (0..300).map(|_| 0).collect(); /// assert_eq!(SmallString::try_from_utf16(out_of_bounds), Err(Error::OutOfBounds)); /// # Ok(()) /// # } /// ``` #[inline] pubfn try_from_utf16<B>(slice: B) -> Result<Self, Error> where
B: AsRef<[u16]>,
{
debug!("From utf16: {:?}", slice.as_ref()); letmut out = Self::default(); for c in decode_utf16(slice.as_ref().iter().cloned()) {
out.try_push(c?)?;
}
Ok(out)
}
/// Creates new `ArrayString` from `u16` slice, returning [`Utf16`] on invalid utf-16 data, truncating if bigger than [`capacity`]. /// /// [`Utf16`]: ./error/struct.Utf16.html /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063]; /// let string = SmallString::from_utf16(music)?; /// assert_eq!(string.as_str(), "music"); /// /// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063]; /// assert_eq!(SmallString::from_utf16(invalid_utf16), Err(Utf16)); /// /// let out_of_bounds: Vec<u16> = (0..300).map(|_| 0).collect(); /// assert_eq!(SmallString::from_utf16(out_of_bounds)?.as_str(), /// "\0".repeat(SmallString::capacity().into()).as_str()); /// # Ok(()) /// # } /// ``` #[inline] pubfn from_utf16<B>(slice: B) -> Result<Self, Utf16> where
B: AsRef<[u16]>,
{
debug!("From utf16: {:?}", slice.as_ref()); letmut out = Self::default(); for c in decode_utf16(slice.as_ref().iter().cloned()) { if out.try_push(c?).is_err() { break;
}
}
Ok(out)
}
/// Creates new `ArrayString` from `u16` slice, replacing invalid utf-16 data with `REPLACEMENT_CHARACTER` (\u{FFFD}) and truncating size if bigger than [`capacity`] /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063]; /// let string = SmallString::from_utf16_lossy(music); /// assert_eq!(string.as_str(), "music"); /// /// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063]; /// assert_eq!(SmallString::from_utf16_lossy(invalid_utf16).as_str(), "mu\u{FFFD}ic"); /// /// let out_of_bounds: Vec<u16> = (0..300).map(|_| 0).collect(); /// assert_eq!(SmallString::from_utf16_lossy(&out_of_bounds).as_str(), /// "\0".repeat(SmallString::capacity().into()).as_str()); /// # Ok(()) /// # } /// ``` #[inline] pubfn from_utf16_lossy<B>(slice: B) -> Self where
B: AsRef<[u16]>,
{
debug!("From utf16 lossy: {:?}", slice.as_ref()); letmut out = Self::default(); for c in decode_utf16(slice.as_ref().iter().cloned()) { if out.try_push(c.unwrap_or(REPLACEMENT_CHARACTER)).is_err() { break;
}
}
out
}
/// Extracts a string slice containing the entire `ArrayString` /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let s = SmallString::try_from_str("My String")?; /// assert_eq!(s.as_str(), "My String"); /// # Ok(()) /// # } /// ``` #[inline] pubfn as_str(&self) -> &str {
trace!("As str: {}", <Selfas AsRef<str>>::as_ref(self)); self.as_ref()
}
/// Extracts a mutable string slice containing the entire `ArrayString` /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let mut s = SmallString::try_from_str("My String")?; /// assert_eq!(s.as_mut_str(), "My String"); /// # Ok(()) /// # } /// ``` #[inline] pubfn as_mut_str(&mutself) -> &mut str {
trace!("As mut str: {}", self.as_mut()); self.as_mut()
}
/// Extracts a byte slice containing the entire `ArrayString` /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let s = SmallString::try_from_str("My String")?; /// assert_eq!(s.as_bytes(), "My String".as_bytes()); /// # Ok(()) /// # } /// ``` #[inline] pubfn as_bytes(&self) -> &[u8] {
trace!("As str: {}", self.as_str()); self.as_ref()
}
/// Extracts a mutable string slice containing the entire `ArrayString` /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// let mut s = SmallString::try_from_str("My String")?; /// assert_eq!(unsafe { s.as_mut_bytes() }, "My String".as_bytes()); /// # Ok(()) /// # } /// ``` #[inline] pubunsafefn as_mut_bytes(&mutself) -> &mut [u8] {
trace!("As mut str: {}", self.as_str()); let len = self.len(); self.array.as_mut_slice().get_unchecked_mut(..len.into())
}
/// Returns maximum string capacity, defined at compile time, it will never change /// /// ```rust /// # use arraystring::prelude::*; /// # let _ = env_logger::try_init(); /// assert_eq!(ArrayString::<typenum::U32>::capacity(), 32); /// ``` #[inline] pubfn capacity() -> u8 {
SIZE::to_u8()
}
/// Pushes string slice to the end of the `ArrayString` if total size is lower or equal to [`capacity`], otherwise returns an error. /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let mut s = MaxString::try_from_str("My String")?; /// s.try_push_str(" My other String")?; /// assert_eq!(s.as_str(), "My String My other String"); /// /// assert!(s.try_push_str("0".repeat(MaxString::capacity().into())).is_err()); /// # Ok(()) /// # } /// ``` #[inline] pubfn try_push_str<S>(&mutself, string: S) -> Result<(), OutOfBounds> where
S: AsRef<str>,
{
trace!("Push str"); let new_end = string.as_ref().len().saturating_add(self.len().into());
is_inside_boundary(new_end, Self::capacity())?; unsafe { self.push_str_unchecked(string) };
Ok(())
}
/// Pushes string slice to the end of the `ArrayString` truncating total size if bigger than [`capacity`]. /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let mut s = MaxString::try_from_str("My String")?; /// s.push_str(" My other String"); /// assert_eq!(s.as_str(), "My String My other String"); /// /// let mut s = SmallString::default(); /// s.push_str("0".repeat(SmallString::capacity() as usize + 1)); /// assert_eq!(s.as_str(), "0".repeat(SmallString::capacity().into()).as_str()); /// # Ok(()) /// # } /// ``` #[inline] pubfn push_str<S>(&mutself, string: S) where
S: AsRef<str>,
{
trace!("Push str truncate"); let size = Self::capacity().saturating_sub(self.len()); unsafe { self.push_str_unchecked(truncate_str(string.as_ref(), size)) }
}
/// Pushes string slice to the end of the `ArrayString` assuming total size is appropriate. /// /// # Safety /// /// It's UB if `self.len() + string.len()` > [`capacity`]. /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// let mut s = MaxString::try_from_str("My String")?; /// unsafe { s.push_str_unchecked(" My other String") }; /// assert_eq!(s.as_str(), "My String My other String"); /// /// // Undefined behavior, don't do it /// // let mut undefined_behavior = SmallString::default(); /// // undefined_behavior.push_str_unchecked("0".repeat(SmallString::capacity().into() + 1)); /// # Ok(()) /// # } /// ``` #[inline] pubunsafefn push_str_unchecked<S>(&mutself, string: S) where
S: AsRef<str>,
{ let (s, len) = (string.as_ref(), string.as_ref().len());
debug!("Push str unchecked: {} ({} + {})", s, self.len(), len);
debug_assert!(len.saturating_add(self.len().into()) <= Self::capacity() as usize);
let dest = self.as_mut_bytes().as_mut_ptr().add(self.len().into());
copy_nonoverlapping(s.as_ptr(), dest, len); self.size = self.size.saturating_add(len.into_lossy());
}
/// Inserts character to the end of the `ArrayString` erroring if total size if bigger than [`capacity`]. /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let mut s = SmallString::try_from_str("My String")?; /// s.try_push('!')?; /// assert_eq!(s.as_str(), "My String!"); /// /// let mut s = SmallString::try_from_str(&"0".repeat(SmallString::capacity().into()))?; /// assert!(s.try_push('!').is_err()); /// # Ok(()) /// # } /// ``` #[inline] pubfn try_push(&mutself, character: char) -> Result<(), OutOfBounds> {
trace!("Push: {}", character); let new_end = character.len_utf8().saturating_add(self.len().into());
is_inside_boundary(new_end, Self::capacity())?; unsafe { self.push_unchecked(character) };
Ok(())
}
/// Inserts character to the end of the `ArrayString` assuming length is appropriate /// /// # Safety /// /// It's UB if `self.len() + character.len_utf8()` > [`capacity`] /// /// [`capacity`]: ./struct.ArrayString.html#method.capacity /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// let mut s = SmallString::try_from_str("My String")?; /// unsafe { s.push_unchecked('!') }; /// assert_eq!(s.as_str(), "My String!"); /// /// // s = SmallString::try_from_str(&"0".repeat(SmallString::capacity().into()))?; /// // Undefined behavior, don't do it /// // s.push_unchecked('!'); /// # Ok(()) /// # } /// ``` #[inline] pubunsafefn push_unchecked(&mutself, ch: char) { let (len, chlen) = (self.len(), ch.len_utf8().into_lossy());
debug!("Push unchecked (len: {}): {} (len: {})", len, ch, chlen);
encode_char_utf8_unchecked(self, ch, len); self.size = self.size.saturating_add(chlen);
}
/// Truncates `ArrayString` to specified size (if smaller than current size and a valid utf-8 char index). /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let mut s = SmallString::try_from_str("My String")?; /// s.truncate(5)?; /// assert_eq!(s.as_str(), "My St"); /// /// // Does nothing /// s.truncate(6)?; /// assert_eq!(s.as_str(), "My St"); /// /// // Index is not at a valid char /// let mut s = SmallString::try_from_str("")?; /// assert!(s.truncate(1).is_err()); /// # Ok(()) /// # } /// ``` #[inline] pubfn truncate(&mutself, size: u8) -> Result<(), Utf8> {
debug!("Truncate: {}", size); let len = min(self.len(), size);
is_char_boundary(self, len).map(|()| self.size = len)
}
/// Removes last character from `ArrayString`, if any. /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let mut s = SmallString::try_from_str("A")?; /// assert_eq!(s.pop(), Some('')); /// assert_eq!(s.pop(), Some('A')); /// assert_eq!(s.pop(), None); /// # Ok(()) /// # } /// ``` #[inline] pubfn pop(&mutself) -> Option<char> {
debug!("Pop"); self.as_str().chars().last().map(|ch| { self.size = self.size.saturating_sub(ch.len_utf8().into_lossy());
ch
})
}
/// Removes spaces from the beggining and end of the string /// /// ```rust /// # use arraystring::prelude::*; /// # fn main() -> Result<(), OutOfBounds> { /// # let _ = env_logger::try_init(); /// let mut string = MaxString::try_from_str(" to be trimmed ")?; /// string.trim(); /// assert_eq!(string.as_str(), "to be trimmed"); /// /// let mut string = SmallString::try_from_str(" ")?; /// string.trim(); /// assert_eq!(string.as_str(), ""); /// # Ok(()) /// # } /// ``` #[inline] pubfn trim(&mutself) {
trace!("Trim"); let is_whitespace = |s: &[u8], index: usize| {
debug_assert!(index < s.len()); unsafe { s.get_unchecked(index) == &b' ' }
}; let (mut start, mut end, mut leave) = (0_u8, self.len(), 0_u8); while start < end && leave < 2 {
leave = 0;
if is_whitespace(self.as_bytes(), start.into()) {
start = start.saturating_add(1); if start >= end { continue;
};
} else {
leave = leave.saturating_add(1);
}
if start < end && is_whitespace(self.as_bytes(), end.saturating_sub(1).into()) {
end = end.saturating_sub(1);
} else {
leave = leave.saturating_add(1);
}
}
/// Creates a draining iterator that removes the specified range in the `ArrayString` and yields the removed chars. /// /// Note: The element range is removed even if the iterator is not consumed until the end. /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let mut s = SmallString::try_from_str("ABCD")?; /// assert_eq!(s.drain(..3)?.collect::<Vec<_>>(), vec!['A', 'B', 'C']); /// assert_eq!(s.as_str(), "D"); /// /// assert_eq!(s.drain(3..), Err(Error::Utf8)); /// assert_eq!(s.drain(10..), Err(Error::OutOfBounds)); /// # Ok(()) /// # } /// ``` #[inline] pubfn drain<R>(&mutself, range: R) -> Result<Drain<SIZE>, Error> where
R: RangeBounds<u8>,
{ let start = match range.start_bound() {
Bound::Included(t) => *t,
Bound::Excluded(t) => t.saturating_add(1),
Bound::Unbounded => 0,
}; let end = match range.end_bound() {
Bound::Included(t) => t.saturating_add(1),
Bound::Excluded(t) => *t,
Bound::Unbounded => self.len(),
};
/// Removes the specified range of the `ArrayString`, and replaces it with the given string. The given string doesn't need to have the same length as the range. /// /// ```rust /// # use arraystring::{error::Error, prelude::*}; /// # fn main() -> Result<(), Error> { /// # let _ = env_logger::try_init(); /// let mut s = SmallString::try_from_str("ABCD")?; /// s.replace_range(2..4, "EFGHI")?; /// assert_eq!(s.as_str(), "ABEFGHI"); /// /// assert_eq!(s.replace_range(9.., "J"), Err(Error::Utf8)); /// assert_eq!(s.replace_range(..90, "K"), Err(Error::OutOfBounds)); /// assert_eq!(s.replace_range(0..1, "0".repeat(SmallString::capacity().into())), /// Err(Error::OutOfBounds)); /// # Ok(()) /// # } /// ``` #[inline] pubfn replace_range<S, R>(&mutself, r: R, with: S) -> Result<(), Error> where
S: AsRef<str>,
R: RangeBounds<u8>,
{ let replace_with = with.as_ref(); let start = match r.start_bound() {
Bound::Included(t) => *t,
Bound::Excluded(t) => t.saturating_add(1),
Bound::Unbounded => 0,
}; let end = match r.end_bound() {
Bound::Included(t) => t.saturating_add(1),
Bound::Excluded(t) => *t,
Bound::Unbounded => self.len(),
};
let len = replace_with.len().into_lossy();
debug!( "Replace range (len: {}) ({}..{}) with (len: {}) {}", self.len(),
start,
end,
len,
replace_with
);
is_inside_boundary(start, end)?;
is_inside_boundary(end, self.len())?; let replaced = (end as usize).saturating_sub(start.into());
is_inside_boundary(replaced.saturating_add(len.into()), Self::capacity())?;
is_char_boundary(self, start)?;
is_char_boundary(self, end)?;
debug_assert!(start <= end && end <= self.len());
debug_assert!(len.saturating_sub(end).saturating_add(start) <= Self::capacity());
debug_assert!(self.as_str().is_char_boundary(start.into()));
debug_assert!(self.as_str().is_char_boundary(end.into()));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.