ascii_domain

Domains whose labels are only ASCII.
git clone https://git.philomathiclife.com/repos/ascii_domain
Log | Files | Refs | README

char_set.rs (22959B)


      1 use core::{
      2     error::Error,
      3     fmt::{self, Display, Formatter},
      4     str,
      5 };
      6 /// Error returned from [`AllowedAscii::try_from_unique_ascii`].
      7 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
      8 pub enum AsciiErr {
      9     /// Since `AllowedAscii` only allows unique ASCII characters and doesn't allow `b'.'`, the maximum count is
     10     /// 127. This variant is returned when the count exceeds that.
     11     CountTooLarge(usize),
     12     /// The contained `u8` is not valid ASCII (i.e., it is strictly greater than 127).
     13     InvalidByte(u8),
     14     /// `b'.'` was in the allowed ASCII. It is the only ASCII value not allowed since it is always used
     15     /// as a [`crate::dom::Label`] separator.
     16     Contains46,
     17     /// The contained ASCII appeared more than once.
     18     Duplicate(u8),
     19 }
     20 impl Display for AsciiErr {
     21     #[inline]
     22     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
     23         match *self {
     24             Self::CountTooLarge(byt) => {
     25                 write!(f, "the allowed ASCII had {byt} values, but 127 is the max")
     26             }
     27             Self::InvalidByte(byt) => {
     28                 write!(f, "allowed ASCII was passed the invalid byte value {byt}")
     29             }
     30             Self::Contains46 => f.write_str("allowed ASCII contains '.'"),
     31             Self::Duplicate(byt) => {
     32                 let input = [byt];
     33                 if let Ok(val) = str::from_utf8(input.as_slice()) {
     34                     write!(f, "allowed ASCII has the duplicate value '{val}'")
     35                 } else {
     36                     write!(f, "allowed ASCII has the invalid value '{byt}'")
     37                 }
     38             }
     39         }
     40     }
     41 }
     42 impl Error for AsciiErr {}
     43 /// Container of the ASCII `u8`s that are allowed to appear in a [`crate::dom::Label`].
     44 ///
     45 /// Note that while
     46 /// [`crate::dom::Domain`] treats ASCII uppercase letters as lowercase, it still depends on such `u8`s being
     47 /// included. For example if `b'A'` is not included, then `b'A'` is not allowed even if `b'a'` is included.
     48 ///
     49 /// It is _highly_ unlikely that non-printable ASCII nor `b'\\'` should be used since such ASCII would almost
     50 /// certainly require being escaped.
     51 pub struct AllowedAscii<T> {
     52     /// The allowed ASCII `u8`s.
     53     allowed: T,
     54 }
     55 impl<T> AllowedAscii<T> {
     56     /// Returns a reference to the contained value.
     57     ///
     58     /// # Example
     59     ///
     60     /// ```
     61     /// use ascii_domain::char_set;
     62     /// assert!(char_set::ASCII_LETTERS.as_inner().len() == 52);
     63     /// ```
     64     #[inline]
     65     pub const fn as_inner(&self) -> &T {
     66         &self.allowed
     67     }
     68     /// Returns the contained value consuming `self`.
     69     ///
     70     /// # Example
     71     ///
     72     /// ```
     73     /// use ascii_domain::char_set;
     74     /// assert!(char_set::ASCII_LETTERS.into_inner().len() == 52);
     75     /// ```
     76     #[inline]
     77     pub fn into_inner(self) -> T {
     78         self.allowed
     79     }
     80 }
     81 impl<T: AsRef<[u8]>> AllowedAscii<T> {
     82     /// Returns `true` iff `val` is an allowed ASCII value in a [`crate::dom::Label`].
     83     ///
     84     /// # Example
     85     ///
     86     /// ```
     87     /// use ascii_domain::char_set;
     88     /// assert!(char_set::ASCII_LETTERS.contains(b'a'));
     89     /// ```
     90     #[inline]
     91     #[must_use]
     92     pub fn contains(&self, val: u8) -> bool {
     93         // We sort `allowed` in `try_from_unique_ascii`, so `binary_search` is fine.
     94         self.allowed.as_ref().binary_search(&val).is_ok()
     95     }
     96     /// Returns the number of allowed ASCII characters.
     97     ///
     98     /// # Example
     99     ///
    100     /// ```
    101     /// use ascii_domain::char_set;
    102     /// assert!(char_set::ASCII_LETTERS.len() == 52);
    103     /// ```
    104     #[expect(
    105         clippy::as_conversions,
    106         clippy::cast_possible_truncation,
    107         reason = "comment justifies its correctness"
    108     )]
    109     #[inline]
    110     #[must_use]
    111     pub fn len(&self) -> u8 {
    112         // We enforce only unique non `b'.'` ASCII in `try_from_unique_ascii` which among other things means
    113         // the max count is 127 so truncation will not occur
    114         self.allowed.as_ref().len() as u8
    115     }
    116     /// Returns `true` iff `self` does not contain any ASCII.
    117     ///
    118     /// # Examples
    119     ///
    120     /// ```
    121     /// use ascii_domain::char_set::{self, AllowedAscii};
    122     /// assert!(!char_set::ASCII_LETTERS.is_empty());
    123     /// assert!(AllowedAscii::try_from_unique_ascii([]).unwrap().is_empty());
    124     /// ```
    125     #[inline]
    126     #[must_use]
    127     pub fn is_empty(&self) -> bool {
    128         self.allowed.as_ref().is_empty()
    129     }
    130 }
    131 impl<T: AsMut<[u8]>> AllowedAscii<T> {
    132     /// `allowed` must contain unique ASCII `u8`s. Note it is likely `allowed` should be a subset of
    133     /// [`PRINTABLE_ASCII`] since any other ASCII would likely require some form of escape character logic.
    134     /// Additionally, it is likely `allowed.as_mut().len()` should be greater than 0; otherwise the returned
    135     /// `AllowedAscii` will always cause [`crate::dom::Domain::try_from_bytes`] to error since `Domain` requires
    136     /// at least one non-root [`crate::dom::Label`].
    137     ///
    138     /// `allowed` is mutated such that `allowed.as_mut()` is sorted in order.
    139     ///
    140     /// # Errors
    141     ///
    142     /// Returns `AsciiError` iff `allowed` does not contain a set of unique ASCII `u8`s or contains `b'.'`.
    143     ///
    144     /// # Examples
    145     ///
    146     /// ```
    147     /// use ascii_domain::char_set::{AllowedAscii, AsciiErr};
    148     /// assert!(AllowedAscii::try_from_unique_ascii(b"asdfghjkl".to_owned()).map_or(false, |ascii| ascii.contains(b'a') && !ascii.contains(b'A')));
    149     /// assert!(AllowedAscii::try_from_unique_ascii(b"aa".to_owned()).map_or_else(|err| err == AsciiErr::Duplicate(b'a'), |_| false));
    150     /// assert!(AllowedAscii::try_from_unique_ascii([255]).map_or_else(|err| err == AsciiErr::InvalidByte(255), |_| false));
    151     /// assert!(AllowedAscii::try_from_unique_ascii([0; 128]).map_or_else(|err| err == AsciiErr::CountTooLarge(128), |_| false));
    152     /// assert!(AllowedAscii::try_from_unique_ascii([b'.']).map_or_else(|err| err == AsciiErr::Contains46, |_| false));
    153     /// ```
    154     #[inline]
    155     pub fn try_from_unique_ascii(mut allowed: T) -> Result<Self, AsciiErr> {
    156         let bytes = allowed.as_mut();
    157         if bytes.len() > 127 {
    158             Err(AsciiErr::CountTooLarge(bytes.len()))
    159         } else {
    160             bytes.sort_unstable();
    161             // Since `bytes` is sorted, we simply have to check the last value to determine if valid ASCII was
    162             // provided.
    163             if let Some(byt) = bytes.last() {
    164                 let b = *byt;
    165                 if b > 127 {
    166                     return Err(AsciiErr::InvalidByte(b));
    167                 }
    168             }
    169             bytes
    170                 .iter()
    171                 // 255 is not valid ASCII, so we can use it as an initializer.
    172                 .try_fold(255, |prev, b| {
    173                     let byt = *b;
    174                     if byt == b'.' {
    175                         Err(AsciiErr::Contains46)
    176                     } else if prev == byt {
    177                         Err(AsciiErr::Duplicate(prev))
    178                     } else {
    179                         Ok(byt)
    180                     }
    181                 })
    182                 .map(|_| Self { allowed })
    183         }
    184     }
    185 }
    186 /// Printable ASCII that should not need to be "escaped".
    187 ///
    188 /// That is to say printable ASCII excluding space (i.e., 32), dot (i.e. 46), and backslash (i.e., 92).
    189 /// This returns all `u8`s inclusively between 33 and 126 except 46 and 92.
    190 pub const PRINTABLE_ASCII: AllowedAscii<[u8; 92]> = AllowedAscii {
    191     allowed: [
    192         b'!', b'"', b'#', b'$', b'%', b'&', b'\'', b'(', b')', b'*', b'+', b',', b'-', b'/', b'0',
    193         b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b':', b';', b'<', b'=', b'>', b'?',
    194         b'@', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N',
    195         b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'[', b']', b'^',
    196         b'_', b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm',
    197         b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', b'{', b'|',
    198         b'}', b'~',
    199     ],
    200 };
    201 /// ASCII allowed in [RFC 5322 `atext`](https://www.rfc-editor.org/rfc/rfc5322#section-3.2.3).
    202 /// This contains the following `u8`s:
    203 ///
    204 /// 33, 35–39, 42–43, 45, 47–57, 61, 63, 65–90, and 94–126.
    205 pub const RFC5322_ATEXT: AllowedAscii<[u8; 81]> = AllowedAscii {
    206     allowed: [
    207         b'!', b'#', b'$', b'%', b'&', b'\'', b'*', b'+', b'-', b'/', b'0', b'1', b'2', b'3', b'4',
    208         b'5', b'6', b'7', b'8', b'9', b'=', b'?', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H',
    209         b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W',
    210         b'X', b'Y', b'Z', b'^', b'_', b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i',
    211         b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x',
    212         b'y', b'z', b'{', b'|', b'}', b'~',
    213     ],
    214 };
    215 /// ASCII allowed in a domain by [Firefox](https://www.mozilla.org/en-US/firefox/)
    216 /// as of 2023-09-03T20:50+00:00.
    217 /// This contains the following `u8`s:
    218 ///
    219 /// 33, 36, 38–41, 43–45, 48–57, 59, 61, 65–90, 95–123, and 125–126.
    220 pub const ASCII_FIREFOX: AllowedAscii<[u8; 78]> = AllowedAscii {
    221     allowed: [
    222         b'!', b'$', b'&', b'\'', b'(', b')', b'+', b',', b'-', b'0', b'1', b'2', b'3', b'4', b'5',
    223         b'6', b'7', b'8', b'9', b';', b'=', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I',
    224         b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X',
    225         b'Y', b'Z', b'_', b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k',
    226         b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z',
    227         b'{', b'}', b'~',
    228     ],
    229 };
    230 /// ASCII hyphen, digits, and letters.
    231 /// This contains 45 and all `u8`s inclusively between 48 and 57, 65 and 90, and 97 and 122.
    232 pub const ASCII_HYPHEN_DIGITS_LETTERS: AllowedAscii<[u8; 63]> = AllowedAscii {
    233     allowed: [
    234         b'-', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D',
    235         b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S',
    236         b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h',
    237         b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w',
    238         b'x', b'y', b'z',
    239     ],
    240 };
    241 /// ASCII digits and letters.
    242 /// This contains all `u8`s inclusively between 48 and 57, 65 and 90, and 97 and 122.
    243 pub const ASCII_DIGITS_LETTERS: AllowedAscii<[u8; 62]> = AllowedAscii {
    244     allowed: [
    245         b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E',
    246         b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T',
    247         b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i',
    248         b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x',
    249         b'y', b'z',
    250     ],
    251 };
    252 /// ASCII letters.
    253 /// This contains all `u8`s inclusively between 65 and 90 and 97 and 122.
    254 pub const ASCII_LETTERS: AllowedAscii<[u8; 52]> = AllowedAscii {
    255     allowed: [
    256         b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O',
    257         b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd',
    258         b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's',
    259         b't', b'u', b'v', b'w', b'x', b'y', b'z',
    260     ],
    261 };
    262 /// ASCII hyphen, digits, and uppercase letters.
    263 /// This contains 45 and all `u8`s inclusively between 48 and 57 and 65 and 90.
    264 pub const ASCII_HYPHEN_DIGITS_UPPERCASE: AllowedAscii<[u8; 37]> = AllowedAscii {
    265     allowed: [
    266         b'-', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D',
    267         b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S',
    268         b'T', b'U', b'V', b'W', b'X', b'Y', b'Z',
    269     ],
    270 };
    271 /// ASCII hyphen, digits, and lowercase letters.
    272 /// This contains 45 and all `u8`s inclusively between 48 and 57 and 97 and 122.
    273 pub const ASCII_HYPHEN_DIGITS_LOWERCASE: AllowedAscii<[u8; 37]> = AllowedAscii {
    274     allowed: [
    275         b'-', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd',
    276         b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's',
    277         b't', b'u', b'v', b'w', b'x', b'y', b'z',
    278     ],
    279 };
    280 /// ASCII digits and uppercase letters.
    281 /// This contains all `u8`s inclusively between 48 and 57 and 65 and 90.
    282 pub const ASCII_DIGITS_UPPERCASE: AllowedAscii<[u8; 36]> = AllowedAscii {
    283     allowed: [
    284         b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E',
    285         b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T',
    286         b'U', b'V', b'W', b'X', b'Y', b'Z',
    287     ],
    288 };
    289 /// ASCII digits and lowercase letters.
    290 /// This contains all `u8`s inclusively between 48 and 57 and 97 and 122.
    291 pub const ASCII_DIGITS_LOWERCASE: AllowedAscii<[u8; 36]> = AllowedAscii {
    292     allowed: [
    293         b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e',
    294         b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't',
    295         b'u', b'v', b'w', b'x', b'y', b'z',
    296     ],
    297 };
    298 /// ASCII uppercase letters.
    299 /// This contains all `u8`s inclusively between 65 and 90.
    300 pub const ASCII_UPPERCASE: AllowedAscii<[u8; 26]> = AllowedAscii {
    301     allowed: [
    302         b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O',
    303         b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z',
    304     ],
    305 };
    306 /// ASCII lowercase letters.
    307 /// This contains all `u8`s inclusively between 97 and 122.
    308 pub const ASCII_LOWERCASE: AllowedAscii<[u8; 26]> = AllowedAscii {
    309     allowed: [
    310         b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o',
    311         b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z',
    312     ],
    313 };
    314 /// ASCII digits.
    315 /// This contains all `u8`s inclusively between 48 and 57.
    316 pub const ASCII_DIGITS: AllowedAscii<[u8; 10]> = AllowedAscii {
    317     allowed: [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9'],
    318 };
    319 /// ASCII that is not a [forbidden domain code point](https://url.spec.whatwg.org/#forbidden-domain-code-point).
    320 ///
    321 /// This contains the following `u8`s:
    322 ///
    323 /// 33, 34, 36, 38–45, 48–57, 59, 61, 65–90, 95–123, and 125–126
    324 pub const WHATWG_VALID_DOMAIN_CODE_POINTS: AllowedAscii<[u8; 80]> = AllowedAscii {
    325     allowed: [
    326         b'!', b'"', b'$', b'&', b'\'', b'(', b')', b'*', b'+', b',', b'-', b'0', b'1', b'2', b'3',
    327         b'4', b'5', b'6', b'7', b'8', b'9', b';', b'=', b'A', b'B', b'C', b'D', b'E', b'F', b'G',
    328         b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V',
    329         b'W', b'X', b'Y', b'Z', b'_', b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i',
    330         b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x',
    331         b'y', b'z', b'{', b'}', b'~',
    332     ],
    333 };
    334 #[cfg(test)]
    335 mod tests {
    336     extern crate alloc;
    337     use crate::char_set::{
    338         ASCII_DIGITS, ASCII_DIGITS_LETTERS, ASCII_DIGITS_LOWERCASE, ASCII_DIGITS_UPPERCASE,
    339         ASCII_FIREFOX, ASCII_HYPHEN_DIGITS_LETTERS, ASCII_HYPHEN_DIGITS_LOWERCASE,
    340         ASCII_HYPHEN_DIGITS_UPPERCASE, ASCII_LETTERS, ASCII_LOWERCASE, ASCII_UPPERCASE,
    341         AllowedAscii, AsciiErr, PRINTABLE_ASCII, RFC5322_ATEXT, WHATWG_VALID_DOMAIN_CODE_POINTS,
    342     };
    343     use alloc::{borrow::ToOwned, vec::Vec};
    344     #[test]
    345     fn try_from() {
    346         // Empty is allowed.
    347         assert!(AllowedAscii::try_from_unique_ascii([]).is_ok());
    348         // Duplicates are not allowed.
    349         assert!(
    350             AllowedAscii::try_from_unique_ascii(b"aba".to_owned())
    351                 .map_or_else(|e| e == AsciiErr::Duplicate(b'a'), |_| false)
    352         );
    353         // `b'.'` is not allowed.
    354         assert!(
    355             AllowedAscii::try_from_unique_ascii(b"a.c".to_owned())
    356                 .map_or_else(|e| e == AsciiErr::Contains46, |_| false)
    357         );
    358         // At most 127 bytes are allowed.
    359         assert!(
    360             AllowedAscii::try_from_unique_ascii([0; 128])
    361                 .map_or_else(|e| e == AsciiErr::CountTooLarge(128), |_| false)
    362         );
    363         let mut all_ascii = (0..b'.').collect::<Vec<u8>>();
    364         let next = b'.' + 1;
    365         all_ascii.extend(next..=127);
    366         assert!(AllowedAscii::try_from_unique_ascii(all_ascii).is_ok());
    367         // Only ASCII is allowed.
    368         assert!(
    369             AllowedAscii::try_from_unique_ascii([255])
    370                 .map_or_else(|e| e == AsciiErr::InvalidByte(255), |_| false)
    371         );
    372         assert!(
    373             AllowedAscii::try_from_unique_ascii(b"abcdef".to_owned()).map_or(false, |bytes| bytes
    374                 .contains(b'a')
    375                 && bytes.contains(b'b')
    376                 && bytes.contains(b'c')
    377                 && bytes.contains(b'd')
    378                 && bytes.contains(b'e')
    379                 && bytes.contains(b'f'))
    380         );
    381     }
    382     #[test]
    383     fn test_consts() {
    384         let letters = ASCII_LETTERS;
    385         assert!(letters.len() == 52);
    386         for i in b'A'..=b'Z' {
    387             assert!(letters.contains(i));
    388         }
    389         for i in b'a'..=b'z' {
    390             assert!(letters.contains(i));
    391         }
    392         let digits = ASCII_DIGITS;
    393         assert!(digits.len() == 10);
    394         for i in b'0'..=b'9' {
    395             assert!(digits.contains(i));
    396         }
    397         let lower = ASCII_LOWERCASE;
    398         assert!(lower.len() == 26);
    399         for i in b'a'..=b'z' {
    400             assert!(lower.contains(i));
    401         }
    402         let upper = ASCII_UPPERCASE;
    403         assert!(upper.len() == 26);
    404         for i in b'A'..=b'Z' {
    405             assert!(upper.contains(i));
    406         }
    407         let dig_let = ASCII_DIGITS_LETTERS;
    408         assert!(dig_let.len() == 62);
    409         for i in b'a'..=b'z' {
    410             assert!(dig_let.contains(i));
    411         }
    412         for i in b'0'..=b'9' {
    413             assert!(dig_let.contains(i));
    414         }
    415         for i in b'A'..=b'Z' {
    416             assert!(dig_let.contains(i));
    417         }
    418         let dig_lower = ASCII_DIGITS_LOWERCASE;
    419         assert!(dig_lower.len() == 36);
    420         for i in b'a'..=b'z' {
    421             assert!(dig_lower.contains(i));
    422         }
    423         for i in b'0'..=b'9' {
    424             assert!(dig_lower.contains(i));
    425         }
    426         let dig_upper = ASCII_DIGITS_UPPERCASE;
    427         assert!(dig_upper.len() == 36);
    428         for i in b'A'..=b'Z' {
    429             assert!(dig_upper.contains(i));
    430         }
    431         for i in b'0'..=b'9' {
    432             assert!(dig_upper.contains(i));
    433         }
    434         let ffox = ASCII_FIREFOX;
    435         assert!(ffox.len() == 78);
    436         for i in b'A'..=b'Z' {
    437             assert!(ffox.contains(i));
    438         }
    439         for i in b'a'..=b'z' {
    440             assert!(ffox.contains(i));
    441         }
    442         for i in b'0'..=b'9' {
    443             assert!(ffox.contains(i));
    444         }
    445         assert!(ffox.contains(b'!'));
    446         assert!(ffox.contains(b'$'));
    447         assert!(ffox.contains(b'&'));
    448         assert!(ffox.contains(b'\''));
    449         assert!(ffox.contains(b'('));
    450         assert!(ffox.contains(b')'));
    451         assert!(ffox.contains(b'+'));
    452         assert!(ffox.contains(b','));
    453         assert!(ffox.contains(b'-'));
    454         assert!(ffox.contains(b';'));
    455         assert!(ffox.contains(b'='));
    456         assert!(ffox.contains(b'_'));
    457         assert!(ffox.contains(b'`'));
    458         assert!(ffox.contains(b'{'));
    459         assert!(ffox.contains(b'}'));
    460         assert!(ffox.contains(b'~'));
    461         assert!(ASCII_HYPHEN_DIGITS_LETTERS.len() == 63);
    462         assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(b'-'));
    463         for i in b'A'..=b'Z' {
    464             assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(i));
    465         }
    466         for i in b'a'..=b'z' {
    467             assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(i));
    468         }
    469         for i in b'0'..=b'9' {
    470             assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(i));
    471         }
    472         let hyp_lower = ASCII_HYPHEN_DIGITS_LOWERCASE;
    473         assert!(hyp_lower.len() == 37);
    474         assert!(hyp_lower.contains(b'-'));
    475         for i in b'a'..=b'z' {
    476             assert!(hyp_lower.contains(i));
    477         }
    478         for i in b'0'..=b'9' {
    479             assert!(hyp_lower.contains(i));
    480         }
    481         let hyp_upper = ASCII_HYPHEN_DIGITS_UPPERCASE;
    482         assert!(hyp_upper.len() == 37);
    483         assert!(hyp_upper.contains(b'-'));
    484         for i in b'A'..=b'Z' {
    485             assert!(hyp_upper.contains(i));
    486         }
    487         for i in b'0'..=b'9' {
    488             assert!(hyp_upper.contains(i));
    489         }
    490         let printable = PRINTABLE_ASCII;
    491         assert!(printable.len() == 92);
    492         let stop = b'.' - 1;
    493         for i in 33..=stop {
    494             assert!(printable.contains(i));
    495         }
    496         let stop2 = b'\\' - 1;
    497         for i in stop + 2..=stop2 {
    498             assert!(printable.contains(i));
    499         }
    500         for i in stop2 + 2..=b'~' {
    501             assert!(printable.contains(i));
    502         }
    503         let rfc = RFC5322_ATEXT;
    504         assert!(rfc.len() == 81);
    505         for i in b'A'..=b'Z' {
    506             assert!(rfc.contains(i));
    507         }
    508         for i in b'a'..=b'z' {
    509             assert!(rfc.contains(i));
    510         }
    511         for i in b'0'..=b'9' {
    512             assert!(rfc.contains(i));
    513         }
    514         assert!(rfc.contains(b'!'));
    515         assert!(rfc.contains(b'#'));
    516         assert!(rfc.contains(b'$'));
    517         assert!(rfc.contains(b'%'));
    518         assert!(rfc.contains(b'&'));
    519         assert!(rfc.contains(b'\''));
    520         assert!(rfc.contains(b'*'));
    521         assert!(rfc.contains(b'+'));
    522         assert!(rfc.contains(b'-'));
    523         assert!(rfc.contains(b'/'));
    524         assert!(rfc.contains(b'='));
    525         assert!(rfc.contains(b'?'));
    526         assert!(rfc.contains(b'^'));
    527         assert!(rfc.contains(b'_'));
    528         assert!(rfc.contains(b'`'));
    529         assert!(rfc.contains(b'{'));
    530         assert!(rfc.contains(b'|'));
    531         assert!(rfc.contains(b'}'));
    532         assert!(rfc.contains(b'~'));
    533         let whatwg = WHATWG_VALID_DOMAIN_CODE_POINTS;
    534         for i in 0..=0x1f {
    535             assert!(!whatwg.contains(i));
    536         }
    537         assert!(!whatwg.contains(b'\x20'));
    538         assert!(!whatwg.contains(b'#'));
    539         assert!(!whatwg.contains(b'/'));
    540         assert!(!whatwg.contains(b':'));
    541         assert!(!whatwg.contains(b'<'));
    542         assert!(!whatwg.contains(b'>'));
    543         assert!(!whatwg.contains(b'?'));
    544         assert!(!whatwg.contains(b'@'));
    545         assert!(!whatwg.contains(b'['));
    546         assert!(!whatwg.contains(b'\\'));
    547         assert!(!whatwg.contains(b']'));
    548         assert!(!whatwg.contains(b'^'));
    549         assert!(!whatwg.contains(b'|'));
    550         assert!(!whatwg.contains(b'%'));
    551         assert!(!whatwg.contains(b'\x7f'));
    552         assert!(!whatwg.contains(b'.'));
    553         assert!(whatwg.len() == 128 - 32 - 16);
    554     }
    555 }