Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  ascii.rs   Sprache: unbekannt

 
// 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)
    }

    /// Creates a `TinyAsciiStr<N>` from a byte slice, replacing invalid bytes.
    ///
    /// Null and non-ASCII bytes (i.e. those outside the range `0x01..=0x7F`)
    /// will be replaced with the '?' character.
    ///
    /// The input slice will be truncated if its length exceeds `N`.
    pub const fn from_bytes_lossy(bytes: &[u8]) -> Self {
        const QUESTION: u8 = b'?';
        let mut out = [0; N];
        let mut i = 0;
        // Ord is not available in const, so no `.min(N)`
        let len = if bytes.len() > N { N } else { bytes.len() };

        // Indexing is protected by the len check above
        #[allow(clippy::indexing_slicing)]
        while i < len {
            let b = bytes[i];
            if b > 0 && b < 0x80 {
                out[i] = b;
            } else {
                out[i] = QUESTION;
            }
            i += 1;
        }

        Self {
            // SAFETY: `out` only contains ASCII bytes and has same size as `self.bytes`
            bytes: unsafe { AsciiByte::to_ascii_byte_array(&out) },
        }
    }

    /// 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] {
        // 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::slice::from_raw_parts(self.bytes.as_slice().as_ptr() as *const u8, self.len())
        }
    }

    #[inline]
    #[must_use]
    pub const fn all_bytes(&self) -> &[u8; N] {
        // SAFETY: `self.bytes` has same size as [u8; N]
        unsafe { &*(self.bytes.as_ptr() as *const [u8; N]) }
    }

    #[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::<u8, AsciiByte>(
                        ($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::<u8, AsciiByte>(($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, Self::Err> {
        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; 26] = [
        "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
            .into_iter()
            .map(str::to_owned)
            .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).as_str().to_owned(),
            )
        }
        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).as_str().to_owned(),
            )
        }
        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).as_str().to_owned(),
            )
        }
        check::<2>();
        check::<3>();
        check::<4>();
        check::<5>();
        check::<8>();
        check::<16>();
    }

    #[test]
    fn lossy_constructor() {
        assert_eq!(TinyAsciiStr::<4>::from_bytes_lossy(b"").as_str(), "");
        assert_eq!(
            TinyAsciiStr::<4>::from_bytes_lossy(b"oh\0o").as_str(),
            "oh?o"
        );
        assert_eq!(TinyAsciiStr::<4>::from_bytes_lossy(b"\0").as_str(), "?");
        assert_eq!(
            TinyAsciiStr::<4>::from_bytes_lossy(b"toolong").as_str(),
            "tool"
        );
        assert_eq!(
            TinyAsciiStr::<4>::from_bytes_lossy(&[b'a', 0x80, 0xFF, b'1']).as_str(),
            "a??1"
        );
    }
}

[ Dauer der Verarbeitung: 0.35 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge