summaryrefslogtreecommitdiffstats
path: root/vendor/tinystr/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:21 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:21 +0000
commit4e8199b572f2035b7749cba276ece3a26630d23e (patch)
treef09feeed6a0fe39d027b1908aa63ea6b35e4b631 /vendor/tinystr/src
parentAdding upstream version 1.66.0+dfsg1. (diff)
downloadrustc-4e8199b572f2035b7749cba276ece3a26630d23e.tar.xz
rustc-4e8199b572f2035b7749cba276ece3a26630d23e.zip
Adding upstream version 1.67.1+dfsg1.upstream/1.67.1+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/tinystr/src')
-rw-r--r--vendor/tinystr/src/ascii.rs987
-rw-r--r--vendor/tinystr/src/asciibyte.rs145
-rw-r--r--vendor/tinystr/src/databake.rs21
-rw-r--r--vendor/tinystr/src/error.rs16
-rw-r--r--vendor/tinystr/src/helpers.rs32
-rw-r--r--vendor/tinystr/src/int_ops.rs315
-rw-r--r--vendor/tinystr/src/lib.rs159
-rw-r--r--vendor/tinystr/src/macros.rs32
-rw-r--r--vendor/tinystr/src/serde.rs91
-rw-r--r--vendor/tinystr/src/tinystr16.rs327
-rw-r--r--vendor/tinystr/src/tinystr4.rs299
-rw-r--r--vendor/tinystr/src/tinystr8.rs319
-rw-r--r--vendor/tinystr/src/tinystrauto.rs72
-rw-r--r--vendor/tinystr/src/ule.rs76
14 files changed, 1768 insertions, 1123 deletions
diff --git a/vendor/tinystr/src/ascii.rs b/vendor/tinystr/src/ascii.rs
new file mode 100644
index 000000000..0be1125e3
--- /dev/null
+++ b/vendor/tinystr/src/ascii.rs
@@ -0,0 +1,987 @@
+// This file is part of ICU4X. For terms of use, please see the file
+// called LICENSE at the top level of the ICU4X source tree
+// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
+
+use crate::asciibyte::AsciiByte;
+use crate::int_ops::{Aligned4, Aligned8};
+use crate::TinyStrError;
+use core::fmt;
+use core::ops::Deref;
+use core::str::{self, FromStr};
+
+#[repr(transparent)]
+#[derive(PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Hash)]
+pub struct TinyAsciiStr<const N: usize> {
+ bytes: [AsciiByte; N],
+}
+
+impl<const N: usize> TinyAsciiStr<N> {
+ /// Creates a `TinyAsciiStr<N>` from the given byte slice.
+ /// `bytes` may contain at most `N` non-null ASCII bytes.
+ pub const fn from_bytes(bytes: &[u8]) -> Result<Self, TinyStrError> {
+ Self::from_bytes_inner(bytes, 0, bytes.len(), false)
+ }
+
+ /// Attempts to parse a fixed-length byte array to a `TinyAsciiStr`.
+ ///
+ /// The byte array may contain trailing NUL bytes.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tinystr::tinystr;
+ /// use tinystr::TinyAsciiStr;
+ ///
+ /// assert_eq!(
+ /// TinyAsciiStr::<3>::try_from_raw(*b"GB\0"),
+ /// Ok(tinystr!(3, "GB"))
+ /// );
+ /// assert_eq!(
+ /// TinyAsciiStr::<3>::try_from_raw(*b"USD"),
+ /// Ok(tinystr!(3, "USD"))
+ /// );
+ /// assert!(matches!(TinyAsciiStr::<3>::try_from_raw(*b"\0A\0"), Err(_)));
+ /// ```
+ pub const fn try_from_raw(raw: [u8; N]) -> Result<Self, TinyStrError> {
+ Self::from_bytes_inner(&raw, 0, N, true)
+ }
+
+ /// Equivalent to [`from_bytes(bytes[start..end])`](Self::from_bytes),
+ /// but callable in a `const` context (which range indexing is not).
+ pub const fn from_bytes_manual_slice(
+ bytes: &[u8],
+ start: usize,
+ end: usize,
+ ) -> Result<Self, TinyStrError> {
+ Self::from_bytes_inner(bytes, start, end, false)
+ }
+
+ #[inline]
+ pub(crate) const fn from_bytes_inner(
+ bytes: &[u8],
+ start: usize,
+ end: usize,
+ allow_trailing_null: bool,
+ ) -> Result<Self, TinyStrError> {
+ let len = end - start;
+ if len > N {
+ return Err(TinyStrError::TooLarge { max: N, len });
+ }
+
+ let mut out = [0; N];
+ let mut i = 0;
+ let mut found_null = false;
+ // Indexing is protected by TinyStrError::TooLarge
+ #[allow(clippy::indexing_slicing)]
+ while i < len {
+ let b = bytes[start + i];
+
+ if b == 0 {
+ found_null = true;
+ } else if b >= 0x80 {
+ return Err(TinyStrError::NonAscii);
+ } else if found_null {
+ // Error if there are contentful bytes after null
+ return Err(TinyStrError::ContainsNull);
+ }
+ out[i] = b;
+
+ i += 1;
+ }
+
+ if !allow_trailing_null && found_null {
+ // We found some trailing nulls, error
+ return Err(TinyStrError::ContainsNull);
+ }
+
+ Ok(Self {
+ // SAFETY: `out` only contains ASCII bytes and has same size as `self.bytes`
+ bytes: unsafe { AsciiByte::to_ascii_byte_array(&out) },
+ })
+ }
+
+ // TODO: This function shadows the FromStr trait. Rename?
+ #[inline]
+ pub const fn from_str(s: &str) -> Result<Self, TinyStrError> {
+ Self::from_bytes_inner(s.as_bytes(), 0, s.len(), false)
+ }
+
+ #[inline]
+ pub const fn as_str(&self) -> &str {
+ // as_bytes is valid utf8
+ unsafe { str::from_utf8_unchecked(self.as_bytes()) }
+ }
+
+ #[inline]
+ #[must_use]
+ pub const fn len(&self) -> usize {
+ if N <= 4 {
+ Aligned4::from_ascii_bytes(&self.bytes).len()
+ } else if N <= 8 {
+ Aligned8::from_ascii_bytes(&self.bytes).len()
+ } else {
+ let mut i = 0;
+ #[allow(clippy::indexing_slicing)] // < N is safe
+ while i < N && self.bytes[i] as u8 != AsciiByte::B0 as u8 {
+ i += 1
+ }
+ i
+ }
+ }
+
+ #[inline]
+ #[must_use]
+ pub const fn is_empty(&self) -> bool {
+ self.bytes[0] as u8 == AsciiByte::B0 as u8
+ }
+
+ #[inline]
+ #[must_use]
+ pub const fn as_bytes(&self) -> &[u8] {
+ /// core::slice::from_raw_parts(a, b) = core::mem::transmute((a, b)) hack
+ /// ```compile_fail
+ /// const unsafe fn canary() { core::slice::from_raw_parts(0 as *const u8, 0); }
+ /// ```
+ const _: () = ();
+ // Safe because `self.bytes.as_slice()` pointer-casts to `&[u8]`,
+ // and changing the length of that slice to self.len() < N is safe.
+ unsafe { core::mem::transmute((self.bytes.as_slice().as_ptr(), self.len())) }
+ }
+
+ #[inline]
+ #[must_use]
+ pub const fn all_bytes(&self) -> &[u8; N] {
+ // SAFETY: `self.bytes` has same size as [u8; N]
+ unsafe { core::mem::transmute(&self.bytes) }
+ }
+
+ #[inline]
+ #[must_use]
+ /// Resizes a TinyAsciiStr<N> to a TinyAsciiStr<M>.
+ ///
+ /// If M < len() the string gets truncated, otherwise only the
+ /// memory representation changes.
+ pub const fn resize<const M: usize>(self) -> TinyAsciiStr<M> {
+ let mut bytes = [0; M];
+ let mut i = 0;
+ // Indexing is protected by the loop guard
+ #[allow(clippy::indexing_slicing)]
+ while i < M && i < N {
+ bytes[i] = self.bytes[i] as u8;
+ i += 1;
+ }
+ // `self.bytes` only contains ASCII bytes, with no null bytes between
+ // ASCII characters, so this also holds for `bytes`.
+ unsafe { TinyAsciiStr::from_bytes_unchecked(bytes) }
+ }
+
+ /// # Safety
+ /// Must be called with a bytes array made of valid ASCII bytes, with no null bytes
+ /// between ASCII characters
+ #[must_use]
+ pub const unsafe fn from_bytes_unchecked(bytes: [u8; N]) -> Self {
+ Self {
+ bytes: AsciiByte::to_ascii_byte_array(&bytes),
+ }
+ }
+}
+
+macro_rules! check_is {
+ ($self:ident, $check_int:ident, $check_u8:ident) => {
+ if N <= 4 {
+ Aligned4::from_ascii_bytes(&$self.bytes).$check_int()
+ } else if N <= 8 {
+ Aligned8::from_ascii_bytes(&$self.bytes).$check_int()
+ } else {
+ let mut i = 0;
+ // Won't panic because self.bytes has length N
+ #[allow(clippy::indexing_slicing)]
+ while i < N && $self.bytes[i] as u8 != AsciiByte::B0 as u8 {
+ if !($self.bytes[i] as u8).$check_u8() {
+ return false;
+ }
+ i += 1;
+ }
+ true
+ }
+ };
+ ($self:ident, $check_int:ident, !$check_u8_0_inv:ident, !$check_u8_1_inv:ident) => {
+ if N <= 4 {
+ Aligned4::from_ascii_bytes(&$self.bytes).$check_int()
+ } else if N <= 8 {
+ Aligned8::from_ascii_bytes(&$self.bytes).$check_int()
+ } else {
+ // Won't panic because N is > 8
+ if ($self.bytes[0] as u8).$check_u8_0_inv() {
+ return false;
+ }
+ let mut i = 1;
+ // Won't panic because self.bytes has length N
+ #[allow(clippy::indexing_slicing)]
+ while i < N && $self.bytes[i] as u8 != AsciiByte::B0 as u8 {
+ if ($self.bytes[i] as u8).$check_u8_1_inv() {
+ return false;
+ }
+ i += 1;
+ }
+ true
+ }
+ };
+ ($self:ident, $check_int:ident, $check_u8_0_inv:ident, $check_u8_1_inv:ident) => {
+ if N <= 4 {
+ Aligned4::from_ascii_bytes(&$self.bytes).$check_int()
+ } else if N <= 8 {
+ Aligned8::from_ascii_bytes(&$self.bytes).$check_int()
+ } else {
+ // Won't panic because N is > 8
+ if !($self.bytes[0] as u8).$check_u8_0_inv() {
+ return false;
+ }
+ let mut i = 1;
+ // Won't panic because self.bytes has length N
+ #[allow(clippy::indexing_slicing)]
+ while i < N && $self.bytes[i] as u8 != AsciiByte::B0 as u8 {
+ if !($self.bytes[i] as u8).$check_u8_1_inv() {
+ return false;
+ }
+ i += 1;
+ }
+ true
+ }
+ };
+}
+
+impl<const N: usize> TinyAsciiStr<N> {
+ /// Checks if the value is composed of ASCII alphabetic characters:
+ ///
+ /// * U+0041 'A' ..= U+005A 'Z', or
+ /// * U+0061 'a' ..= U+007A 'z'.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tinystr::TinyAsciiStr;
+ ///
+ /// let s1: TinyAsciiStr<4> = "Test".parse().expect("Failed to parse.");
+ /// let s2: TinyAsciiStr<4> = "Te3t".parse().expect("Failed to parse.");
+ ///
+ /// assert!(s1.is_ascii_alphabetic());
+ /// assert!(!s2.is_ascii_alphabetic());
+ /// ```
+ #[inline]
+ #[must_use]
+ pub const fn is_ascii_alphabetic(&self) -> bool {
+ check_is!(self, is_ascii_alphabetic, is_ascii_alphabetic)
+ }
+
+ /// Checks if the value is composed of ASCII alphanumeric characters:
+ ///
+ /// * U+0041 'A' ..= U+005A 'Z', or
+ /// * U+0061 'a' ..= U+007A 'z', or
+ /// * U+0030 '0' ..= U+0039 '9'.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tinystr::TinyAsciiStr;
+ ///
+ /// let s1: TinyAsciiStr<4> = "A15b".parse().expect("Failed to parse.");
+ /// let s2: TinyAsciiStr<4> = "[3@w".parse().expect("Failed to parse.");
+ ///
+ /// assert!(s1.is_ascii_alphanumeric());
+ /// assert!(!s2.is_ascii_alphanumeric());
+ /// ```
+ #[inline]
+ #[must_use]
+ pub const fn is_ascii_alphanumeric(&self) -> bool {
+ check_is!(self, is_ascii_alphanumeric, is_ascii_alphanumeric)
+ }
+
+ /// Checks if the value is composed of ASCII decimal digits:
+ ///
+ /// * U+0030 '0' ..= U+0039 '9'.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tinystr::TinyAsciiStr;
+ ///
+ /// let s1: TinyAsciiStr<4> = "312".parse().expect("Failed to parse.");
+ /// let s2: TinyAsciiStr<4> = "3d".parse().expect("Failed to parse.");
+ ///
+ /// assert!(s1.is_ascii_numeric());
+ /// assert!(!s2.is_ascii_numeric());
+ /// ```
+ #[inline]
+ #[must_use]
+ pub const fn is_ascii_numeric(&self) -> bool {
+ check_is!(self, is_ascii_numeric, is_ascii_digit)
+ }
+
+ /// Checks if the value is in ASCII lower case.
+ ///
+ /// All letter characters are checked for case. Non-letter characters are ignored.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tinystr::TinyAsciiStr;
+ ///
+ /// let s1: TinyAsciiStr<4> = "teSt".parse().expect("Failed to parse.");
+ /// let s2: TinyAsciiStr<4> = "test".parse().expect("Failed to parse.");
+ /// let s3: TinyAsciiStr<4> = "001z".parse().expect("Failed to parse.");
+ ///
+ /// assert!(!s1.is_ascii_lowercase());
+ /// assert!(s2.is_ascii_lowercase());
+ /// assert!(s3.is_ascii_lowercase());
+ /// ```
+ #[inline]
+ #[must_use]
+ pub const fn is_ascii_lowercase(&self) -> bool {
+ check_is!(
+ self,
+ is_ascii_lowercase,
+ !is_ascii_uppercase,
+ !is_ascii_uppercase
+ )
+ }
+
+ /// Checks if the value is in ASCII title case.
+ ///
+ /// This verifies that the first character is ASCII uppercase and all others ASCII lowercase.
+ /// Non-letter characters are ignored.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tinystr::TinyAsciiStr;
+ ///
+ /// let s1: TinyAsciiStr<4> = "teSt".parse().expect("Failed to parse.");
+ /// let s2: TinyAsciiStr<4> = "Test".parse().expect("Failed to parse.");
+ /// let s3: TinyAsciiStr<4> = "001z".parse().expect("Failed to parse.");
+ ///
+ /// assert!(!s1.is_ascii_titlecase());
+ /// assert!(s2.is_ascii_titlecase());
+ /// assert!(s3.is_ascii_titlecase());
+ /// ```
+ #[inline]
+ #[must_use]
+ pub const fn is_ascii_titlecase(&self) -> bool {
+ check_is!(
+ self,
+ is_ascii_titlecase,
+ !is_ascii_lowercase,
+ !is_ascii_uppercase
+ )
+ }
+
+ /// Checks if the value is in ASCII upper case.
+ ///
+ /// All letter characters are checked for case. Non-letter characters are ignored.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tinystr::TinyAsciiStr;
+ ///
+ /// let s1: TinyAsciiStr<4> = "teSt".parse().expect("Failed to parse.");
+ /// let s2: TinyAsciiStr<4> = "TEST".parse().expect("Failed to parse.");
+ /// let s3: TinyAsciiStr<4> = "001z".parse().expect("Failed to parse.");
+ ///
+ /// assert!(!s1.is_ascii_uppercase());
+ /// assert!(s2.is_ascii_uppercase());
+ /// assert!(!s3.is_ascii_uppercase());
+ /// ```
+ #[inline]
+ #[must_use]
+ pub const fn is_ascii_uppercase(&self) -> bool {
+ check_is!(
+ self,
+ is_ascii_uppercase,
+ !is_ascii_lowercase,
+ !is_ascii_lowercase
+ )
+ }
+
+ /// Checks if the value is composed of ASCII alphabetic lower case characters:
+ ///
+ /// * U+0061 'a' ..= U+007A 'z',
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tinystr::TinyAsciiStr;
+ ///
+ /// let s1: TinyAsciiStr<4> = "Test".parse().expect("Failed to parse.");
+ /// let s2: TinyAsciiStr<4> = "Te3t".parse().expect("Failed to parse.");
+ /// let s3: TinyAsciiStr<4> = "teSt".parse().expect("Failed to parse.");
+ /// let s4: TinyAsciiStr<4> = "test".parse().expect("Failed to parse.");
+ /// let s5: TinyAsciiStr<4> = "001z".parse().expect("Failed to parse.");
+ ///
+ /// assert!(!s1.is_ascii_alphabetic_lowercase());
+ /// assert!(!s2.is_ascii_alphabetic_lowercase());
+ /// assert!(!s3.is_ascii_alphabetic_lowercase());
+ /// assert!(s4.is_ascii_alphabetic_lowercase());
+ /// assert!(!s5.is_ascii_alphabetic_lowercase());
+ /// ```
+ #[inline]
+ #[must_use]
+ pub const fn is_ascii_alphabetic_lowercase(&self) -> bool {
+ check_is!(
+ self,
+ is_ascii_alphabetic_lowercase,
+ is_ascii_lowercase,
+ is_ascii_lowercase
+ )
+ }
+
+ /// Checks if the value is composed of ASCII alphabetic, with the first character being ASCII uppercase, and all others ASCII lowercase.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tinystr::TinyAsciiStr;
+ ///
+ /// let s1: TinyAsciiStr<4> = "Test".parse().expect("Failed to parse.");
+ /// let s2: TinyAsciiStr<4> = "Te3t".parse().expect("Failed to parse.");
+ /// let s3: TinyAsciiStr<4> = "teSt".parse().expect("Failed to parse.");
+ /// let s4: TinyAsciiStr<4> = "test".parse().expect("Failed to parse.");
+ /// let s5: TinyAsciiStr<4> = "001z".parse().expect("Failed to parse.");
+ ///
+ /// assert!(s1.is_ascii_alphabetic_titlecase());
+ /// assert!(!s2.is_ascii_alphabetic_titlecase());
+ /// assert!(!s3.is_ascii_alphabetic_titlecase());
+ /// assert!(!s4.is_ascii_alphabetic_titlecase());
+ /// assert!(!s5.is_ascii_alphabetic_titlecase());
+ /// ```
+ #[inline]
+ #[must_use]
+ pub const fn is_ascii_alphabetic_titlecase(&self) -> bool {
+ check_is!(
+ self,
+ is_ascii_alphabetic_titlecase,
+ is_ascii_uppercase,
+ is_ascii_lowercase
+ )
+ }
+
+ /// Checks if the value is composed of ASCII alphabetic upper case characters:
+ ///
+ /// * U+0041 'A' ..= U+005A 'Z',
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tinystr::TinyAsciiStr;
+ ///
+ /// let s1: TinyAsciiStr<4> = "Test".parse().expect("Failed to parse.");
+ /// let s2: TinyAsciiStr<4> = "Te3t".parse().expect("Failed to parse.");
+ /// let s3: TinyAsciiStr<4> = "teSt".parse().expect("Failed to parse.");
+ /// let s4: TinyAsciiStr<4> = "TEST".parse().expect("Failed to parse.");
+ /// let s5: TinyAsciiStr<4> = "001z".parse().expect("Failed to parse.");
+ ///
+ /// assert!(!s1.is_ascii_alphabetic_uppercase());
+ /// assert!(!s2.is_ascii_alphabetic_uppercase());
+ /// assert!(!s3.is_ascii_alphabetic_uppercase());
+ /// assert!(s4.is_ascii_alphabetic_uppercase());
+ /// assert!(!s5.is_ascii_alphabetic_uppercase());
+ /// ```
+ #[inline]
+ #[must_use]
+ pub const fn is_ascii_alphabetic_uppercase(&self) -> bool {
+ check_is!(
+ self,
+ is_ascii_alphabetic_uppercase,
+ is_ascii_uppercase,
+ is_ascii_uppercase
+ )
+ }
+}
+
+macro_rules! to {
+ ($self:ident, $to:ident, $later_char_to:ident $(,$first_char_to:ident)?) => {{
+ let mut i = 0;
+ if N <= 4 {
+ let aligned = Aligned4::from_ascii_bytes(&$self.bytes).$to().to_ascii_bytes();
+ // Won't panic because self.bytes has length N and aligned has length >= N
+ #[allow(clippy::indexing_slicing)]
+ while i < N {
+ $self.bytes[i] = aligned[i];
+ i += 1;
+ }
+ } else if N <= 8 {
+ let aligned = Aligned8::from_ascii_bytes(&$self.bytes).$to().to_ascii_bytes();
+ // Won't panic because self.bytes has length N and aligned has length >= N
+ #[allow(clippy::indexing_slicing)]
+ while i < N {
+ $self.bytes[i] = aligned[i];
+ i += 1;
+ }
+ } else {
+ // Won't panic because self.bytes has length N
+ #[allow(clippy::indexing_slicing)]
+ while i < N && $self.bytes[i] as u8 != AsciiByte::B0 as u8 {
+ // SAFETY: AsciiByte is repr(u8) and has same size as u8
+ unsafe {
+ $self.bytes[i] = core::mem::transmute(
+ ($self.bytes[i] as u8).$later_char_to()
+ );
+ }
+ i += 1;
+ }
+ // SAFETY: AsciiByte is repr(u8) and has same size as u8
+ $(
+ $self.bytes[0] = unsafe {
+ core::mem::transmute(($self.bytes[0] as u8).$first_char_to())
+ };
+ )?
+ }
+ $self
+ }};
+}
+
+impl<const N: usize> TinyAsciiStr<N> {
+ /// Converts this type to its ASCII lower case equivalent in-place.
+ ///
+ /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', other characters are unchanged.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tinystr::TinyAsciiStr;
+ ///
+ /// let s1: TinyAsciiStr<4> = "TeS3".parse().expect("Failed to parse.");
+ ///
+ /// assert_eq!(&*s1.to_ascii_lowercase(), "tes3");
+ /// ```
+ #[inline]
+ #[must_use]
+ pub const fn to_ascii_lowercase(mut self) -> Self {
+ to!(self, to_ascii_lowercase, to_ascii_lowercase)
+ }
+
+ /// Converts this type to its ASCII title case equivalent in-place.
+ ///
+ /// The first character is converted to ASCII uppercase; the remaining characters
+ /// are converted to ASCII lowercase.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tinystr::TinyAsciiStr;
+ ///
+ /// let s1: TinyAsciiStr<4> = "teSt".parse().expect("Failed to parse.");
+ ///
+ /// assert_eq!(&*s1.to_ascii_titlecase(), "Test");
+ /// ```
+ #[inline]
+ #[must_use]
+ pub const fn to_ascii_titlecase(mut self) -> Self {
+ to!(
+ self,
+ to_ascii_titlecase,
+ to_ascii_lowercase,
+ to_ascii_uppercase
+ )
+ }
+
+ /// Converts this type to its ASCII upper case equivalent in-place.
+ ///
+ /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', other characters are unchanged.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tinystr::TinyAsciiStr;
+ ///
+ /// let s1: TinyAsciiStr<4> = "Tes3".parse().expect("Failed to parse.");
+ ///
+ /// assert_eq!(&*s1.to_ascii_uppercase(), "TES3");
+ /// ```
+ #[inline]
+ #[must_use]
+ pub const fn to_ascii_uppercase(mut self) -> Self {
+ to!(self, to_ascii_uppercase, to_ascii_uppercase)
+ }
+}
+
+impl<const N: usize> fmt::Debug for TinyAsciiStr<N> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Debug::fmt(self.as_str(), f)
+ }
+}
+
+impl<const N: usize> fmt::Display for TinyAsciiStr<N> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(self.as_str(), f)
+ }
+}
+
+impl<const N: usize> Deref for TinyAsciiStr<N> {
+ type Target = str;
+ #[inline]
+ fn deref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl<const N: usize> FromStr for TinyAsciiStr<N> {
+ type Err = TinyStrError;
+ #[inline]
+ fn from_str(s: &str) -> Result<Self, TinyStrError> {
+ Self::from_str(s)
+ }
+}
+
+impl<const N: usize> PartialEq<str> for TinyAsciiStr<N> {
+ fn eq(&self, other: &str) -> bool {
+ self.deref() == other
+ }
+}
+
+impl<const N: usize> PartialEq<&str> for TinyAsciiStr<N> {
+ fn eq(&self, other: &&str) -> bool {
+ self.deref() == *other
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<const N: usize> PartialEq<alloc::string::String> for TinyAsciiStr<N> {
+ fn eq(&self, other: &alloc::string::String) -> bool {
+ self.deref() == other.deref()
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<const N: usize> PartialEq<TinyAsciiStr<N>> for alloc::string::String {
+ fn eq(&self, other: &TinyAsciiStr<N>) -> bool {
+ self.deref() == other.deref()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use rand::distributions::Distribution;
+ use rand::distributions::Standard;
+ use rand::rngs::SmallRng;
+ use rand::seq::SliceRandom;
+ use rand::SeedableRng;
+
+ const STRINGS: &[&str] = &[
+ "Latn",
+ "laTn",
+ "windows",
+ "AR",
+ "Hans",
+ "macos",
+ "AT",
+ "infiniband",
+ "FR",
+ "en",
+ "Cyrl",
+ "FromIntegral",
+ "NO",
+ "419",
+ "MacintoshOSX2019",
+ "a3z",
+ "A3z",
+ "A3Z",
+ "a3Z",
+ "3A",
+ "3Z",
+ "3a",
+ "3z",
+ "@@[`{",
+ "UK",
+ "E12",
+ ];
+
+ fn gen_strings(num_strings: usize, allowed_lengths: &[usize]) -> Vec<String> {
+ let mut rng = SmallRng::seed_from_u64(2022);
+ // Need to do this in 2 steps since the RNG is needed twice
+ let string_lengths = core::iter::repeat_with(|| *allowed_lengths.choose(&mut rng).unwrap())
+ .take(num_strings)
+ .collect::<Vec<usize>>();
+ string_lengths
+ .iter()
+ .map(|len| {
+ Standard
+ .sample_iter(&mut rng)
+ .filter(|b: &u8| *b > 0 && *b < 0x80)
+ .take(*len)
+ .collect::<Vec<u8>>()
+ })
+ .map(|byte_vec| String::from_utf8(byte_vec).expect("All ASCII"))
+ .collect()
+ }
+
+ fn check_operation<T, F1, F2, const N: usize>(reference_f: F1, tinystr_f: F2)
+ where
+ F1: Fn(&str) -> T,
+ F2: Fn(TinyAsciiStr<N>) -> T,
+ T: core::fmt::Debug + core::cmp::PartialEq,
+ {
+ for s in STRINGS
+ .iter()
+ .map(|s| s.to_string())
+ .chain(gen_strings(100, &[3, 4, 5, 8, 12]))
+ {
+ let t = match TinyAsciiStr::<N>::from_str(&s) {
+ Ok(t) => t,
+ Err(TinyStrError::TooLarge { .. }) => continue,
+ Err(e) => panic!("{}", e),
+ };
+ let expected = reference_f(&s);
+ let actual = tinystr_f(t);
+ assert_eq!(expected, actual, "TinyAsciiStr<{}>: {:?}", N, s);
+ }
+ }
+
+ #[test]
+ fn test_is_ascii_alphabetic() {
+ fn check<const N: usize>() {
+ check_operation(
+ |s| s.chars().all(|c| c.is_ascii_alphabetic()),
+ |t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_alphabetic(&t),
+ )
+ }
+ check::<2>();
+ check::<3>();
+ check::<4>();
+ check::<5>();
+ check::<8>();
+ check::<16>();
+ }
+
+ #[test]
+ fn test_is_ascii_alphanumeric() {
+ fn check<const N: usize>() {
+ check_operation(
+ |s| s.chars().all(|c| c.is_ascii_alphanumeric()),
+ |t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_alphanumeric(&t),
+ )
+ }
+ check::<2>();
+ check::<3>();
+ check::<4>();
+ check::<5>();
+ check::<8>();
+ check::<16>();
+ }
+
+ #[test]
+ fn test_is_ascii_numeric() {
+ fn check<const N: usize>() {
+ check_operation(
+ |s| s.chars().all(|c| c.is_ascii_digit()),
+ |t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_numeric(&t),
+ )
+ }
+ check::<2>();
+ check::<3>();
+ check::<4>();
+ check::<5>();
+ check::<8>();
+ check::<16>();
+ }
+
+ #[test]
+ fn test_is_ascii_lowercase() {
+ fn check<const N: usize>() {
+ check_operation(
+ |s| {
+ s == TinyAsciiStr::<16>::from_str(s)
+ .unwrap()
+ .to_ascii_lowercase()
+ .as_str()
+ },
+ |t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_lowercase(&t),
+ )
+ }
+ check::<2>();
+ check::<3>();
+ check::<4>();
+ check::<5>();
+ check::<8>();
+ check::<16>();
+ }
+
+ #[test]
+ fn test_is_ascii_titlecase() {
+ fn check<const N: usize>() {
+ check_operation(
+ |s| {
+ s == TinyAsciiStr::<16>::from_str(s)
+ .unwrap()
+ .to_ascii_titlecase()
+ .as_str()
+ },
+ |t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_titlecase(&t),
+ )
+ }
+ check::<2>();
+ check::<3>();
+ check::<4>();
+ check::<5>();
+ check::<8>();
+ check::<16>();
+ }
+
+ #[test]
+ fn test_is_ascii_uppercase() {
+ fn check<const N: usize>() {
+ check_operation(
+ |s| {
+ s == TinyAsciiStr::<16>::from_str(s)
+ .unwrap()
+ .to_ascii_uppercase()
+ .as_str()
+ },
+ |t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_uppercase(&t),
+ )
+ }
+ check::<2>();
+ check::<3>();
+ check::<4>();
+ check::<5>();
+ check::<8>();
+ check::<16>();
+ }
+
+ #[test]
+ fn test_is_ascii_alphabetic_lowercase() {
+ fn check<const N: usize>() {
+ check_operation(
+ |s| {
+ // Check alphabetic
+ s.chars().all(|c| c.is_ascii_alphabetic()) &&
+ // Check lowercase
+ s == TinyAsciiStr::<16>::from_str(s)
+ .unwrap()
+ .to_ascii_lowercase()
+ .as_str()
+ },
+ |t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_alphabetic_lowercase(&t),
+ )
+ }
+ check::<2>();
+ check::<3>();
+ check::<4>();
+ check::<5>();
+ check::<8>();
+ check::<16>();
+ }
+
+ #[test]
+ fn test_is_ascii_alphabetic_titlecase() {
+ fn check<const N: usize>() {
+ check_operation(
+ |s| {
+ // Check alphabetic
+ s.chars().all(|c| c.is_ascii_alphabetic()) &&
+ // Check titlecase
+ s == TinyAsciiStr::<16>::from_str(s)
+ .unwrap()
+ .to_ascii_titlecase()
+ .as_str()
+ },
+ |t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_alphabetic_titlecase(&t),
+ )
+ }
+ check::<2>();
+ check::<3>();
+ check::<4>();
+ check::<5>();
+ check::<8>();
+ check::<16>();
+ }
+
+ #[test]
+ fn test_is_ascii_alphabetic_uppercase() {
+ fn check<const N: usize>() {
+ check_operation(
+ |s| {
+ // Check alphabetic
+ s.chars().all(|c| c.is_ascii_alphabetic()) &&
+ // Check uppercase
+ s == TinyAsciiStr::<16>::from_str(s)
+ .unwrap()
+ .to_ascii_uppercase()
+ .as_str()
+ },
+ |t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_alphabetic_uppercase(&t),
+ )
+ }
+ check::<2>();
+ check::<3>();
+ check::<4>();
+ check::<5>();
+ check::<8>();
+ check::<16>();
+ }
+
+ #[test]
+ fn test_to_ascii_lowercase() {
+ fn check<const N: usize>() {
+ check_operation(
+ |s| {
+ s.chars()
+ .map(|c| c.to_ascii_lowercase())
+ .collect::<String>()
+ },
+ |t: TinyAsciiStr<N>| TinyAsciiStr::to_ascii_lowercase(t).to_string(),
+ )
+ }
+ check::<2>();
+ check::<3>();
+ check::<4>();
+ check::<5>();
+ check::<8>();
+ check::<16>();
+ }
+
+ #[test]
+ fn test_to_ascii_titlecase() {
+ fn check<const N: usize>() {
+ check_operation(
+ |s| {
+ let mut r = s
+ .chars()
+ .map(|c| c.to_ascii_lowercase())
+ .collect::<String>();
+ // Safe because the string is nonempty and an ASCII string
+ unsafe { r.as_bytes_mut()[0].make_ascii_uppercase() };
+ r
+ },
+ |t: TinyAsciiStr<N>| TinyAsciiStr::to_ascii_titlecase(t).to_string(),
+ )
+ }
+ check::<2>();
+ check::<3>();
+ check::<4>();
+ check::<5>();
+ check::<8>();
+ check::<16>();
+ }
+
+ #[test]
+ fn test_to_ascii_uppercase() {
+ fn check<const N: usize>() {
+ check_operation(
+ |s| {
+ s.chars()
+ .map(|c| c.to_ascii_uppercase())
+ .collect::<String>()
+ },
+ |t: TinyAsciiStr<N>| TinyAsciiStr::to_ascii_uppercase(t).to_string(),
+ )
+ }
+ check::<2>();
+ check::<3>();
+ check::<4>();
+ check::<5>();
+ check::<8>();
+ check::<16>();
+ }
+}
diff --git a/vendor/tinystr/src/asciibyte.rs b/vendor/tinystr/src/asciibyte.rs
new file mode 100644
index 000000000..f41a03341
--- /dev/null
+++ b/vendor/tinystr/src/asciibyte.rs
@@ -0,0 +1,145 @@
+// This file is part of ICU4X. For terms of use, please see the file
+// called LICENSE at the top level of the ICU4X source tree
+// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
+
+#[repr(u8)]
+#[allow(dead_code)]
+#[derive(PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Hash)]
+pub enum AsciiByte {
+ B0 = 0,
+ B1 = 1,
+ B2 = 2,
+ B3 = 3,
+ B4 = 4,
+ B5 = 5,
+ B6 = 6,
+ B7 = 7,
+ B8 = 8,
+ B9 = 9,
+ B10 = 10,
+ B11 = 11,
+ B12 = 12,
+ B13 = 13,
+ B14 = 14,
+ B15 = 15,
+ B16 = 16,
+ B17 = 17,
+ B18 = 18,
+ B19 = 19,
+ B20 = 20,
+ B21 = 21,
+ B22 = 22,
+ B23 = 23,
+ B24 = 24,
+ B25 = 25,
+ B26 = 26,
+ B27 = 27,
+ B28 = 28,
+ B29 = 29,
+ B30 = 30,
+ B31 = 31,
+ B32 = 32,
+ B33 = 33,
+ B34 = 34,
+ B35 = 35,
+ B36 = 36,
+ B37 = 37,
+ B38 = 38,
+ B39 = 39,
+ B40 = 40,
+ B41 = 41,
+ B42 = 42,
+ B43 = 43,
+ B44 = 44,
+ B45 = 45,
+ B46 = 46,
+ B47 = 47,
+ B48 = 48,
+ B49 = 49,
+ B50 = 50,
+ B51 = 51,
+ B52 = 52,
+ B53 = 53,
+ B54 = 54,
+ B55 = 55,
+ B56 = 56,
+ B57 = 57,
+ B58 = 58,
+ B59 = 59,
+ B60 = 60,
+ B61 = 61,
+ B62 = 62,
+ B63 = 63,
+ B64 = 64,
+ B65 = 65,
+ B66 = 66,
+ B67 = 67,
+ B68 = 68,
+ B69 = 69,
+ B70 = 70,
+ B71 = 71,
+ B72 = 72,
+ B73 = 73,
+ B74 = 74,
+ B75 = 75,
+ B76 = 76,
+ B77 = 77,
+ B78 = 78,
+ B79 = 79,
+ B80 = 80,
+ B81 = 81,
+ B82 = 82,
+ B83 = 83,
+ B84 = 84,
+ B85 = 85,
+ B86 = 86,
+ B87 = 87,
+ B88 = 88,
+ B89 = 89,
+ B90 = 90,
+ B91 = 91,
+ B92 = 92,
+ B93 = 93,
+ B94 = 94,
+ B95 = 95,
+ B96 = 96,
+ B97 = 97,
+ B98 = 98,
+ B99 = 99,
+ B100 = 100,
+ B101 = 101,
+ B102 = 102,
+ B103 = 103,
+ B104 = 104,
+ B105 = 105,
+ B106 = 106,
+ B107 = 107,
+ B108 = 108,
+ B109 = 109,
+ B110 = 110,
+ B111 = 111,
+ B112 = 112,
+ B113 = 113,
+ B114 = 114,
+ B115 = 115,
+ B116 = 116,
+ B117 = 117,
+ B118 = 118,
+ B119 = 119,
+ B120 = 120,
+ B121 = 121,
+ B122 = 122,
+ B123 = 123,
+ B124 = 124,
+ B125 = 125,
+ B126 = 126,
+ B127 = 127,
+}
+
+impl AsciiByte {
+ // Convert [u8; N] to [AsciiByte; N]
+ #[inline]
+ pub const unsafe fn to_ascii_byte_array<const N: usize>(bytes: &[u8; N]) -> [AsciiByte; N] {
+ *(bytes as *const [u8; N] as *const [AsciiByte; N])
+ }
+}
diff --git a/vendor/tinystr/src/databake.rs b/vendor/tinystr/src/databake.rs
new file mode 100644
index 000000000..e10c194f8
--- /dev/null
+++ b/vendor/tinystr/src/databake.rs
@@ -0,0 +1,21 @@
+// This file is part of ICU4X. For terms of use, please see the file
+// called LICENSE at the top level of the ICU4X source tree
+// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
+
+use crate::TinyAsciiStr;
+use databake::*;
+
+impl<const N: usize> Bake for TinyAsciiStr<N> {
+ fn bake(&self, env: &CrateEnv) -> TokenStream {
+ env.insert("tinystr");
+ let string = self.as_str();
+ quote! {
+ ::tinystr::tinystr!(#N, #string)
+ }
+ }
+}
+
+#[test]
+fn test() {
+ test_bake!(TinyAsciiStr<10>, const: crate::tinystr!(10usize, "foo"), tinystr);
+}
diff --git a/vendor/tinystr/src/error.rs b/vendor/tinystr/src/error.rs
new file mode 100644
index 000000000..03901431c
--- /dev/null
+++ b/vendor/tinystr/src/error.rs
@@ -0,0 +1,16 @@
+// This file is part of ICU4X. For terms of use, please see the file
+// called LICENSE at the top level of the ICU4X source tree
+// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
+
+use displaydoc::Display;
+
+#[derive(Display, Debug, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum TinyStrError {
+ #[displaydoc("found string of larger length {len} when constructing string of length {max}")]
+ TooLarge { max: usize, len: usize },
+ #[displaydoc("tinystr types do not support strings with null bytes")]
+ ContainsNull,
+ #[displaydoc("attempted to construct TinyStrAuto from a non-ascii string")]
+ NonAscii,
+}
diff --git a/vendor/tinystr/src/helpers.rs b/vendor/tinystr/src/helpers.rs
deleted file mode 100644
index c3d17d028..000000000
--- a/vendor/tinystr/src/helpers.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use std::num::NonZeroU32;
-use std::ptr::copy_nonoverlapping;
-
-use super::Error;
-
-#[cfg(any(feature = "std", test))]
-pub use std::string::String;
-
-#[cfg(all(not(feature = "std"), not(test)))]
-extern crate alloc;
-
-#[cfg(all(not(feature = "std"), not(test)))]
-pub use alloc::string::String;
-
-#[inline(always)]
-pub(crate) unsafe fn make_4byte_bytes(
- bytes: &[u8],
- len: usize,
- mask: u32,
-) -> Result<NonZeroU32, Error> {
- // Mask is always supplied as little-endian.
- let mask = u32::from_le(mask);
- let mut word: u32 = 0;
- copy_nonoverlapping(bytes.as_ptr(), &mut word as *mut u32 as *mut u8, len);
- if (word & mask) != 0 {
- return Err(Error::NonAscii);
- }
- if ((mask - word) & mask) != 0 {
- return Err(Error::InvalidNull);
- }
- Ok(NonZeroU32::new_unchecked(word))
-}
diff --git a/vendor/tinystr/src/int_ops.rs b/vendor/tinystr/src/int_ops.rs
new file mode 100644
index 000000000..102b052f2
--- /dev/null
+++ b/vendor/tinystr/src/int_ops.rs
@@ -0,0 +1,315 @@
+// This file is part of ICU4X. For terms of use, please see the file
+// called LICENSE at the top level of the ICU4X source tree
+// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
+
+use crate::asciibyte::AsciiByte;
+
+/// Internal helper struct that performs operations on aligned integers.
+/// Supports strings up to 4 bytes long.
+#[repr(transparent)]
+pub struct Aligned4(u32);
+
+impl Aligned4 {
+ /// # Panics
+ /// Panics if N is greater than 4
+ #[inline]
+ pub const fn from_bytes<const N: usize>(src: &[u8; N]) -> Self {
+ let mut bytes = [0; 4];
+ let mut i = 0;
+ // The function documentation defines when panics may occur
+ #[allow(clippy::indexing_slicing)]
+ while i < N {
+ bytes[i] = src[i];
+ i += 1;
+ }
+ Self(u32::from_ne_bytes(bytes))
+ }
+
+ #[inline]
+ pub const fn from_ascii_bytes<const N: usize>(src: &[AsciiByte; N]) -> Self {
+ Self::from_bytes::<N>(unsafe { core::mem::transmute(src) })
+ }
+
+ #[inline]
+ pub const fn to_bytes(&self) -> [u8; 4] {
+ self.0.to_ne_bytes()
+ }
+
+ #[inline]
+ pub const fn to_ascii_bytes(&self) -> [AsciiByte; 4] {
+ unsafe { core::mem::transmute(self.to_bytes()) }
+ }
+
+ pub const fn len(&self) -> usize {
+ let word = self.0;
+ #[cfg(target_endian = "little")]
+ let len = (4 - word.leading_zeros() / 8) as usize;
+ #[cfg(target_endian = "big")]
+ let len = (4 - word.trailing_zeros() / 8) as usize;
+ len
+ }
+
+ pub const fn is_ascii_alphabetic(&self) -> bool {
+ let word = self.0;
+ // Each of the following bitmasks set *the high bit* (0x8) to 0 for valid and 1 for invalid.
+ // `mask` sets all NUL bytes to 0.
+ let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
+ // `lower` converts the string to lowercase. It may also change the value of non-alpha
+ // characters, but this does not matter for the alphabetic test that follows.
+ let lower = word | 0x2020_2020;
+ // `alpha` sets all alphabetic bytes to 0. We only need check for lowercase characters.
+ let alpha = !(lower + 0x1f1f_1f1f) | (lower + 0x0505_0505);
+ // The overall string is valid if every character passes at least one test.
+ // We performed two tests here: non-NUL (`mask`) and alphabetic (`alpha`).
+ (alpha & mask) == 0
+ }
+
+ pub const fn is_ascii_alphanumeric(&self) -> bool {
+ let word = self.0;
+ // See explanatory comments in is_ascii_alphabetic
+ let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
+ let numeric = !(word + 0x5050_5050) | (word + 0x4646_4646);
+ let lower = word | 0x2020_2020;
+ let alpha = !(lower + 0x1f1f_1f1f) | (lower + 0x0505_0505);
+ (alpha & numeric & mask) == 0
+ }
+
+ pub const fn is_ascii_numeric(&self) -> bool {
+ let word = self.0;
+ // See explanatory comments in is_ascii_alphabetic
+ let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
+ let numeric = !(word + 0x5050_5050) | (word + 0x4646_4646);
+ (numeric & mask) == 0
+ }
+
+ pub const fn is_ascii_lowercase(&self) -> bool {
+ let word = self.0;
+ // For efficiency, this function tests for an invalid string rather than a valid string.
+ // A string is ASCII lowercase iff it contains no uppercase ASCII characters.
+ // `invalid_case` sets all uppercase ASCII characters to 0 and all others to 1.
+ let invalid_case = !(word + 0x3f3f_3f3f) | (word + 0x2525_2525);
+ // The string is valid if it contains no invalid characters (if all high bits are 1).
+ (invalid_case & 0x8080_8080) == 0x8080_8080
+ }
+
+ pub const fn is_ascii_titlecase(&self) -> bool {
+ let word = self.0;
+ // See explanatory comments in is_ascii_lowercase
+ let invalid_case = if cfg!(target_endian = "little") {
+ !(word + 0x3f3f_3f1f) | (word + 0x2525_2505)
+ } else {
+ !(word + 0x1f3f_3f3f) | (word + 0x0525_2525)
+ };
+ (invalid_case & 0x8080_8080) == 0x8080_8080
+ }
+
+ pub const fn is_ascii_uppercase(&self) -> bool {
+ let word = self.0;
+ // See explanatory comments in is_ascii_lowercase
+ let invalid_case = !(word + 0x1f1f_1f1f) | (word + 0x0505_0505);
+ (invalid_case & 0x8080_8080) == 0x8080_8080
+ }
+
+ pub const fn is_ascii_alphabetic_lowercase(&self) -> bool {
+ let word = self.0;
+ // `mask` sets all NUL bytes to 0.
+ let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
+ // `lower_alpha` sets all lowercase ASCII characters to 0 and all others to 1.
+ let lower_alpha = !(word + 0x1f1f_1f1f) | (word + 0x0505_0505);
+ // The overall string is valid if every character passes at least one test.
+ // We performed two tests here: non-NUL (`mask`) and lowercase ASCII character (`alpha`).
+ (lower_alpha & mask) == 0
+ }
+
+ pub const fn is_ascii_alphabetic_titlecase(&self) -> bool {
+ let word = self.0;
+ // See explanatory comments in is_ascii_alphabetic_lowercase
+ let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
+ let title_case = if cfg!(target_endian = "little") {
+ !(word + 0x1f1f_1f3f) | (word + 0x0505_0525)
+ } else {
+ !(word + 0x3f1f_1f1f) | (word + 0x2505_0505)
+ };
+ (title_case & mask) == 0
+ }
+
+ pub const fn is_ascii_alphabetic_uppercase(&self) -> bool {
+ let word = self.0;
+ // See explanatory comments in is_ascii_alphabetic_lowercase
+ let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
+ let upper_alpha = !(word + 0x3f3f_3f3f) | (word + 0x2525_2525);
+ (upper_alpha & mask) == 0
+ }
+
+ pub const fn to_ascii_lowercase(&self) -> Self {
+ let word = self.0;
+ let result = word | (((word + 0x3f3f_3f3f) & !(word + 0x2525_2525) & 0x8080_8080) >> 2);
+ Self(result)
+ }
+
+ pub const fn to_ascii_titlecase(&self) -> Self {
+ let word = self.0.to_le();
+ let mask = ((word + 0x3f3f_3f1f) & !(word + 0x2525_2505) & 0x8080_8080) >> 2;
+ let result = (word | mask) & !(0x20 & mask);
+ Self(u32::from_le(result))
+ }
+
+ pub const fn to_ascii_uppercase(&self) -> Self {
+ let word = self.0;
+ let result = word & !(((word + 0x1f1f_1f1f) & !(word + 0x0505_0505) & 0x8080_8080) >> 2);
+ Self(result)
+ }
+}
+
+/// Internal helper struct that performs operations on aligned integers.
+/// Supports strings up to 8 bytes long.
+#[repr(transparent)]
+pub struct Aligned8(u64);
+
+impl Aligned8 {
+ /// # Panics
+ /// Panics if N is greater than 8
+ #[inline]
+ pub const fn from_bytes<const N: usize>(src: &[u8; N]) -> Self {
+ let mut bytes = [0; 8];
+ let mut i = 0;
+ // The function documentation defines when panics may occur
+ #[allow(clippy::indexing_slicing)]
+ while i < N {
+ bytes[i] = src[i];
+ i += 1;
+ }
+ Self(u64::from_ne_bytes(bytes))
+ }
+
+ #[inline]
+ pub const fn from_ascii_bytes<const N: usize>(src: &[AsciiByte; N]) -> Self {
+ Self::from_bytes::<N>(unsafe { core::mem::transmute(src) })
+ }
+
+ #[inline]
+ pub const fn to_bytes(&self) -> [u8; 8] {
+ self.0.to_ne_bytes()
+ }
+
+ #[inline]
+ pub const fn to_ascii_bytes(&self) -> [AsciiByte; 8] {
+ unsafe { core::mem::transmute(self.to_bytes()) }
+ }
+
+ pub const fn len(&self) -> usize {
+ let word = self.0;
+ #[cfg(target_endian = "little")]
+ let len = (8 - word.leading_zeros() / 8) as usize;
+ #[cfg(target_endian = "big")]
+ let len = (8 - word.trailing_zeros() / 8) as usize;
+ len
+ }
+
+ pub const fn is_ascii_alphabetic(&self) -> bool {
+ let word = self.0;
+ let mask = (word + 0x7f7f_7f7f_7f7f_7f7f) & 0x8080_8080_8080_8080;
+ let lower = word | 0x2020_2020_2020_2020;
+ let alpha = !(lower + 0x1f1f_1f1f_1f1f_1f1f) | (lower + 0x0505_0505_0505_0505);
+ (alpha & mask) == 0
+ }
+
+ pub const fn is_ascii_alphanumeric(&self) -> bool {
+ let word = self.0;
+ let mask = (word + 0x7f7f_7f7f_7f7f_7f7f) & 0x8080_8080_8080_8080;
+ let numeric = !(word + 0x5050_5050_5050_5050) | (word + 0x4646_4646_4646_4646);
+ let lower = word | 0x2020_2020_2020_2020;
+ let alpha = !(lower + 0x1f1f_1f1f_1f1f_1f1f) | (lower + 0x0505_0505_0505_0505);
+ (alpha & numeric & mask) == 0
+ }
+
+ pub const fn is_ascii_numeric(&self) -> bool {
+ let word = self.0;
+ let mask = (word + 0x7f7f_7f7f_7f7f_7f7f) & 0x8080_8080_8080_8080;
+ let numeric = !(word + 0x5050_5050_5050_5050) | (word + 0x4646_4646_4646_4646);
+ (numeric & mask) == 0
+ }
+
+ pub const fn is_ascii_lowercase(&self) -> bool {
+ let word = self.0;
+ let invalid_case = !(word + 0x3f3f_3f3f_3f3f_3f3f) | (word + 0x2525_2525_2525_2525);
+ (invalid_case & 0x8080_8080_8080_8080) == 0x8080_8080_8080_8080
+ }
+
+ pub const fn is_ascii_titlecase(&self) -> bool {
+ let word = self.0;
+ let invalid_case = if cfg!(target_endian = "little") {
+ !(word + 0x3f3f_3f3f_3f3f_3f1f) | (word + 0x2525_2525_2525_2505)
+ } else {
+ !(word + 0x1f3f_3f3f_3f3f_3f3f) | (word + 0x0525_2525_2525_2525)
+ };
+ (invalid_case & 0x8080_8080_8080_8080) == 0x8080_8080_8080_8080
+ }
+
+ pub const fn is_ascii_uppercase(&self) -> bool {
+ let word = self.0;
+ let invalid_case = !(word + 0x1f1f_1f1f_1f1f_1f1f) | (word + 0x0505_0505_0505_0505);
+ (invalid_case & 0x8080_8080_8080_8080) == 0x8080_8080_8080_8080
+ }
+
+ pub const fn is_ascii_alphabetic_lowercase(&self) -> bool {
+ let word = self.0;
+ // `mask` sets all NUL bytes to 0.
+ let mask = (word + 0x7f7f_7f7f_7f7f_7f7f) & 0x8080_8080_8080_8080;
+ // `lower_alpha` sets all lowercase ASCII characters to 0 and all others to 1.
+ let lower_alpha = !(word + 0x1f1f_1f1f_1f1f_1f1f) | (word + 0x0505_0505_0505_0505);
+ // The overall string is valid if every character passes at least one test.
+ // We performed two tests here: non-NUL (`mask`) and lowercase ASCII character (`alpha`).
+ (lower_alpha & mask) == 0
+ }
+
+ pub const fn is_ascii_alphabetic_titlecase(&self) -> bool {
+ let word = self.0;
+ // See explanatory comments in is_ascii_alphabetic_lowercase
+ let mask = (word + 0x7f7f_7f7f_7f7f_7f7f) & 0x8080_8080_8080_8080;
+ let title_case = if cfg!(target_endian = "little") {
+ !(word + 0x1f1f_1f1f_1f1f_1f3f) | (word + 0x0505_0505_0505_0525)
+ } else {
+ !(word + 0x3f1f_1f1f_1f1f_1f1f) | (word + 0x2505_0505_0505_0505)
+ };
+ (title_case & mask) == 0
+ }
+
+ pub const fn is_ascii_alphabetic_uppercase(&self) -> bool {
+ let word = self.0;
+ // See explanatory comments in is_ascii_alphabetic_lowercase
+ let mask = (word + 0x7f7f_7f7f_7f7f_7f7f) & 0x8080_8080_8080_8080;
+ let upper_alpha = !(word + 0x3f3f_3f3f_3f3f_3f3f) | (word + 0x2525_2525_2525_2525);
+ (upper_alpha & mask) == 0
+ }
+
+ pub const fn to_ascii_lowercase(&self) -> Self {
+ let word = self.0;
+ let result = word
+ | (((word + 0x3f3f_3f3f_3f3f_3f3f)
+ & !(word + 0x2525_2525_2525_2525)
+ & 0x8080_8080_8080_8080)
+ >> 2);
+ Self(result)
+ }
+
+ pub const fn to_ascii_titlecase(&self) -> Self {
+ let word = self.0.to_le();
+ let mask = ((word + 0x3f3f_3f3f_3f3f_3f1f)
+ & !(word + 0x2525_2525_2525_2505)
+ & 0x8080_8080_8080_8080)
+ >> 2;
+ let result = (word | mask) & !(0x20 & mask);
+ Self(u64::from_le(result))
+ }
+
+ pub const fn to_ascii_uppercase(&self) -> Self {
+ let word = self.0;
+ let result = word
+ & !(((word + 0x1f1f_1f1f_1f1f_1f1f)
+ & !(word + 0x0505_0505_0505_0505)
+ & 0x8080_8080_8080_8080)
+ >> 2);
+ Self(result)
+ }
+}
diff --git a/vendor/tinystr/src/lib.rs b/vendor/tinystr/src/lib.rs
index 6f4c59658..96018b8b2 100644
--- a/vendor/tinystr/src/lib.rs
+++ b/vendor/tinystr/src/lib.rs
@@ -1,105 +1,116 @@
-//! `tinystr` is a small ASCII-only bounded length string representation.
-//!
-//! The crate is meant to be used for scenarios where one needs a fast
-//! and memory efficient way to store and manipulate short ASCII-only strings.
-//!
-//! `tinystr` converts each string into an unsigned integer, and uses bitmasking
-//! to compare, convert cases and test for common characteristics of strings.
-//!
-//! # Details
-//!
-//! The crate provides three structs and an enum:
-//! * `TinyStr4` an ASCII-only string limited to 4 characters.
-//! * `TinyStr8` an ASCII-only string limited to 8 characters.
-//! * `TinyStr16` an ASCII-only string limited to 16 characters.
-//! * `TinyStrAuto` (enum):
-//! * `Tiny` when the string is 16 characters or less.
-//! * `Heap` when the string is 17 or more characters.
-//!
-//! `TinyStrAuto` stores the string as a TinyStr16 when it is short enough, or else falls back to a
-//! standard `String`. You should use TinyStrAuto when you expect most strings to be 16 characters
-//! or smaller, but occasionally you receive one that exceeds that length. Unlike the structs,
-//! `TinyStrAuto` does not implement `Copy`.
+// This file is part of ICU4X. For terms of use, please see the file
+// called LICENSE at the top level of the ICU4X source tree
+// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
+
+//! `tinystr` is a utility crate of the [`ICU4X`] project.
//!
-//! # no_std
+//! It includes [`TinyAsciiStr`], a core API for representing small ASCII-only bounded length strings.
//!
-//! Disable the `std` feature of this crate to make it `#[no_std]`. Doing so disables `TinyStrAuto`.
-//! You can re-enable `TinyStrAuto` in `#[no_std]` mode by enabling the `alloc` feature.
+//! It is optimized for operations on strings of size 8 or smaller. When use cases involve comparison
+//! and conversion of strings for lowercase/uppercase/titlecase, or checking
+//! numeric/alphabetic/alphanumeric, `TinyAsciiStr` is the edge performance library.
//!
-//! # Example
+//! # Examples
//!
-//! ```
-//! use tinystr::{TinyStr4, TinyStr8, TinyStr16, TinyStrAuto};
+//! ```rust
+//! use tinystr::TinyAsciiStr;
//!
-//! let s1: TinyStr4 = "tEsT".parse()
-//! .expect("Failed to parse.");
+//! let s1: TinyAsciiStr<4> = "tEsT".parse().expect("Failed to parse.");
//!
//! assert_eq!(s1, "tEsT");
//! assert_eq!(s1.to_ascii_uppercase(), "TEST");
//! assert_eq!(s1.to_ascii_lowercase(), "test");
//! assert_eq!(s1.to_ascii_titlecase(), "Test");
//! assert_eq!(s1.is_ascii_alphanumeric(), true);
+//! assert_eq!(s1.is_ascii_numeric(), false);
//!
-//! let s2: TinyStr8 = "New York".parse()
-//! .expect("Failed to parse.");
+//! let s2 = TinyAsciiStr::<8>::try_from_raw(*b"New York")
+//! .expect("Failed to parse.");
//!
//! assert_eq!(s2, "New York");
//! assert_eq!(s2.to_ascii_uppercase(), "NEW YORK");
//! assert_eq!(s2.to_ascii_lowercase(), "new york");
//! assert_eq!(s2.to_ascii_titlecase(), "New york");
//! assert_eq!(s2.is_ascii_alphanumeric(), false);
+//! ```
//!
-//! let s3: TinyStr16 = "metaMoRphosis123".parse()
-//! .expect("Failed to parse.");
-//!
-//! assert_eq!(s3, "metaMoRphosis123");
-//! assert_eq!(s3.to_ascii_uppercase(), "METAMORPHOSIS123");
-//! assert_eq!(s3.to_ascii_lowercase(), "metamorphosis123");
-//! assert_eq!(s3.to_ascii_titlecase(), "Metamorphosis123");
-//! assert_eq!(s3.is_ascii_alphanumeric(), true);
+//! # Details
//!
-//! let s4: TinyStrAuto = "shortNoAlloc".parse().unwrap();
-//! assert!(matches!(s4, TinyStrAuto::Tiny { .. }));
-//! assert_eq!(s4, "shortNoAlloc");
+//! When strings are of size 8 or smaller, the struct transforms the strings as `u32`/`u64` and uses
+//! bitmasking to provide basic string manipulation operations:
+//! * `is_ascii_numeric`
+//! * `is_ascii_alphabetic`
+//! * `is_ascii_alphanumeric`
+//! * `to_ascii_lowercase`
+//! * `to_ascii_uppercase`
+//! * `to_ascii_titlecase`
+//! * `PartialEq`
+//!
+//! `TinyAsciiStr` will fall back to `u8` character manipulation for strings of length greater than 8.
+
//!
-//! let s5: TinyStrAuto = "longFallbackToHeap".parse().unwrap();
-//! assert!(matches!(s5, TinyStrAuto::Heap { .. }));
-//! assert_eq!(s5, "longFallbackToHeap");
-//! ```
+//! [`ICU4X`]: ../icu/index.html
+
+// https://github.com/unicode-org/icu4x/blob/main/docs/process/boilerplate.md#library-annotations
+#![cfg_attr(not(test), no_std)]
+#![cfg_attr(
+ not(test),
+ deny(
+ clippy::indexing_slicing,
+ clippy::unwrap_used,
+ clippy::expect_used,
+ clippy::panic,
+ clippy::exhaustive_structs,
+ clippy::exhaustive_enums,
+ missing_debug_implementations,
+ )
+)]
-#![no_std]
+mod macros;
-#[cfg(any(feature = "std", test))]
-extern crate std;
+mod ascii;
+mod asciibyte;
+mod error;
+mod int_ops;
-#[cfg(all(not(feature = "std"), not(test)))]
-extern crate core as std;
+#[cfg(feature = "serde")]
+mod serde;
-mod helpers;
-mod tinystr16;
-mod tinystr4;
-mod tinystr8;
+#[cfg(feature = "databake")]
+mod databake;
-#[cfg(any(feature = "std", feature = "alloc"))]
-mod tinystrauto;
+#[cfg(feature = "zerovec")]
+mod ule;
-pub use tinystr16::TinyStr16;
-pub use tinystr4::TinyStr4;
-pub use tinystr8::TinyStr8;
+#[cfg(any(feature = "serde", feature = "alloc"))]
+extern crate alloc;
-#[cfg(any(feature = "std", feature = "alloc"))]
-pub use tinystrauto::TinyStrAuto;
+pub use ascii::TinyAsciiStr;
+pub use error::TinyStrError;
-#[cfg(feature = "macros")]
-pub use tinystr_macros as macros;
+/// These are temporary compatability reexports that will be removed
+/// in a future version.
+pub type TinyStr4 = TinyAsciiStr<4>;
+/// These are temporary compatability reexports that will be removed
+/// in a future version.
+pub type TinyStr8 = TinyAsciiStr<8>;
+/// These are temporary compatability reexports that will be removed
+/// in a future version.
+pub type TinyStr16 = TinyAsciiStr<16>;
-/// Enum to store the various types of errors that can cause parsing a TinyStr to fail.
-#[derive(PartialEq, Eq, Debug)]
-pub enum Error {
- /// String is too large or too small to store as TinyStr.
- InvalidSize,
- /// String is empty.
- InvalidNull,
- /// String contains non-ASCII character(s).
- NonAscii,
+#[test]
+fn test_size() {
+ assert_eq!(
+ core::mem::size_of::<TinyStr4>(),
+ core::mem::size_of::<Option<TinyStr4>>()
+ );
+ assert_eq!(
+ core::mem::size_of::<TinyStr8>(),
+ core::mem::size_of::<Option<TinyStr8>>()
+ );
}
+// /// Allows unit tests to use the macro
+// #[cfg(test)]
+// mod tinystr {
+// pub use super::{TinyAsciiStr, TinyStrError};
+// }
diff --git a/vendor/tinystr/src/macros.rs b/vendor/tinystr/src/macros.rs
new file mode 100644
index 000000000..b00185238
--- /dev/null
+++ b/vendor/tinystr/src/macros.rs
@@ -0,0 +1,32 @@
+// This file is part of ICU4X. For terms of use, please see the file
+// called LICENSE at the top level of the ICU4X source tree
+// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
+
+#[macro_export]
+macro_rules! tinystr {
+ ($n:literal, $s:literal) => {{
+ // Force it into a const context; otherwise it may get evaluated at runtime instead.
+ const TINYSTR_MACRO_CONST: $crate::TinyAsciiStr<$n> = {
+ match $crate::TinyAsciiStr::from_bytes($s.as_bytes()) {
+ Ok(s) => s,
+ // We are okay with panicking here because this is in a const context
+ #[allow(clippy::panic)]
+ // Cannot format the error since formatting isn't const yet
+ Err(_) => panic!(concat!("Failed to construct tinystr from ", $s)),
+ }
+ };
+ TINYSTR_MACRO_CONST
+ }};
+}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn test_macro_construction() {
+ let s1 = tinystr!(8, "foobar");
+ assert_eq!(&*s1, "foobar");
+
+ let s1 = tinystr!(12, "foobarbaz");
+ assert_eq!(&*s1, "foobarbaz");
+ }
+}
diff --git a/vendor/tinystr/src/serde.rs b/vendor/tinystr/src/serde.rs
new file mode 100644
index 000000000..933491f17
--- /dev/null
+++ b/vendor/tinystr/src/serde.rs
@@ -0,0 +1,91 @@
+// This file is part of ICU4X. For terms of use, please see the file
+// called LICENSE at the top level of the ICU4X source tree
+// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
+
+use crate::TinyAsciiStr;
+use alloc::borrow::Cow;
+use alloc::string::ToString;
+use core::fmt;
+use core::marker::PhantomData;
+use core::ops::Deref;
+use serde::de::{Error, SeqAccess, Visitor};
+use serde::ser::SerializeTuple;
+use serde::{Deserialize, Deserializer, Serialize, Serializer};
+
+impl<const N: usize> Serialize for TinyAsciiStr<N> {
+ #[inline]
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ if serializer.is_human_readable() {
+ self.deref().serialize(serializer)
+ } else {
+ let mut seq = serializer.serialize_tuple(N)?;
+ for byte in self.all_bytes() {
+ seq.serialize_element(byte)?;
+ }
+ seq.end()
+ }
+ }
+}
+
+struct TinyAsciiStrVisitor<const N: usize> {
+ marker: PhantomData<TinyAsciiStr<N>>,
+}
+
+impl<const N: usize> TinyAsciiStrVisitor<N> {
+ fn new() -> Self {
+ TinyAsciiStrVisitor {
+ marker: PhantomData,
+ }
+ }
+}
+
+impl<'de, const N: usize> Visitor<'de> for TinyAsciiStrVisitor<N> {
+ type Value = TinyAsciiStr<N>;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ write!(formatter, "a TinyAsciiStr<{}>", N)
+ }
+
+ #[inline]
+ fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
+ where
+ A: SeqAccess<'de>,
+ {
+ let mut bytes = [0u8; N];
+ let mut zeroes = false;
+ for out in &mut bytes.iter_mut().take(N) {
+ let byte = seq
+ .next_element()?
+ .ok_or_else(|| Error::invalid_length(N, &self))?;
+ if byte == 0 {
+ zeroes = true;
+ } else if zeroes {
+ return Err(Error::custom("TinyAsciiStr cannot contain null bytes"));
+ }
+
+ if byte >= 0x80 {
+ return Err(Error::custom("TinyAsciiStr cannot contain non-ascii bytes"));
+ }
+ *out = byte;
+ }
+
+ Ok(unsafe { TinyAsciiStr::from_bytes_unchecked(bytes) })
+ }
+}
+
+impl<'de, const N: usize> Deserialize<'de> for TinyAsciiStr<N> {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ if deserializer.is_human_readable() {
+ let x: Cow<'de, str> = Deserialize::deserialize(deserializer)?;
+ TinyAsciiStr::from_str(&x).map_err(|e| Error::custom(e.to_string()))
+ } else {
+ deserializer.deserialize_tuple(N, TinyAsciiStrVisitor::<N>::new())
+ }
+ }
+}
diff --git a/vendor/tinystr/src/tinystr16.rs b/vendor/tinystr/src/tinystr16.rs
deleted file mode 100644
index 7403813f2..000000000
--- a/vendor/tinystr/src/tinystr16.rs
+++ /dev/null
@@ -1,327 +0,0 @@
-use std::cmp::Ordering;
-use std::convert::Into;
-use std::fmt;
-use std::num::NonZeroU128;
-use std::ops::Deref;
-use std::ptr::copy_nonoverlapping;
-use std::str::FromStr;
-
-use crate::Error;
-
-/// A tiny string that is from 1 to 16 non-NUL ASCII characters.
-///
-/// # Examples
-///
-/// ```
-/// use tinystr::TinyStr16;
-///
-/// let s1: TinyStr16 = "Metamorphosis".parse()
-/// .expect("Failed to parse.");
-///
-/// assert_eq!(s1, "Metamorphosis");
-/// assert!(s1.is_ascii_alphabetic());
-/// ```
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub struct TinyStr16(NonZeroU128);
-
-impl TinyStr16 {
- /// Creates a TinyStr16 from a byte slice.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr16;
- ///
- /// let s1 = TinyStr16::from_bytes("Testing".as_bytes())
- /// .expect("Failed to parse.");
- ///
- /// assert_eq!(s1, "Testing");
- /// ```
- #[inline(always)]
- pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
- let len = bytes.len();
- if len < 1 || len > 16 {
- return Err(Error::InvalidSize);
- }
- unsafe {
- let mut word: u128 = 0;
- copy_nonoverlapping(bytes.as_ptr(), &mut word as *mut u128 as *mut u8, len);
- let mask = 0x80808080_80808080_80808080_80808080u128 >> (8 * (16 - len));
- // TODO: could do this with #cfg(target_endian), but this is clearer and
- // more confidence-inspiring.
- let mask = u128::from_le(mask);
- if (word & mask) != 0 {
- return Err(Error::NonAscii);
- }
- if ((mask - word) & mask) != 0 {
- return Err(Error::InvalidNull);
- }
- Ok(Self(NonZeroU128::new_unchecked(word)))
- }
- }
-
- /// An unsafe constructor intended for cases where the consumer
- /// guarantees that the input is a little endian integer which
- /// is a correct representation of a `TinyStr16` string.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr16;
- ///
- /// let s1: TinyStr16 = "Metamorphosis".parse()
- /// .expect("Failed to parse.");
- ///
- /// let num: u128 = s1.into();
- ///
- /// let s2 = unsafe { TinyStr16::new_unchecked(num) };
- ///
- /// assert_eq!(s1, s2);
- /// assert_eq!(s2.as_str(), "Metamorphosis");
- /// ```
- ///
- /// # Safety
- ///
- /// The method does not validate the `u128` to be properly encoded
- /// value for `TinyStr16`.
- /// The value can be retrieved via `Into<u128> for TinyStr16`.
- #[inline(always)]
- pub const unsafe fn new_unchecked(text: u128) -> Self {
- Self(NonZeroU128::new_unchecked(u128::from_le(text)))
- }
-
- /// Extracts a string slice containing the entire `TinyStr16`.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr16;
- ///
- /// let s1: TinyStr16 = "Metamorphosis".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert_eq!(s1.as_str(), "Metamorphosis");
- /// ```
- #[inline(always)]
- pub fn as_str(&self) -> &str {
- self.deref()
- }
-
- /// Checks if the value is composed of ASCII alphabetic characters:
- ///
- /// * U+0041 'A' ..= U+005A 'Z', or
- /// * U+0061 'a' ..= U+007A 'z'.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr16;
- ///
- /// let s1: TinyStr16 = "Metamorphosis".parse()
- /// .expect("Failed to parse.");
- /// let s2: TinyStr16 = "Met3mo4pho!is".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert!(s1.is_ascii_alphabetic());
- /// assert!(!s2.is_ascii_alphabetic());
- /// ```
- pub fn is_ascii_alphabetic(self) -> bool {
- let word = self.0.get();
- let mask =
- (word + 0x7f7f7f7f_7f7f7f7f_7f7f7f7f_7f7f7f7f) & 0x80808080_80808080_80808080_80808080;
- let lower = word | 0x20202020_20202020_20202020_20202020;
- let alpha = !(lower + 0x1f1f1f1f_1f1f1f1f_1f1f1f1f_1f1f1f1f)
- | (lower + 0x05050505_05050505_05050505_05050505);
- (alpha & mask) == 0
- }
-
- /// Checks if the value is composed of ASCII alphanumeric characters:
- ///
- /// * U+0041 'A' ..= U+005A 'Z', or
- /// * U+0061 'a' ..= U+007A 'z', or
- /// * U+0030 '0' ..= U+0039 '9'.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr16;
- ///
- /// let s1: TinyStr16 = "A15bingA1".parse()
- /// .expect("Failed to parse.");
- /// let s2: TinyStr16 = "[3@w00Fs1".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert!(s1.is_ascii_alphanumeric());
- /// assert!(!s2.is_ascii_alphanumeric());
- /// ```
- pub fn is_ascii_alphanumeric(self) -> bool {
- let word = self.0.get();
- let mask =
- (word + 0x7f7f7f7f_7f7f7f7f_7f7f7f7f_7f7f7f7f) & 0x80808080_80808080_80808080_80808080;
- let numeric = !(word + 0x50505050_50505050_50505050_50505050)
- | (word + 0x46464646_46464646_46464646_46464646);
- let lower = word | 0x20202020_20202020_20202020_20202020;
- let alpha = !(lower + 0x1f1f1f1f_1f1f1f1f_1f1f1f1f_1f1f1f1f)
- | (lower + 0x05050505_05050505_05050505_05050505);
- (alpha & numeric & mask) == 0
- }
-
- /// Checks if the value is composed of ASCII decimal digits:
- ///
- /// * U+0030 '0' ..= U+0039 '9'.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr16;
- ///
- /// let s1: TinyStr16 = "31212314141".parse()
- /// .expect("Failed to parse.");
- /// let s2: TinyStr16 = "3d3d3d3d".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert!(s1.is_ascii_numeric());
- /// assert!(!s2.is_ascii_numeric());
- /// ```
- pub fn is_ascii_numeric(self) -> bool {
- let word = self.0.get();
- let mask =
- (word + 0x7f7f7f7f_7f7f7f7f_7f7f7f7f_7f7f7f7f) & 0x80808080_80808080_80808080_80808080;
- let numeric = !(word + 0x50505050_50505050_50505050_50505050)
- | (word + 0x46464646_46464646_46464646_46464646);
- (numeric & mask) == 0
- }
-
- /// Converts this type to its ASCII lower case equivalent in-place.
- ///
- /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', other characters are unchanged.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr16;
- ///
- /// let s1: TinyStr16 = "MeTAmOrpHo3sis".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert_eq!(s1.to_ascii_lowercase(), "metamorpho3sis");
- /// ```
- pub fn to_ascii_lowercase(self) -> Self {
- let word = self.0.get();
- let result = word
- | (((word + 0x3f3f3f3f_3f3f3f3f_3f3f3f3f_3f3f3f3f)
- & !(word + 0x25252525_25252525_25252525_25252525)
- & 0x80808080_80808080_80808080_80808080)
- >> 2);
- unsafe { Self(NonZeroU128::new_unchecked(result)) }
- }
-
- /// Converts this type to its ASCII title case equivalent in-place.
- ///
- /// First character, if is an ASCII letter 'a' to 'z' is mapped to 'A' to 'Z',
- /// other characters are unchanged.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr16;
- ///
- /// let s1: TinyStr16 = "metamorphosis".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert_eq!(s1.to_ascii_titlecase(), "Metamorphosis");
- /// ```
- pub fn to_ascii_titlecase(self) -> Self {
- let word = self.0.get().to_le();
- let mask = ((word + 0x3f3f3f3f_3f3f3f3f_3f3f3f3f_3f3f3f1f)
- & !(word + 0x25252525_25252525_25252525_25252505)
- & 0x80808080_80808080_80808080_80808080)
- >> 2;
- let result = (word | mask) & !(0x20 & mask);
- unsafe { Self(NonZeroU128::new_unchecked(u128::from_le(result))) }
- }
-
- /// Converts this type to its ASCII upper case equivalent in-place.
- ///
- /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', other characters are unchanged.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr16;
- ///
- /// let s1: TinyStr16 = "Met3amorphosis".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert_eq!(s1.to_ascii_uppercase(), "MET3AMORPHOSIS");
- /// ```
- pub fn to_ascii_uppercase(self) -> Self {
- let word = self.0.get();
- let result = word
- & !(((word + 0x1f1f1f1f_1f1f1f1f_1f1f1f1f_1f1f1f1f)
- & !(word + 0x05050505_05050505_05050505_05050505)
- & 0x80808080_80808080_80808080_80808080)
- >> 2);
- unsafe { Self(NonZeroU128::new_unchecked(result)) }
- }
-}
-
-impl fmt::Display for TinyStr16 {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", self.deref())
- }
-}
-
-impl fmt::Debug for TinyStr16 {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{:?}", self.deref())
- }
-}
-
-impl Deref for TinyStr16 {
- type Target = str;
-
- #[inline(always)]
- fn deref(&self) -> &str {
- // Again, could use #cfg to hand-roll a big-endian implementation.
- let word = self.0.get().to_le();
- let len = (16 - word.leading_zeros() / 8) as usize;
- unsafe {
- let slice = core::slice::from_raw_parts(&self.0 as *const _ as *const u8, len);
- std::str::from_utf8_unchecked(slice)
- }
- }
-}
-
-impl PartialEq<&str> for TinyStr16 {
- fn eq(&self, other: &&str) -> bool {
- self.deref() == *other
- }
-}
-
-impl PartialOrd for TinyStr16 {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for TinyStr16 {
- fn cmp(&self, other: &Self) -> Ordering {
- self.0.get().to_be().cmp(&other.0.get().to_be())
- }
-}
-
-impl FromStr for TinyStr16 {
- type Err = Error;
-
- #[inline(always)]
- fn from_str(text: &str) -> Result<Self, Self::Err> {
- Self::from_bytes(text.as_bytes())
- }
-}
-
-impl Into<u128> for TinyStr16 {
- fn into(self) -> u128 {
- self.0.get().to_le()
- }
-}
diff --git a/vendor/tinystr/src/tinystr4.rs b/vendor/tinystr/src/tinystr4.rs
deleted file mode 100644
index c63d25113..000000000
--- a/vendor/tinystr/src/tinystr4.rs
+++ /dev/null
@@ -1,299 +0,0 @@
-use std::cmp::Ordering;
-use std::convert::Into;
-use std::fmt;
-use std::num::NonZeroU32;
-use std::ops::Deref;
-use std::str::FromStr;
-
-use crate::helpers::make_4byte_bytes;
-use crate::Error;
-
-/// A tiny string that is from 1 to 4 non-NUL ASCII characters.
-///
-/// # Examples
-///
-/// ```
-/// use tinystr::TinyStr4;
-///
-/// let s1: TinyStr4 = "Test".parse()
-/// .expect("Failed to parse.");
-///
-/// assert_eq!(s1, "Test");
-/// assert!(s1.is_ascii_alphabetic());
-/// ```
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub struct TinyStr4(NonZeroU32);
-
-impl TinyStr4 {
- /// Creates a TinyStr4 from a byte slice.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr4;
- ///
- /// let s1 = TinyStr4::from_bytes("Test".as_bytes())
- /// .expect("Failed to parse.");
- ///
- /// assert_eq!(s1, "Test");
- /// ```
- #[inline(always)]
- pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
- unsafe {
- match bytes.len() {
- 1 => make_4byte_bytes(bytes, 1, 0x80).map(Self),
- 2 => make_4byte_bytes(bytes, 2, 0x8080).map(Self),
- 3 => make_4byte_bytes(bytes, 3, 0x0080_8080).map(Self),
- 4 => make_4byte_bytes(bytes, 4, 0x8080_8080).map(Self),
- _ => Err(Error::InvalidSize),
- }
- }
- }
-
- /// An unsafe constructor intended for cases where the consumer
- /// guarantees that the input is a little endian integer which
- /// is a correct representation of a `TinyStr4` string.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr4;
- ///
- /// let s1: TinyStr4 = "Test".parse()
- /// .expect("Failed to parse.");
- ///
- /// let num: u32 = s1.into();
- ///
- /// let s2 = unsafe { TinyStr4::new_unchecked(num) };
- ///
- /// assert_eq!(s1, s2);
- /// assert_eq!(s2.as_str(), "Test");
- /// ```
- ///
- /// # Safety
- ///
- /// The method does not validate the `u32` to be properly encoded
- /// value for `TinyStr4`.
- /// The value can be retrieved via `Into<u32> for TinyStr4`.
- #[inline(always)]
- pub const unsafe fn new_unchecked(text: u32) -> Self {
- Self(NonZeroU32::new_unchecked(u32::from_le(text)))
- }
-
- /// Extracts a string slice containing the entire `TinyStr4`.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr4;
- ///
- /// let s1: TinyStr4 = "Test".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert_eq!(s1.as_str(), "Test");
- /// ```
- #[inline(always)]
- pub fn as_str(&self) -> &str {
- self.deref()
- }
-
- /// Checks if the value is composed of ASCII alphabetic characters:
- ///
- /// * U+0041 'A' ..= U+005A 'Z', or
- /// * U+0061 'a' ..= U+007A 'z'.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr4;
- ///
- /// let s1: TinyStr4 = "Test".parse()
- /// .expect("Failed to parse.");
- /// let s2: TinyStr4 = "Te3t".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert!(s1.is_ascii_alphabetic());
- /// assert!(!s2.is_ascii_alphabetic());
- /// ```
- pub fn is_ascii_alphabetic(self) -> bool {
- let word = self.0.get();
- let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
- let lower = word | 0x2020_2020;
- let alpha = !(lower + 0x1f1f_1f1f) | (lower + 0x0505_0505);
- (alpha & mask) == 0
- }
-
- /// Checks if the value is composed of ASCII alphanumeric characters:
- ///
- /// * U+0041 'A' ..= U+005A 'Z', or
- /// * U+0061 'a' ..= U+007A 'z', or
- /// * U+0030 '0' ..= U+0039 '9'.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr4;
- ///
- /// let s1: TinyStr4 = "A15b".parse()
- /// .expect("Failed to parse.");
- /// let s2: TinyStr4 = "[3@w".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert!(s1.is_ascii_alphanumeric());
- /// assert!(!s2.is_ascii_alphanumeric());
- /// ```
- pub fn is_ascii_alphanumeric(self) -> bool {
- let word = self.0.get();
- let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
- let numeric = !(word + 0x5050_5050) | (word + 0x4646_4646);
- let lower = word | 0x2020_2020;
- let alpha = !(lower + 0x1f1f_1f1f) | (lower + 0x0505_0505);
- (alpha & numeric & mask) == 0
- }
-
- /// Checks if the value is composed of ASCII decimal digits:
- ///
- /// * U+0030 '0' ..= U+0039 '9'.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr4;
- ///
- /// let s1: TinyStr4 = "312".parse()
- /// .expect("Failed to parse.");
- /// let s2: TinyStr4 = "3d".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert!(s1.is_ascii_numeric());
- /// assert!(!s2.is_ascii_numeric());
- /// ```
- pub fn is_ascii_numeric(self) -> bool {
- let word = self.0.get();
- let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
- let numeric = !(word + 0x5050_5050) | (word + 0x4646_4646);
- (numeric & mask) == 0
- }
-
- /// Converts this type to its ASCII lower case equivalent in-place.
- ///
- /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', other characters are unchanged.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr4;
- ///
- /// let s1: TinyStr4 = "TeS3".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert_eq!(s1.to_ascii_lowercase(), "tes3");
- /// ```
- pub fn to_ascii_lowercase(self) -> Self {
- let word = self.0.get();
- let result = word | (((word + 0x3f3f_3f3f) & !(word + 0x2525_2525) & 0x8080_8080) >> 2);
- unsafe { Self(NonZeroU32::new_unchecked(result)) }
- }
-
- /// Converts this type to its ASCII title case equivalent in-place.
- ///
- /// First character, if is an ASCII letter 'a' to 'z' is mapped to 'A' to 'Z',
- /// other characters are unchanged.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr4;
- ///
- /// let s1: TinyStr4 = "test".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert_eq!(s1.to_ascii_titlecase(), "Test");
- /// ```
- pub fn to_ascii_titlecase(self) -> Self {
- let word = self.0.get().to_le();
- let mask = ((word + 0x3f3f_3f1f) & !(word + 0x2525_2505) & 0x8080_8080) >> 2;
- let result = (word | mask) & !(0x20 & mask);
- unsafe { Self(NonZeroU32::new_unchecked(u32::from_le(result))) }
- }
-
- /// Converts this type to its ASCII upper case equivalent in-place.
- ///
- /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', other characters are unchanged.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr4;
- ///
- /// let s1: TinyStr4 = "Tes3".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert_eq!(s1.to_ascii_uppercase(), "TES3");
- /// ```
- pub fn to_ascii_uppercase(self) -> Self {
- let word = self.0.get();
- let result = word & !(((word + 0x1f1f_1f1f) & !(word + 0x0505_0505) & 0x8080_8080) >> 2);
- unsafe { Self(NonZeroU32::new_unchecked(result)) }
- }
-}
-
-impl fmt::Display for TinyStr4 {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", self.deref())
- }
-}
-
-impl fmt::Debug for TinyStr4 {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{:?}", self.deref())
- }
-}
-
-impl Deref for TinyStr4 {
- type Target = str;
-
- #[inline(always)]
- fn deref(&self) -> &str {
- // Again, could use #cfg to hand-roll a big-endian implementation.
- let word = self.0.get().to_le();
- let len = (4 - word.leading_zeros() / 8) as usize;
- unsafe {
- let slice = core::slice::from_raw_parts(&self.0 as *const _ as *const u8, len);
- std::str::from_utf8_unchecked(slice)
- }
- }
-}
-
-impl PartialEq<&str> for TinyStr4 {
- fn eq(&self, other: &&str) -> bool {
- self.deref() == *other
- }
-}
-
-impl PartialOrd for TinyStr4 {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for TinyStr4 {
- fn cmp(&self, other: &Self) -> Ordering {
- self.0.get().to_be().cmp(&other.0.get().to_be())
- }
-}
-
-impl FromStr for TinyStr4 {
- type Err = Error;
-
- #[inline(always)]
- fn from_str(text: &str) -> Result<Self, Self::Err> {
- Self::from_bytes(text.as_bytes())
- }
-}
-
-impl Into<u32> for TinyStr4 {
- fn into(self) -> u32 {
- self.0.get().to_le()
- }
-}
diff --git a/vendor/tinystr/src/tinystr8.rs b/vendor/tinystr/src/tinystr8.rs
deleted file mode 100644
index e121c519a..000000000
--- a/vendor/tinystr/src/tinystr8.rs
+++ /dev/null
@@ -1,319 +0,0 @@
-use std::cmp::Ordering;
-use std::convert::Into;
-use std::fmt;
-use std::num::NonZeroU64;
-use std::ops::Deref;
-use std::ptr::copy_nonoverlapping;
-use std::str::FromStr;
-
-use crate::Error;
-
-/// A tiny string that is from 1 to 8 non-NUL ASCII characters.
-///
-/// # Examples
-///
-/// ```
-/// use tinystr::TinyStr8;
-///
-/// let s1: TinyStr8 = "Testing".parse()
-/// .expect("Failed to parse.");
-///
-/// assert_eq!(s1, "Testing");
-/// assert!(s1.is_ascii_alphabetic());
-/// ```
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub struct TinyStr8(NonZeroU64);
-
-impl TinyStr8 {
- /// Creates a TinyStr8 from a byte slice.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr8;
- ///
- /// let s1 = TinyStr8::from_bytes("Testing".as_bytes())
- /// .expect("Failed to parse.");
- ///
- /// assert_eq!(s1, "Testing");
- /// ```
- #[inline(always)]
- pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
- let len = bytes.len();
- if len < 1 || len > 8 {
- return Err(Error::InvalidSize);
- }
- unsafe {
- let mut word: u64 = 0;
- copy_nonoverlapping(bytes.as_ptr(), &mut word as *mut u64 as *mut u8, len);
- let mask = 0x80808080_80808080u64 >> (8 * (8 - len));
- // TODO: could do this with #cfg(target_endian), but this is clearer and
- // more confidence-inspiring.
- let mask = u64::from_le(mask);
- if (word & mask) != 0 {
- return Err(Error::NonAscii);
- }
- if ((mask - word) & mask) != 0 {
- return Err(Error::InvalidNull);
- }
- Ok(Self(NonZeroU64::new_unchecked(word)))
- }
- }
-
- /// An unsafe constructor intended for cases where the consumer
- /// guarantees that the input is a little endian integer which
- /// is a correct representation of a `TinyStr8` string.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr8;
- ///
- /// let s1: TinyStr8 = "Testing".parse()
- /// .expect("Failed to parse.");
- ///
- /// let num: u64 = s1.into();
- ///
- /// let s2 = unsafe { TinyStr8::new_unchecked(num) };
- ///
- /// assert_eq!(s1, s2);
- /// assert_eq!(s2.as_str(), "Testing");
- /// ```
- ///
- /// # Safety
- ///
- /// The method does not validate the `u64` to be properly encoded
- /// value for `TinyStr8`.
- /// The value can be retrieved via `Into<u64> for TinyStr8`.
- #[inline(always)]
- pub const unsafe fn new_unchecked(text: u64) -> Self {
- Self(NonZeroU64::new_unchecked(u64::from_le(text)))
- }
-
- /// Extracts a string slice containing the entire `TinyStr8`.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr8;
- ///
- /// let s1: TinyStr8 = "Testing".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert_eq!(s1.as_str(), "Testing");
- /// ```
- #[inline(always)]
- pub fn as_str(&self) -> &str {
- self.deref()
- }
-
- /// Checks if the value is composed of ASCII alphabetic characters:
- ///
- /// * U+0041 'A' ..= U+005A 'Z', or
- /// * U+0061 'a' ..= U+007A 'z'.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr8;
- ///
- /// let s1: TinyStr8 = "Testing".parse()
- /// .expect("Failed to parse.");
- /// let s2: TinyStr8 = "Te3ting".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert!(s1.is_ascii_alphabetic());
- /// assert!(!s2.is_ascii_alphabetic());
- /// ```
- pub fn is_ascii_alphabetic(self) -> bool {
- let word = self.0.get();
- let mask = (word + 0x7f7f7f7f_7f7f7f7f) & 0x80808080_80808080;
- let lower = word | 0x20202020_20202020;
- let alpha = !(lower + 0x1f1f1f1f_1f1f1f1f) | (lower + 0x05050505_05050505);
- (alpha & mask) == 0
- }
-
- /// Checks if the value is composed of ASCII alphanumeric characters:
- ///
- /// * U+0041 'A' ..= U+005A 'Z', or
- /// * U+0061 'a' ..= U+007A 'z', or
- /// * U+0030 '0' ..= U+0039 '9'.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr8;
- ///
- /// let s1: TinyStr8 = "A15bing".parse()
- /// .expect("Failed to parse.");
- /// let s2: TinyStr8 = "[3@wing".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert!(s1.is_ascii_alphanumeric());
- /// assert!(!s2.is_ascii_alphanumeric());
- /// ```
- pub fn is_ascii_alphanumeric(self) -> bool {
- let word = self.0.get();
- let mask = (word + 0x7f7f7f7f_7f7f7f7f) & 0x80808080_80808080;
- let numeric = !(word + 0x50505050_50505050) | (word + 0x46464646_46464646);
- let lower = word | 0x20202020_20202020;
- let alpha = !(lower + 0x1f1f1f1f_1f1f1f1f) | (lower + 0x05050505_05050505);
- (alpha & numeric & mask) == 0
- }
-
- /// Checks if the value is composed of ASCII decimal digits:
- ///
- /// * U+0030 '0' ..= U+0039 '9'.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr8;
- ///
- /// let s1: TinyStr8 = "3121029".parse()
- /// .expect("Failed to parse.");
- /// let s2: TinyStr8 = "3d212d".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert!(s1.is_ascii_numeric());
- /// assert!(!s2.is_ascii_numeric());
- /// ```
- pub fn is_ascii_numeric(self) -> bool {
- let word = self.0.get();
- let mask = (word + 0x7f7f7f7f_7f7f7f7f) & 0x80808080_80808080;
- let numeric = !(word + 0x50505050_50505050) | (word + 0x46464646_46464646);
- (numeric & mask) == 0
- }
-
- /// Converts this type to its ASCII lower case equivalent in-place.
- ///
- /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', other characters are unchanged.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr8;
- ///
- /// let s1: TinyStr8 = "TeS3ing".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert_eq!(s1.to_ascii_lowercase(), "tes3ing");
- /// ```
- pub fn to_ascii_lowercase(self) -> Self {
- let word = self.0.get();
- let result = word
- | (((word + 0x3f3f3f3f_3f3f3f3f)
- & !(word + 0x25252525_25252525)
- & 0x80808080_80808080)
- >> 2);
- unsafe { Self(NonZeroU64::new_unchecked(result)) }
- }
-
- /// Converts this type to its ASCII title case equivalent in-place.
- ///
- /// First character, if is an ASCII letter 'a' to 'z' is mapped to 'A' to 'Z',
- /// other characters are unchanged.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr8;
- ///
- /// let s1: TinyStr8 = "testing".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert_eq!(s1.to_ascii_titlecase(), "Testing");
- /// ```
- pub fn to_ascii_titlecase(self) -> Self {
- let word = self.0.get().to_le();
- let mask =
- ((word + 0x3f3f3f3f_3f3f3f1f) & !(word + 0x25252525_25252505) & 0x80808080_80808080)
- >> 2;
- let result = (word | mask) & !(0x20 & mask);
- unsafe { Self(NonZeroU64::new_unchecked(u64::from_le(result))) }
- }
-
- /// Converts this type to its ASCII upper case equivalent in-place.
- ///
- /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', other characters are unchanged.
- ///
- /// # Examples
- ///
- /// ```
- /// use tinystr::TinyStr8;
- ///
- /// let s1: TinyStr8 = "Tes3ing".parse()
- /// .expect("Failed to parse.");
- ///
- /// assert_eq!(s1.to_ascii_uppercase(), "TES3ING");
- /// ```
- pub fn to_ascii_uppercase(self) -> Self {
- let word = self.0.get();
- let result = word
- & !(((word + 0x1f1f1f1f_1f1f1f1f)
- & !(word + 0x05050505_05050505)
- & 0x80808080_80808080)
- >> 2);
- unsafe { Self(NonZeroU64::new_unchecked(result)) }
- }
-}
-
-impl fmt::Display for TinyStr8 {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", self.deref())
- }
-}
-
-impl fmt::Debug for TinyStr8 {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{:?}", self.deref())
- }
-}
-
-impl Deref for TinyStr8 {
- type Target = str;
-
- #[inline(always)]
- fn deref(&self) -> &str {
- // Again, could use #cfg to hand-roll a big-endian implementation.
- let word = self.0.get().to_le();
- let len = (8 - word.leading_zeros() / 8) as usize;
- unsafe {
- let slice = core::slice::from_raw_parts(&self.0 as *const _ as *const u8, len);
- std::str::from_utf8_unchecked(slice)
- }
- }
-}
-
-impl PartialEq<&str> for TinyStr8 {
- fn eq(&self, other: &&str) -> bool {
- self.deref() == *other
- }
-}
-
-impl PartialOrd for TinyStr8 {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for TinyStr8 {
- fn cmp(&self, other: &Self) -> Ordering {
- self.0.get().to_be().cmp(&other.0.get().to_be())
- }
-}
-
-impl FromStr for TinyStr8 {
- type Err = Error;
-
- #[inline(always)]
- fn from_str(text: &str) -> Result<Self, Self::Err> {
- TinyStr8::from_bytes(text.as_bytes())
- }
-}
-
-impl Into<u64> for TinyStr8 {
- fn into(self) -> u64 {
- self.0.get().to_le()
- }
-}
diff --git a/vendor/tinystr/src/tinystrauto.rs b/vendor/tinystr/src/tinystrauto.rs
deleted file mode 100644
index 9e2387cc1..000000000
--- a/vendor/tinystr/src/tinystrauto.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-use std::fmt;
-use std::ops::Deref;
-use std::str::FromStr;
-
-use crate::helpers::String;
-use crate::Error;
-use crate::TinyStr16;
-
-/// An ASCII string that is tiny when <= 16 chars and a String otherwise.
-///
-/// # Examples
-///
-/// ```
-/// use tinystr::TinyStrAuto;
-///
-/// let s1: TinyStrAuto = "Testing".parse()
-/// .expect("Failed to parse.");
-///
-/// assert_eq!(s1, "Testing");
-/// ```
-#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
-pub enum TinyStrAuto {
- /// Up to 16 characters stored on the stack.
- Tiny(TinyStr16),
- /// 17 or more characters stored on the heap.
- Heap(String),
-}
-
-impl fmt::Display for TinyStrAuto {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.deref().fmt(f)
- }
-}
-
-impl Deref for TinyStrAuto {
- type Target = str;
-
- fn deref(&self) -> &str {
- use TinyStrAuto::*;
- match self {
- Tiny(value) => value.deref(),
- Heap(value) => value.deref(),
- }
- }
-}
-
-impl PartialEq<&str> for TinyStrAuto {
- fn eq(&self, other: &&str) -> bool {
- self.deref() == *other
- }
-}
-
-impl FromStr for TinyStrAuto {
- type Err = Error;
-
- fn from_str(text: &str) -> Result<Self, Self::Err> {
- if text.len() <= 16 {
- match TinyStr16::from_str(text) {
- Ok(result) => Ok(TinyStrAuto::Tiny(result)),
- Err(err) => Err(err),
- }
- } else {
- if !text.is_ascii() {
- return Err(Error::NonAscii);
- }
- match String::from_str(text) {
- Ok(result) => Ok(TinyStrAuto::Heap(result)),
- Err(_) => unreachable!(),
- }
- }
- }
-}
diff --git a/vendor/tinystr/src/ule.rs b/vendor/tinystr/src/ule.rs
new file mode 100644
index 000000000..0fa212095
--- /dev/null
+++ b/vendor/tinystr/src/ule.rs
@@ -0,0 +1,76 @@
+// This file is part of ICU4X. For terms of use, please see the file
+// called LICENSE at the top level of the ICU4X source tree
+// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
+
+use crate::TinyAsciiStr;
+use zerovec::maps::ZeroMapKV;
+use zerovec::ule::*;
+use zerovec::{ZeroSlice, ZeroVec};
+
+// Safety (based on the safety checklist on the ULE trait):
+// 1. CharULE does not include any uninitialized or padding bytes.
+// (achieved by `#[repr(transparent)]` on a type that satisfies this invariant)
+// 2. CharULE is aligned to 1 byte.
+// (achieved by `#[repr(transparent)]` on a type that satisfies this invariant)
+// 3. The impl of validate_byte_slice() returns an error if any byte is not valid.
+// 4. The impl of validate_byte_slice() returns an error if there are extra bytes.
+// 5. The other ULE methods use the default impl.
+// 6. CharULE byte equality is semantic equality
+unsafe impl<const N: usize> ULE for TinyAsciiStr<N> {
+ #[inline]
+ fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> {
+ if bytes.len() % N != 0 {
+ return Err(ZeroVecError::length::<Self>(bytes.len()));
+ }
+ // Validate the bytes
+ for chunk in bytes.chunks_exact(N) {
+ let _ = TinyAsciiStr::<N>::from_bytes_inner(chunk, 0, N, true)
+ .map_err(|_| ZeroVecError::parse::<Self>())?;
+ }
+ Ok(())
+ }
+}
+
+impl<const N: usize> AsULE for TinyAsciiStr<N> {
+ type ULE = Self;
+
+ #[inline]
+ fn to_unaligned(self) -> Self::ULE {
+ self
+ }
+
+ #[inline]
+ fn from_unaligned(unaligned: Self::ULE) -> Self {
+ unaligned
+ }
+}
+
+impl<'a, const N: usize> ZeroMapKV<'a> for TinyAsciiStr<N> {
+ type Container = ZeroVec<'a, TinyAsciiStr<N>>;
+ type Slice = ZeroSlice<TinyAsciiStr<N>>;
+ type GetType = TinyAsciiStr<N>;
+ type OwnedType = TinyAsciiStr<N>;
+}
+
+#[cfg(test)]
+mod test {
+ use crate::*;
+ use zerovec::*;
+
+ #[test]
+ fn test_zerovec() {
+ let mut vec = ZeroVec::<TinyAsciiStr<7>>::new();
+
+ vec.with_mut(|v| v.push("foobar".parse().unwrap()));
+ vec.with_mut(|v| v.push("baz".parse().unwrap()));
+ vec.with_mut(|v| v.push("quux".parse().unwrap()));
+
+ let bytes = vec.as_bytes();
+
+ let vec: ZeroVec<TinyAsciiStr<7>> = ZeroVec::parse_byte_slice(bytes).unwrap();
+
+ assert_eq!(&*vec.get(0).unwrap(), "foobar");
+ assert_eq!(&*vec.get(1).unwrap(), "baz");
+ assert_eq!(&*vec.get(2).unwrap(), "quux");
+ }
+}