const_format/
char_encoding.rs

1use crate::formatting::{hex_as_ascii, HexFormatting};
2
3#[cfg(any(test, feature = "fmt"))]
4pub(crate) const fn char_display_len(c: char) -> usize {
5    match c as u32 {
6        0..=127 => 1,
7        0x80..=0x7FF => 2,
8        0x800..=0xFFFF => 3,
9        0x10000..=u32::MAX => 4,
10    }
11}
12
13#[cfg(any(test, feature = "fmt"))]
14pub(crate) const fn char_debug_len(c: char) -> usize {
15    let inner = match c {
16        '\t' | '\r' | '\n' | '\\' | '\'' | '\"' => 2,
17        '\x00'..='\x1F' => 4,
18        _ => char_display_len(c),
19    };
20    inner + 2
21}
22
23const fn char_to_utf8(char: char) -> ([u8; 4], usize) {
24    let u32 = char as u32;
25    match u32 {
26        0..=127 => ([u32 as u8, 0, 0, 0], 1),
27        0x80..=0x7FF => {
28            let b0 = 0b1100_0000 | (u32 >> 6) as u8;
29            let b1 = 0b1000_0000 | (u32 & 0b0011_1111) as u8;
30            ([b0, b1, 0, 0], 2)
31        }
32        0x800..=0xFFFF => {
33            let b0 = 0b1110_0000 | (u32 >> 12) as u8;
34            let b1 = 0b1000_0000 | ((u32 >> 6) & 0b0011_1111) as u8;
35            let b2 = 0b1000_0000 | (u32 & 0b0011_1111) as u8;
36            ([b0, b1, b2, 0], 3)
37        }
38        0x10000..=u32::MAX => {
39            let b0 = 0b1111_0000 | (u32 >> 18) as u8;
40            let b1 = 0b1000_0000 | ((u32 >> 12) & 0b0011_1111) as u8;
41            let b2 = 0b1000_0000 | ((u32 >> 6) & 0b0011_1111) as u8;
42            let b3 = 0b1000_0000 | (u32 & 0b0011_1111) as u8;
43            ([b0, b1, b2, b3], 4)
44        }
45    }
46}
47
48pub(crate) const fn char_to_display(char: char) -> FmtChar {
49    let ([b0, b1, b2, b3], len) = char_to_utf8(char);
50    FmtChar {
51        encoded: [b0, b1, b2, b3, 0, 0],
52        len: len as u8,
53    }
54}
55
56pub(crate) const fn char_to_debug(c: char) -> FmtChar {
57    let ([b0, b1, b2, b3], len) = match c {
58        '\t' => (*br#"\t  "#, 2),
59        '\r' => (*br#"\r  "#, 2),
60        '\n' => (*br#"\n  "#, 2),
61        '\\' => (*br#"\\  "#, 2),
62        '\'' => (*br#"\'  "#, 2),
63        '\"' => (*br#"\"  "#, 2),
64        '\x00'..='\x1F' => {
65            let n = c as u8;
66            (
67                [
68                    b'\\',
69                    b'x',
70                    hex_as_ascii(n >> 4, HexFormatting::Upper),
71                    hex_as_ascii(n & 0b1111, HexFormatting::Upper),
72                ],
73                4,
74            )
75        }
76        _ => char_to_utf8(c),
77    };
78
79    let mut encoded = [b'\'', b0, b1, b2, b3, 0];
80    encoded[len + 1] = b'\'';
81
82    FmtChar {
83        encoded,
84        len: (len as u8) + 2,
85    }
86}
87
88#[derive(Copy, Clone)]
89pub struct FmtChar {
90    encoded: [u8; 6],
91    len: u8,
92}
93
94impl FmtChar {
95    /// Array which contains the pre-len display/debug-formatted  `char`,
96    /// only `&self.encoded[][..self.len()]` should be copied.
97    pub const fn encoded(&self) -> &[u8; 6] {
98        &self.encoded
99    }
100
101    pub const fn len(&self) -> usize {
102        self.len as usize
103    }
104
105    pub(crate) const fn as_bytes(&self) -> &[u8] {
106        #[cfg(not(feature = "rust_1_64"))]
107        {
108            match self.len() {
109                1 => {
110                    let [ret @ .., _, _, _, _, _] = &self.encoded;
111                    ret
112                }
113                2 => {
114                    let [ret @ .., _, _, _, _] = &self.encoded;
115                    ret
116                }
117                3 => {
118                    let [ret @ .., _, _, _] = &self.encoded;
119                    ret
120                }
121                4 => {
122                    let [ret @ .., _, _] = &self.encoded;
123                    ret
124                }
125                5 => {
126                    let [ret @ .., _] = &self.encoded;
127                    ret
128                }
129                6 => &self.encoded,
130                x => [/*bug WTF*/][x],
131            }
132        }
133
134        #[cfg(feature = "rust_1_64")]
135        {
136            ::konst::slice::slice_up_to(&self.encoded, self.len())
137        }
138    }
139}
140
141#[cfg(all(test, not(miri)))]
142mod tests;