use std::{ mem::{self, MaybeUninit,}, slice, str, ptr, fmt, error, borrow::Borrow, ops, }; mod ext; use ext::*; /// A `String` that lives entirely on the stack. #[derive(Debug, Clone)] #[cfg_attr(feature="copy", derive(Copy))] #[repr(C)] // Needed for SmallString pub struct StackString{ fill_ptr: usize, buffer: MaybeUninit<[u8; SIZE]>, } /// Error returned when trying to create a `StackString` from a string that is too large to fit in it. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct StrTooLargeError(usize); mod small; pub use small::SmallString; impl StrTooLargeError { /// The invalid string's size #[inline] pub const fn invalid_size(&self) -> usize { self.0 } /// The maximum allowed size #[inline] pub const fn max_size() -> usize { SIZE } } impl error::Error for StrTooLargeError{} impl fmt::Display for StrTooLargeError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "string could not fit into {SIZE} bytes: was {} bytes long", self.0) } } impl StackString { /// Try to create a `StackString` from an exact-sized buffer. #[inline] pub fn try_from_utf8_array(sz: [u8; SIZE]) -> Result { let s = std::str::from_utf8(&sz)?.len(); Ok(Self { fill_ptr: s, buffer: MaybeUninit::new(sz), }) } /// The maximum capacity of this instance #[inline] pub const fn capacity(&self) -> usize { SIZE } /// The length of the string. #[inline] pub const fn len(&self) -> usize { self.fill_ptr } /// The amount of memory left before the instance becomes full. #[inline] pub const fn available(&self) -> usize { SIZE - self.fill_ptr } /// Create a new, empty `StackString` #[inline] pub const fn new() -> Self { Self { fill_ptr: 0, buffer: MaybeUninit::uninit(), } } #[inline(always)] fn buf_end(&mut self) -> *mut u8 { unsafe { (self.buffer.as_mut_ptr() as *mut u8).add(self.fill_ptr) } } #[inline(always)] fn as_raw_buf_mut(&mut self) -> &mut [u8] { unsafe { slice::from_raw_parts_mut(self.buffer.as_mut_ptr() as *mut u8, self.fill_ptr) } } #[inline(always)] fn as_raw_buf(&self) -> &[u8] { unsafe { // MaybeUninit::slice_assume_init_ref(&self.buffer.as_bytes()[..self.fill_ptr]); slice::from_raw_parts(self.buffer.as_ptr() as *const u8, self.fill_ptr) } } /// A mutable reference to the `str` inside #[inline] pub fn as_mut_str(&mut self) -> &mut str { unsafe { std::str::from_utf8_unchecked_mut(self.as_raw_buf_mut()) } } /// A reference to the `str` inside. #[inline] pub fn as_str(&self) -> &str { unsafe { std::str::from_utf8_unchecked(self.as_raw_buf()) } } /// Append as much of `s` into `self` as possible, return the number of bytes appeneded. /// /// This function guarantees: /// * if `s` cannot fit wholely into `self`, the lowest valid UTF-8 codepoint of `s` that will fit into `self` is used instead /// # Returns /// The number of bytes copied from `s`. #[inline(always)] pub fn append_from_str(&mut self, s: &str) -> usize { let sl = s.len(); if self.fill_ptr >= SIZE || sl == 0 { return 0; } let av = self.available(); let sl = if sl <= av { // Can fit whole `str` in unsafe { ptr::copy_nonoverlapping(s.as_bytes().as_ptr(), self.buf_end(), sl); } sl } else if s.is_char_boundary(av) { // Can only fit part in, check if on codepoint boundary unsafe { ptr::copy_nonoverlapping(s.as_bytes().as_ptr(), self.buf_end(), av); } av } else { // Can only fit part in, find the char boundary below `av` and append that. return self.append_from_str(&s[..s.floor_char_boundary_impl(av)]); //TODO: implement floor_char_boundary() ourselves, and we probably don't need this recursive call. }; self.fill_ptr += sl; sl } /// Append as much of `s` into `self` as possible. /// /// This function has the same guarantees as `append_from_str()`. /// /// # Returns /// * `Ok(s)` - if the entire string was appeneded: The part of `self` that now contains `s`. /// * `Err(s)` - if the entire string was **not** appeneded: The part of `s` that was not copied into `self`. The difference between the returned string and the parameter `s` is how much was copied into `self. #[inline] pub fn try_append_from_str<'inner, 'outer>(&'inner mut self, s: &'outer str) -> Result<&'inner mut str, &'outer str> { match self.append_from_str(s) { whole if whole == s.len() => { let substr = self.fill_ptr - whole; Ok(&mut self.as_mut_str()[substr..]) }, copied /*if copied < s.len()*/ => Err(&s[copied..]), } } /// Append as much of `s` into `self` as possible. /// /// This function has the same guarantees as `append_from_str()`. /// /// # Returns /// A tuple containing the copied part of `s`, and the part of `s` that `self` did not have space for. #[inline] pub fn append_from_str_split<'i, 'o>(&'i mut self, s: &'o str) -> (&'i mut str, &'o str) { let end = self.fill_ptr; let written = self.append_from_str(s); if written == s.len() { (&mut self.as_mut_str()[end..], "") } else { (&mut self.as_mut_str()[end..written], &s[written..]) } } /// Attempt to append the whole string `s` into `self`. /// /// # Returns /// * `Some(substr)` - If all of `s` fits in to `self`: the substring of `self` that now contains `s`. /// * `None` - If `s` was too large to fit entirely in to `self` #[inline] pub fn try_append_whole_str<'i>(&'i mut self, s: &str) -> Result<&'i mut str, StrTooLargeError> { let av = self.available(); if s.len() <= av { let len = self.append_from_str(s); debug_assert_eq!(len, s.len(), "Bad append"); let _ = len; Ok(&mut self.as_mut_str()[av..]) } else { Err(StrTooLargeError(s.len())) } } } impl Borrow for StackString { #[inline] fn borrow(&self) -> &str { self.as_str() } } impl ops::Deref for StackString { type Target = str; #[inline] fn deref(&self) -> &Self::Target { self.as_str() } } impl std::str::FromStr for StackString { type Err = StrTooLargeError; #[inline] fn from_str(s: &str) -> Result { if s.len() > SIZE { Err(StrTooLargeError(s.len())) } else { let mut o = Self::new(); o.try_append_whole_str(s)?; Ok(o) } } } impl TryFrom for StackString { type Error = StrTooLargeError; #[inline] fn try_from(from: String) -> Result { from.parse() } } impl<'a, const SIZE: usize> TryFrom<&'a str> for StackString { type Error = StrTooLargeError; #[inline] fn try_from(from: &'a str) -> Result { from.parse() } } impl TryFrom<[u8; SIZE]> for StackString { type Error = std::str::Utf8Error; #[inline] fn try_from(from: [u8; SIZE]) -> Result { Self::try_from_utf8_array(from) } } impl fmt::Display for StackString { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.as_str()) } } impl fmt::Write for StackString { #[inline] fn write_str(&mut self, s: &str) -> fmt::Result { self.try_append_whole_str(s).map_err(|_| fmt::Error::default())?; Ok(()) } #[inline] fn write_char(&mut self, c: char) -> fmt::Result { let l = c.len_utf8(); if l > self.available() { Err(fmt::Error::default()) } else { let end = self.fill_ptr; let end = c.encode_utf8(&mut (self.as_raw_buf_mut())[end..]).len(); self.fill_ptr += end; Ok(()) } } } const _: () = { use std::io::{ self,Write }; impl Write for StackString { #[inline] fn write(&mut self, buf: &[u8]) -> std::io::Result { let buf = std::str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; Ok(self.append_from_str(buf)) } #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } #[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { let buf = std::str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; self.try_append_whole_str(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)).map(|_| ()) } } };