|
| 1 | +// FIXME: Replace `AsciiChar` with `[core:ascii::Char]` once [#110998] is stable |
| 2 | +// [#110998]: https://github.com/rust-lang/rust/issues/110998 |
| 3 | + |
| 4 | +#![allow(unreachable_pub)] |
| 5 | + |
| 6 | +use core::ops::{Deref, Index, IndexMut}; |
| 7 | + |
| 8 | +pub use _ascii_char::AsciiChar; |
| 9 | + |
| 10 | +/// A string that only contains ASCII characters, same layout as [`str`]. |
| 11 | +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] |
| 12 | +#[repr(transparent)] |
| 13 | +pub struct AsciiStr([AsciiChar]); |
| 14 | + |
| 15 | +impl AsciiStr { |
| 16 | + #[inline] |
| 17 | + pub const fn new_sized<const N: usize>(src: &str) -> [AsciiChar; N] { |
| 18 | + if !src.is_ascii() || src.len() > N { |
| 19 | + panic!(); |
| 20 | + } |
| 21 | + |
| 22 | + let src = src.as_bytes(); |
| 23 | + let mut result = [AsciiChar::NULL; N]; |
| 24 | + let mut i = 0; |
| 25 | + while i < src.len() { |
| 26 | + result[i] = AsciiChar::new(src[i]); |
| 27 | + i += 1; |
| 28 | + } |
| 29 | + result |
| 30 | + } |
| 31 | + |
| 32 | + #[inline] |
| 33 | + pub const fn from_slice(src: &[AsciiChar]) -> &Self { |
| 34 | + // SAFETY: `Self` is transparent over `[AsciiChar]`. |
| 35 | + unsafe { core::mem::transmute::<&[AsciiChar], &AsciiStr>(src) } |
| 36 | + } |
| 37 | + |
| 38 | + #[inline] |
| 39 | + pub const fn as_str(&self) -> &str { |
| 40 | + // SAFETY: `Self` has the same layout as `str`, |
| 41 | + // and all ASCII characters are valid UTF-8 characters. |
| 42 | + unsafe { core::mem::transmute::<&AsciiStr, &str>(self) } |
| 43 | + } |
| 44 | + |
| 45 | + #[inline] |
| 46 | + pub const fn len(&self) -> usize { |
| 47 | + self.0.len() |
| 48 | + } |
| 49 | + |
| 50 | + #[inline] |
| 51 | + pub const fn is_empty(&self) -> bool { |
| 52 | + self.0.is_empty() |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +// Must not implement `DerefMut`. Not every `char` is an ASCII character. |
| 57 | +impl Deref for AsciiStr { |
| 58 | + type Target = str; |
| 59 | + |
| 60 | + #[inline] |
| 61 | + fn deref(&self) -> &Self::Target { |
| 62 | + self.as_str() |
| 63 | + } |
| 64 | +} |
| 65 | + |
| 66 | +impl<Idx> Index<Idx> for AsciiStr |
| 67 | +where |
| 68 | + [AsciiChar]: Index<Idx, Output = [AsciiChar]>, |
| 69 | +{ |
| 70 | + type Output = [AsciiChar]; |
| 71 | + |
| 72 | + #[inline] |
| 73 | + fn index(&self, index: Idx) -> &Self::Output { |
| 74 | + &self.0[index] |
| 75 | + } |
| 76 | +} |
| 77 | + |
| 78 | +impl<Idx> IndexMut<Idx> for AsciiStr |
| 79 | +where |
| 80 | + [AsciiChar]: IndexMut<Idx, Output = [AsciiChar]>, |
| 81 | +{ |
| 82 | + #[inline] |
| 83 | + fn index_mut(&mut self, index: Idx) -> &mut Self::Output { |
| 84 | + &mut self.0[index] |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +impl Default for &'static AsciiStr { |
| 89 | + #[inline] |
| 90 | + fn default() -> Self { |
| 91 | + // SAFETY: `Self` has the same layout as `str`. |
| 92 | + unsafe { core::mem::transmute::<&str, &AsciiStr>("") } |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +impl AsciiChar { |
| 97 | + pub const NULL: AsciiChar = AsciiChar::new(0); |
| 98 | + |
| 99 | + #[inline] |
| 100 | + pub const fn slice_as_bytes<const N: usize>(src: &[AsciiChar; N]) -> &[u8; N] { |
| 101 | + // SAFETY: `[AsciiChar]` has the same layout as `[u8]`. |
| 102 | + unsafe { core::mem::transmute::<&[AsciiChar; N], &[u8; N]>(src) } |
| 103 | + } |
| 104 | + |
| 105 | + #[inline] |
| 106 | + pub const fn two_digits(d: u32) -> [Self; 2] { |
| 107 | + const ALPHABET: &[u8; 10] = b"0123456789"; |
| 108 | + |
| 109 | + if d >= ALPHABET.len().pow(2) as u32 { |
| 110 | + panic!(); |
| 111 | + } |
| 112 | + [ |
| 113 | + Self::new(ALPHABET[d as usize / ALPHABET.len()]), |
| 114 | + Self::new(ALPHABET[d as usize % ALPHABET.len()]), |
| 115 | + ] |
| 116 | + } |
| 117 | + |
| 118 | + #[inline] |
| 119 | + pub const fn two_hex_digits(d: u32) -> [Self; 2] { |
| 120 | + const ALPHABET: &[u8; 16] = b"0123456789abcdef"; |
| 121 | + |
| 122 | + if d >= ALPHABET.len().pow(2) as u32 { |
| 123 | + panic!(); |
| 124 | + } |
| 125 | + [ |
| 126 | + Self::new(ALPHABET[d as usize / ALPHABET.len()]), |
| 127 | + Self::new(ALPHABET[d as usize % ALPHABET.len()]), |
| 128 | + ] |
| 129 | + } |
| 130 | +} |
| 131 | + |
| 132 | +mod _ascii_char { |
| 133 | + /// A character that is known to be in ASCII range, same layout as [`u8`]. |
| 134 | + #[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] |
| 135 | + #[repr(transparent)] |
| 136 | + pub struct AsciiChar(u8); |
| 137 | + |
| 138 | + impl AsciiChar { |
| 139 | + #[inline] |
| 140 | + pub const fn new(c: u8) -> Self { |
| 141 | + if c.is_ascii() { Self(c) } else { panic!() } |
| 142 | + } |
| 143 | + } |
| 144 | +} |
0 commit comments