From c40b415a06180a2e87ecb92e5fbfb6ddc1511be9 Mon Sep 17 00:00:00 2001 From: Avril Date: Fri, 15 Apr 2022 22:58:11 +0100 Subject: [PATCH] Made work on stable: Added `.floor_char_boundary()` impl. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fortune for stack-str's current commit: Middle blessing − 中吉 --- src/ext.rs | 35 ++++++++++++++++++++++++++++++++ src/lib.rs | 59 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 75 insertions(+), 19 deletions(-) create mode 100644 src/ext.rs diff --git a/src/ext.rs b/src/ext.rs new file mode 100644 index 0000000..d3fbceb --- /dev/null +++ b/src/ext.rs @@ -0,0 +1,35 @@ +//! Extensions + +#[inline(always)] +pub fn is_utf8_char_boundary(byte: u8) -> bool +{ + //! + //! Copied from stdlib + (byte as i8) >= -0x40 +} + +pub trait StrExt +{ + fn floor_char_boundary_impl(&self, index: usize) -> usize; +} + +impl> StrExt for T +{ + #[inline] + fn floor_char_boundary_impl(&self, index: usize) -> usize { + //! + //! copied from stdlib + let this = self.as_ref(); + if index >= this.len() { + this.len() + } else { + let lower_bound = index.saturating_sub(3); + let new_index = this.as_bytes()[lower_bound..=index] + .iter() + .rposition(|&b| is_utf8_char_boundary(b)); + + // SAFETY: we know that the character boundary will be within four bytes + unsafe { lower_bound + new_index.unwrap_unchecked() } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 7831868..5b495d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(round_char_boundary)] - use std::{ mem::{self, MaybeUninit,}, slice, @@ -12,7 +10,9 @@ use std::{ ops, }; -/// A `str` that lives entirely on the stack. +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 @@ -20,9 +20,29 @@ pub struct StackString{ fill_ptr: usize, buffer: MaybeUninit<[u8; SIZE]>, } -#[derive(Debug)] +/// 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 { @@ -34,12 +54,9 @@ impl fmt::Display for StrTooLargeError } -mod small; -pub use small::SmallString; - -//TODO: Document impl StackString { + /// Try to create a `StackString` from an exact-sized buffer. #[inline] pub fn try_from_utf8_array(sz: [u8; SIZE]) -> Result { @@ -49,23 +66,26 @@ impl StackString 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 { @@ -94,10 +114,12 @@ impl StackString 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 { @@ -105,6 +127,7 @@ impl StackString std::str::from_utf8_unchecked_mut(self.as_raw_buf_mut()) } } + /// A reference to the `str` inside. #[inline] pub fn as_str(&self) -> &str { @@ -143,7 +166,7 @@ impl StackString 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(av)]); //TODO: implement floor_char_boundary() ourselves, and we probably don't need this recursive call. + 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 @@ -192,7 +215,7 @@ impl StackString /// * `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) -> Option<&'i mut str> + 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 { @@ -200,9 +223,9 @@ impl StackString debug_assert_eq!(len, s.len(), "Bad append"); let _ = len; - Some(&mut self.as_mut_str()[av..]) + Ok(&mut self.as_mut_str()[av..]) } else { - None + Err(StrTooLargeError(s.len())) } } } @@ -233,11 +256,9 @@ impl std::str::FromStr for StackString Err(StrTooLargeError(s.len())) } else { let mut o = Self::new(); - if o.try_append_whole_str(s).is_none() { - Err(StrTooLargeError(s.len())) - } else { - Ok(o) - } + o.try_append_whole_str(s)?; + Ok(o) + } } }