From 93a8cefd068f24d254fae90a557edc6d49484f51 Mon Sep 17 00:00:00 2001 From: Pantonshire Date: Tue, 12 Jul 2022 14:46:01 +0100 Subject: [PATCH] ShString now uses the StackString from the public API --- src/strings/shstring.rs | 255 ++++++++-------------------------------- 1 file changed, 49 insertions(+), 206 deletions(-) diff --git a/src/strings/shstring.rs b/src/strings/shstring.rs index 278429d..7d3097e 100644 --- a/src/strings/shstring.rs +++ b/src/strings/shstring.rs @@ -8,7 +8,7 @@ use std::{ str::FromStr, }; -use buf::{StackString, HeapString}; +use super::StackString; /// A non-growable string where strings 22 bytes or shorter are stored on the stack and longer /// strings are stored on the heap. @@ -42,31 +42,14 @@ impl ShString { /// or creating a new heap allocation otherwise. #[inline] #[must_use] - pub fn new_from_str(s: &str) -> Self { - match StackString::from_str(s) { - Some(stack_buf) => Self(Repr::Stack(stack_buf)), - None => Self(Repr::Heap(HeapString::from_str(s))), - } - } - - /// Creates a new `ShString` from the given owned `String`, moving the string data onto the - /// stack if possible or reusing the `String`'s heap allocation otherwise. - #[inline] - #[must_use] - pub fn new_from_string(s: String) -> Self { - match StackString::from_str(&s) { - Some(stack_buf) => Self(Repr::Stack(stack_buf)), - None => Self(Repr::Heap(HeapString::from_string(s))), - } - } - - /// Creates a new `ShString` from the given `Cow`. - #[inline] - #[must_use] - pub fn new_from_cow_str(s: Cow) -> Self { - match s { - Cow::Borrowed(s) => Self::new_from_str(s), - Cow::Owned(s) => Self::new_from_string(s), + pub fn new(s: S) -> Self + where + S: AsRef, + Box: From, + { + match StackString::new(&s) { + Ok(stack_buf) => Self(Repr::Stack(stack_buf)), + Err(_) => Self(Repr::Heap(Box::::from(s))), } } @@ -75,8 +58,8 @@ impl ShString { #[must_use] pub fn as_str(&self) -> &str { match self { - Self(Repr::Stack(buf)) => buf.as_str(), - Self(Repr::Heap(buf)) => buf.as_str(), + Self(Repr::Stack(buf)) => buf, + Self(Repr::Heap(buf)) => buf, } } @@ -85,8 +68,8 @@ impl ShString { #[must_use] pub fn as_str_mut(&mut self) -> &mut str { match self { - Self(Repr::Stack(buf)) => buf.as_str_mut(), - Self(Repr::Heap(buf)) => buf.as_str_mut(), + Self(Repr::Stack(buf)) => buf, + Self(Repr::Heap(buf)) => buf, } } @@ -104,7 +87,7 @@ impl ShString { /// /// ``` /// # use libshire::strings::ShString; - /// let s = ShString::<22>::new_from_str("こんにちは"); + /// let s = ShString::<22>::new("こんにちは"); /// assert_eq!(s.len(), 15); /// ``` #[inline] @@ -120,10 +103,10 @@ impl ShString { /// /// ``` /// # use libshire::strings::ShString; - /// let s1 = ShString::<22>::new_from_str(""); + /// let s1 = ShString::<22>::new(""); /// assert!(s1.is_empty()); /// - /// let s2 = ShString::<22>::new_from_str("Hello"); + /// let s2 = ShString::<22>::new("Hello"); /// assert!(!s2.is_empty()); /// ``` #[inline] @@ -139,10 +122,10 @@ impl ShString { /// /// ``` /// # use libshire::strings::ShString; - /// let s1 = ShString::<22>::new_from_str("This string's 22 bytes"); + /// let s1 = ShString::<22>::new("This string's 22 bytes"); /// assert!(!s1.heap_allocated()); /// - /// let s2 = ShString::<22>::new_from_str("This string is 23 bytes"); + /// let s2 = ShString::<22>::new("This string is 23 bytes"); /// assert!(s2.heap_allocated()); /// ``` #[inline] @@ -209,21 +192,21 @@ impl borrow::BorrowMut for ShString { impl<'a, const N: usize> From<&'a str> for ShString { #[inline] fn from(s: &'a str) -> Self { - Self::new_from_str(s) + Self::new(s) } } impl From for ShString { #[inline] fn from(s: String) -> Self { - Self::new_from_string(s) + Self::new(s) } } impl<'a, const N: usize> From> for ShString { #[inline] fn from(s: Cow<'a, str>) -> Self { - Self::new_from_cow_str(s) + Self::new(s) } } @@ -269,7 +252,7 @@ impl FromStr for ShString { #[inline] fn from_str(s: &str) -> Result { - Ok(Self::new_from_str(s)) + Ok(Self::new(s)) } } @@ -304,154 +287,14 @@ impl<'de, const N: usize> serde::Deserialize<'de> for ShString { D: serde::Deserializer<'de> { serde::Deserialize::deserialize(deserializer) - .map(Self::new_from_str) + .map(Self::new::<&'de str>) } } #[derive(Clone)] enum Repr { Stack(StackString), - Heap(HeapString), -} - -mod buf { - use std::str; - - /// A stack-allocated string with a capacity of `N` bytes. `len` must be less than or equal to - /// `N`, and the first `len` bytes of `buf` must be valid UTF-8. - #[derive(Clone)] - pub(super) struct StackString { - buf: [u8; N], - len: u8, - } - - impl StackString { - const MAX_LEN: u8 = { - #[allow(clippy::cast_possible_truncation, clippy::checked_conversions)] - if N <= u8::MAX as usize { - N as u8 - } else { - panic!("`N` must be within the bounds of `u8`") - } - }; - - /// Creates a new `StackString` from a given buffer and length. - /// - /// # Safety - /// - /// The first `len` bytes of `buf` (i.e. `buf[..len]`) must be valid UTF-8. - #[inline] - pub(super) const unsafe fn from_raw_parts(buf: [u8; N], len: u8) -> Self { - Self { buf, len } - } - - #[inline] - pub(super) const fn empty() -> Self { - // SAFETY: - // The first zero bytes of the buffer are valid UTF-8, because an empty byte slice is - // valid UTF-8. - unsafe { Self::from_raw_parts([0; N], 0) } - } - - pub(super) fn from_str(s: &str) -> Option { - let s = s.as_bytes(); - - // If the length of the string is greater than `Self::MAX_LEN`, it will not fit in the - // stack buffer so return `None`. - let len = u8::try_from(s.len()).ok()?; - if len > Self::MAX_LEN { - return None; - } - - let mut buf = [0; N]; - buf[..usize::from(len)].copy_from_slice(s); - - // SAFETY: - // The first `len` bytes of the buffer are valid UTF-8 because the first `len` bytes of - // the buffer contain data copied from a `&str`, and `&str` is always valid UTF-8. - unsafe { Some(Self::from_raw_parts(buf, len)) } - } - - pub(super) fn as_str(&self) -> &str { - // SAFETY: - // `len` being less than or equal to `N` is an invariant of `StackString`, so it is - // always within the bounds of `buf`. - let slice = unsafe { self.buf.get_unchecked(..usize::from(self.len)) }; - - // SAFETY: - // The first `len` bytes of `buf` being valid UTF-8 is an invariant of `StackString`. - unsafe { str::from_utf8_unchecked(slice) } - } - - pub(super) fn as_str_mut(&mut self) -> &mut str { - // SAFETY: - // `len` being less than or equal to `N` is an invariant of `StackString`, so it is - // always within the bounds of `buf`. - let slice = unsafe { self.buf.get_unchecked_mut(..usize::from(self.len)) }; - - // SAFETY: - // The first `len` bytes of `buf` being valid UTF-8 is an invariant of `StackString`. - unsafe { str::from_utf8_unchecked_mut(slice) } - } - - pub(super) fn into_string(self) -> String { - self.as_str().to_owned() - } - - pub(super) fn len(&self) -> usize { - usize::from(self.len) - } - - pub(super) fn is_empty(&self) -> bool { - self.len == 0 - } - } - - /// A heap-allocated non-growable string. `buf` must be valid UTF-8. - #[derive(Clone)] - pub(super) struct HeapString { - buf: Box<[u8]>, - } - - impl HeapString { - pub(super) fn from_str(s: &str) -> Self { - Self { - buf: s.as_bytes().into(), - } - } - - pub(super) fn from_string(s: String) -> Self { - Self { - buf: s.into_boxed_str().into_boxed_bytes(), - } - } - - pub(super) fn as_str(&self) -> &str { - // SAFETY: - // `buf` being valid UTF-8 is an invariant of `HeapString`. - unsafe { str::from_utf8_unchecked(&self.buf) } - } - - pub(super) fn as_str_mut(&mut self) -> &mut str { - // SAFETY: - // `buf` being valid UTF-8 is an invariant of `HeapString`. - unsafe { str::from_utf8_unchecked_mut(&mut self.buf) } - } - - pub(super) fn into_string(self) -> String { - // SAFETY: - // `buf` being valid UTF-8 is an invariant of `HeapString`. - unsafe { String::from_utf8_unchecked(self.buf.into_vec()) } - } - - pub(super) fn len(&self) -> usize { - self.buf.len() - } - - pub(super) fn is_empty(&self) -> bool { - self.buf.is_empty() - } - } + Heap(Box), } #[cfg(test)] @@ -476,53 +319,53 @@ mod tests { let borrowed = Cow::Borrowed(s); let owned = Cow::<'static, str>::Owned(buf.clone()); - assert_eq!(ShString22::new_from_str(s).as_str(), s); - assert_eq!(ShString22::new_from_string(buf).as_str(), s); - assert_eq!(ShString22::new_from_cow_str(borrowed).as_str(), s); - assert_eq!(ShString22::new_from_cow_str(owned).as_str(), s); + assert_eq!(ShString22::new(s).as_str(), s); + assert_eq!(ShString22::new(buf).as_str(), s); + assert_eq!(ShString22::new(borrowed).as_str(), s); + assert_eq!(ShString22::new(owned).as_str(), s); } } #[test] fn test_as_str_mut() { - let mut s1 = ShString22::new_from_str("hello"); + let mut s1 = ShString22::new("hello"); s1.as_str_mut().make_ascii_uppercase(); assert_eq!(s1.as_str(), "HELLO"); - let mut s2 = ShString22::new_from_str("the quick brown fox jumps over the lazy dog"); + let mut s2 = ShString22::new("the quick brown fox jumps over the lazy dog"); s2.as_str_mut().make_ascii_uppercase(); assert_eq!(s2.as_str(), "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG"); } #[test] fn test_len() { - assert_eq!(ShString22::new_from_str("").len(), 0); - assert_eq!(ShString22::new_from_str("Hello").len(), 5); - assert_eq!(ShString22::new_from_str("Somethingfortheweekend").len(), 22); - assert_eq!(ShString22::new_from_str("Dichlorodifluoromethane").len(), 23); - assert_eq!(ShString22::new_from_str("こんにちは").len(), 15); - assert_eq!(ShString22::new_from_str("❤️🧡💛💚💙💜").len(), 26); + assert_eq!(ShString22::new("").len(), 0); + assert_eq!(ShString22::new("Hello").len(), 5); + assert_eq!(ShString22::new("Somethingfortheweekend").len(), 22); + assert_eq!(ShString22::new("Dichlorodifluoromethane").len(), 23); + assert_eq!(ShString22::new("こんにちは").len(), 15); + assert_eq!(ShString22::new("❤️🧡💛💚💙💜").len(), 26); } #[test] fn test_heap_allocated() { - assert!(!ShString22::new_from_str("").heap_allocated()); - assert!(!ShString22::new_from_str("Hello").heap_allocated()); - assert!(!ShString22::new_from_str("Somethingfortheweekend").heap_allocated()); - assert!(!ShString22::new_from_str("こんにちは").heap_allocated()); + assert!(!ShString22::new("").heap_allocated()); + assert!(!ShString22::new("Hello").heap_allocated()); + assert!(!ShString22::new("Somethingfortheweekend").heap_allocated()); + assert!(!ShString22::new("こんにちは").heap_allocated()); - assert!(ShString22::new_from_str("Dichlorodifluoromethane").heap_allocated()); - assert!(ShString22::new_from_str("Squishedbuginsidethescreen").heap_allocated()); - assert!(ShString22::new_from_str("❤️🧡💛💚💙💜").heap_allocated()); + assert!(ShString22::new("Dichlorodifluoromethane").heap_allocated()); + assert!(ShString22::new("Squishedbuginsidethescreen").heap_allocated()); + assert!(ShString22::new("❤️🧡💛💚💙💜").heap_allocated()); } #[test] fn test_zero_capacity() { - assert_eq!(ShString::<0>::new_from_str("").as_str(), ""); - assert!(!ShString::<0>::new_from_str("").heap_allocated()); - assert_eq!(ShString::<0>::new_from_str("a").as_str(), "a"); - assert!(ShString::<0>::new_from_str("a").heap_allocated()); - assert_eq!(ShString::<0>::new_from_str("Hello").as_str(), "Hello"); - assert!(ShString::<0>::new_from_str("Hello").heap_allocated()); + assert_eq!(ShString::<0>::new("").as_str(), ""); + assert!(!ShString::<0>::new("").heap_allocated()); + assert_eq!(ShString::<0>::new("a").as_str(), "a"); + assert!(ShString::<0>::new("a").heap_allocated()); + assert_eq!(ShString::<0>::new("Hello").as_str(), "Hello"); + assert!(ShString::<0>::new("Hello").heap_allocated()); } }