diff options
Diffstat (limited to 'vendor/kstring/src/stack.rs')
-rw-r--r-- | vendor/kstring/src/stack.rs | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/vendor/kstring/src/stack.rs b/vendor/kstring/src/stack.rs new file mode 100644 index 000000000..93e2f0722 --- /dev/null +++ b/vendor/kstring/src/stack.rs @@ -0,0 +1,457 @@ +use std::fmt; + +pub(crate) type Len = u8; + +/// Fixed-size stack-allocated string +#[derive(Copy, Clone)] +pub struct StackString<const CAPACITY: usize> { + len: Len, + buffer: StrBuffer<CAPACITY>, +} + +impl<const CAPACITY: usize> StackString<CAPACITY> { + pub const CAPACITY: usize = CAPACITY; + pub const EMPTY: Self = Self::empty(); + + const fn empty() -> Self { + Self { + len: 0, + buffer: StrBuffer::empty(), + } + } + + /// Create a `StackString` from a `&str`, if it'll fit within `Self::CAPACITY` + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = kstring::StackString::<3>::try_new("foo"); + /// assert_eq!(s.as_deref(), Some("foo")); + /// let s = kstring::StackString::<3>::try_new("foobar"); + /// assert_eq!(s, None); + /// ``` + #[inline] + #[must_use] + pub fn try_new(s: &str) -> Option<Self> { + let len = s.as_bytes().len(); + if len <= Self::CAPACITY { + #[cfg(feature = "unsafe")] + let stack = { + unsafe { + // SAFETY: We've confirmed `len` is within size + Self::new_unchecked(s) + } + }; + #[cfg(not(feature = "unsafe"))] + let stack = { Self::new(s) }; + Some(stack) + } else { + None + } + } + + /// Create a `StackString` from a `&str` + /// + /// # Panic + /// + /// Calling this function with a string larger than `Self::CAPACITY` will panic + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = kstring::StackString::<3>::new("foo"); + /// assert_eq!(s, "foo"); + /// ``` + #[inline] + #[must_use] + pub fn new(s: &str) -> Self { + let len = s.as_bytes().len() as u8; + debug_assert!(Self::CAPACITY <= Len::MAX.into()); + let buffer = StrBuffer::new(s); + Self { len, buffer } + } + + /// Create a `StackString` from a `&str` + /// + /// # Safety + /// + /// Calling this function with a string larger than `Self::CAPACITY` is undefined behavior. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = unsafe { + /// // SAFETY: Literal is short-enough + /// kstring::StackString::<3>::new_unchecked("foo") + /// }; + /// assert_eq!(s, "foo"); + /// ``` + #[inline] + #[must_use] + #[cfg(feature = "unsafe")] + pub unsafe fn new_unchecked(s: &str) -> Self { + let len = s.as_bytes().len() as u8; + debug_assert!(Self::CAPACITY <= Len::MAX.into()); + let buffer = StrBuffer::new_unchecked(s); + Self { len, buffer } + } + + /// Extracts a string slice containing the entire `StackString`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = kstring::StackString::<3>::try_new("foo").unwrap(); + /// + /// assert_eq!("foo", s.as_str()); + /// ``` + #[inline] + #[must_use] + pub fn as_str(&self) -> &str { + let len = self.len as usize; + #[cfg(feature = "unsafe")] + unsafe { + // SAFETY: Constructors guarantee that `buffer[..len]` is a `str`, + // and we don't mutate the data afterwards. + self.buffer.as_str_unchecked(len) + } + #[cfg(not(feature = "unsafe"))] + self.buffer.as_str(len) + } + + /// Converts a `StackString` into a mutable string slice. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = kstring::StackString::<6>::try_new("foobar").unwrap(); + /// let s_mut_str = s.as_mut_str(); + /// + /// s_mut_str.make_ascii_uppercase(); + /// + /// assert_eq!("FOOBAR", s_mut_str); + /// ``` + #[inline] + #[must_use] + pub fn as_mut_str(&mut self) -> &mut str { + let len = self.len as usize; + #[cfg(feature = "unsafe")] + unsafe { + // SAFETY: Constructors guarantee that `buffer[..len]` is a `str`, + // and we don't mutate the data afterwards. + self.buffer.as_mut_str_unchecked(len) + } + #[cfg(not(feature = "unsafe"))] + self.buffer.as_mut_str(len) + } + + /// Returns the length of this `StasckString`, in bytes, not [`char`]s or + /// graphemes. In other words, it might not be what a human considers the + /// length of the string. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let a = kstring::StackString::<3>::try_new("foo").unwrap(); + /// assert_eq!(a.len(), 3); + /// + /// let fancy_f = kstring::StackString::<4>::try_new("ƒoo").unwrap(); + /// assert_eq!(fancy_f.len(), 4); + /// assert_eq!(fancy_f.chars().count(), 3); + /// ``` + #[inline] + #[must_use] + pub fn len(&self) -> usize { + self.len as usize + } + + /// Returns `true` if this `StackString` has a length of zero, and `false` otherwise. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut v = kstring::StackString::<20>::EMPTY; + /// assert!(v.is_empty()); + /// + /// let a = kstring::StackString::<3>::try_new("foo").unwrap(); + /// assert!(!a.is_empty()); + /// ``` + #[inline] + #[must_use] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Truncates this `StackString`, removing all contents. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = kstring::StackString::<3>::try_new("foo").unwrap(); + /// + /// s.clear(); + /// + /// assert!(s.is_empty()); + /// assert_eq!(0, s.len()); + /// ``` + #[inline] + pub fn clear(&mut self) { + self.len = 0; + } + + /// Shortens this `StackString` to the specified length. + /// + /// If `new_len` is greater than the string's current length, this has no + /// effect. + /// + /// Note that this method has no effect on the allocated capacity + /// of the string + /// + /// # Panics + /// + /// Panics if `new_len` does not lie on a [`char`] boundary. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = kstring::StackString::<5>::try_new("hello").unwrap(); + /// + /// s.truncate(2); + /// + /// assert_eq!(s, "he"); + /// ``` + #[inline] + pub fn truncate(&mut self, new_len: usize) { + if new_len <= self.len() { + assert!(self.is_char_boundary(new_len)); + self.len = new_len as u8; + } + } +} + +impl<const CAPACITY: usize> Default for StackString<CAPACITY> { + fn default() -> Self { + Self::empty() + } +} + +impl<const CAPACITY: usize> std::ops::Deref for StackString<CAPACITY> { + type Target = str; + + #[inline] + fn deref(&self) -> &str { + self.as_str() + } +} + +impl<const CAPACITY: usize> Eq for StackString<CAPACITY> {} + +impl<const C1: usize, const C2: usize> PartialEq<StackString<C1>> for StackString<C2> { + #[inline] + fn eq(&self, other: &StackString<C1>) -> bool { + PartialEq::eq(self.as_str(), other.as_str()) + } +} + +impl<const CAPACITY: usize> PartialEq<str> for StackString<CAPACITY> { + #[inline] + fn eq(&self, other: &str) -> bool { + PartialEq::eq(self.as_str(), other) + } +} + +impl<'s, const CAPACITY: usize> PartialEq<&'s str> for StackString<CAPACITY> { + #[inline] + fn eq(&self, other: &&str) -> bool { + PartialEq::eq(self.as_str(), *other) + } +} + +impl<const CAPACITY: usize> PartialEq<String> for StackString<CAPACITY> { + #[inline] + fn eq(&self, other: &String) -> bool { + PartialEq::eq(self.as_str(), other.as_str()) + } +} + +impl<const CAPACITY: usize> Ord for StackString<CAPACITY> { + #[inline] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.as_str().cmp(other.as_str()) + } +} + +impl<const C1: usize, const C2: usize> PartialOrd<StackString<C1>> for StackString<C2> { + #[inline] + fn partial_cmp(&self, other: &StackString<C1>) -> Option<std::cmp::Ordering> { + self.as_str().partial_cmp(other.as_str()) + } +} + +impl<const CAPACITY: usize> PartialOrd<str> for StackString<CAPACITY> { + #[inline] + fn partial_cmp(&self, other: &str) -> Option<std::cmp::Ordering> { + self.as_str().partial_cmp(other) + } +} + +impl<'s, const CAPACITY: usize> PartialOrd<&'s str> for StackString<CAPACITY> { + #[inline] + fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> { + self.as_str().partial_cmp(other) + } +} + +impl<const CAPACITY: usize> PartialOrd<String> for StackString<CAPACITY> { + #[inline] + fn partial_cmp(&self, other: &String) -> Option<std::cmp::Ordering> { + self.as_str().partial_cmp(other.as_str()) + } +} + +impl<const CAPACITY: usize> std::hash::Hash for StackString<CAPACITY> { + #[inline] + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.as_str().hash(state); + } +} + +impl<const CAPACITY: usize> fmt::Debug for StackString<CAPACITY> { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.as_str(), f) + } +} + +impl<const CAPACITY: usize> fmt::Display for StackString<CAPACITY> { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.as_str(), f) + } +} + +impl<const CAPACITY: usize> AsRef<str> for StackString<CAPACITY> { + #[inline] + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl<const CAPACITY: usize> AsRef<[u8]> for StackString<CAPACITY> { + #[inline] + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl<const CAPACITY: usize> AsRef<std::ffi::OsStr> for StackString<CAPACITY> { + #[inline] + fn as_ref(&self) -> &std::ffi::OsStr { + (&**self).as_ref() + } +} + +impl<const CAPACITY: usize> AsRef<std::path::Path> for StackString<CAPACITY> { + #[inline] + fn as_ref(&self) -> &std::path::Path { + std::path::Path::new(self) + } +} + +impl<const CAPACITY: usize> std::borrow::Borrow<str> for StackString<CAPACITY> { + #[inline] + fn borrow(&self) -> &str { + self.as_str() + } +} + +#[derive(Copy, Clone)] +#[repr(transparent)] +pub(crate) struct StrBuffer<const CAPACITY: usize>([u8; CAPACITY]); + +impl<const CAPACITY: usize> StrBuffer<CAPACITY> { + pub(crate) const fn empty() -> Self { + let array = [0; CAPACITY]; + StrBuffer(array) + } + + #[inline] + pub(crate) fn new(s: &str) -> Self { + let len = s.as_bytes().len(); + debug_assert!(len <= CAPACITY); + let mut buffer = Self::default(); + if let Some(buffer) = buffer.0.get_mut(..len) { + buffer.copy_from_slice(s.as_bytes()); + } else { + panic!("`{}` is larger than capacity {}", s, CAPACITY); + } + buffer + } + + #[inline] + #[cfg(not(feature = "unsafe"))] + pub(crate) fn as_str(&self, len: usize) -> &str { + let slice = self.0.get(..len).unwrap(); + std::str::from_utf8(slice).unwrap() + } + + #[inline] + #[cfg(not(feature = "unsafe"))] + pub(crate) fn as_mut_str(&mut self, len: usize) -> &mut str { + let slice = self.0.get_mut(..len).unwrap(); + std::str::from_utf8_mut(slice).unwrap() + } +} + +impl<const CAPACITY: usize> StrBuffer<CAPACITY> { + #[inline] + #[cfg(feature = "unsafe")] + pub(crate) unsafe fn new_unchecked(s: &str) -> Self { + let len = s.as_bytes().len(); + debug_assert!(len <= CAPACITY); + let mut buffer = Self::default(); + buffer + .0 + .get_unchecked_mut(..len) + .copy_from_slice(s.as_bytes()); + buffer + } + + #[inline] + #[cfg(feature = "unsafe")] + pub(crate) unsafe fn as_str_unchecked(&self, len: usize) -> &str { + let slice = self.0.get_unchecked(..len); + std::str::from_utf8_unchecked(slice) + } + + #[inline] + #[cfg(feature = "unsafe")] + pub(crate) unsafe fn as_mut_str_unchecked(&mut self, len: usize) -> &mut str { + let slice = self.0.get_unchecked_mut(..len); + std::str::from_utf8_unchecked_mut(slice) + } +} + +impl<const CAPACITY: usize> Default for StrBuffer<CAPACITY> { + fn default() -> Self { + Self::empty() + } +} |