const_panic/fmt/
char_formatting.rs

1//! `char`-formatted related items
2
3use crate::{
4    fmt::{FmtArg, FmtKind},
5    fmt_impls::basic_fmt_impls::primitive_static_panicfmt,
6    panic_val::{PanicVal, PanicVariant},
7    utils::{string_cap, PreFmtString, StartAndBytes},
8};
9
10#[cfg(all(test, not(miri)))]
11mod tests;
12
13impl PanicVal<'_> {
14    /// Constructs a `PanicVal` from a `char`.
15    pub const fn from_char(c: char, fmtarg: FmtArg) -> Self {
16        let StartAndBytes { start, bytes } = match fmtarg.fmt_kind {
17            FmtKind::Display => {
18                let (arr, len) = char_to_utf8(c);
19                crate::utils::tail_byte_array::<{ string_cap::PREFMT }>(len, &arr)
20            }
21            FmtKind::Debug => {
22                let fmtchar = char_to_debug(c);
23                crate::utils::tail_byte_array(fmtchar.len(), &fmtchar.encoded)
24            }
25        };
26        // SAFETY:
27        // char_to_utf8 is exhaustively tested in the tests module.
28        // char_to_debug is exhaustively tested in the tests module.
29        // tail_byte_array is also tested for smaller/equal/larger input arrays.
30        let prefmt = unsafe { PreFmtString::new(start, bytes) };
31        PanicVal {
32            var: PanicVariant::PreFmt(prefmt),
33        }
34    }
35}
36
37primitive_static_panicfmt! {
38    fn[](&self: char, fmtarg) {
39        PanicVal::from_char(*self.0, fmtarg)
40    }
41}
42
43/// Converts 0..=0xF to its ascii representation of '0'..='9' and 'A'..='F'
44#[inline]
45const fn hex_as_ascii(n: u8) -> u8 {
46    if n < 10 {
47        n + b'0'
48    } else {
49        n - 10 + b'A'
50    }
51}
52
53#[cfg(test)]
54pub(crate) const fn char_debug_len(c: char) -> usize {
55    let inner = match c {
56        '\t' | '\r' | '\n' | '\\' | '\'' => 2,
57        '\x00'..='\x1F' => 4,
58        _ => c.len_utf8(),
59    };
60    inner + 2
61}
62
63const fn char_to_utf8(char: char) -> ([u8; 4], usize) {
64    let u32 = char as u32;
65    match u32 {
66        0..=127 => ([u32 as u8, 0, 0, 0], 1),
67        0x80..=0x7FF => {
68            let b0 = 0b1100_0000 | (u32 >> 6) as u8;
69            let b1 = 0b1000_0000 | (u32 & 0b0011_1111) as u8;
70            ([b0, b1, 0, 0], 2)
71        }
72        0x800..=0xFFFF => {
73            let b0 = 0b1110_0000 | (u32 >> 12) as u8;
74            let b1 = 0b1000_0000 | ((u32 >> 6) & 0b0011_1111) as u8;
75            let b2 = 0b1000_0000 | (u32 & 0b0011_1111) as u8;
76            ([b0, b1, b2, 0], 3)
77        }
78        0x10000..=u32::MAX => {
79            let b0 = 0b1111_0000 | (u32 >> 18) as u8;
80            let b1 = 0b1000_0000 | ((u32 >> 12) & 0b0011_1111) as u8;
81            let b2 = 0b1000_0000 | ((u32 >> 6) & 0b0011_1111) as u8;
82            let b3 = 0b1000_0000 | (u32 & 0b0011_1111) as u8;
83            ([b0, b1, b2, b3], 4)
84        }
85    }
86}
87
88/// Display formats a `char`
89pub const fn char_to_display(char: char) -> FmtChar {
90    let ([b0, b1, b2, b3], len) = char_to_utf8(char);
91    FmtChar {
92        encoded: [b0, b1, b2, b3, 0, 0, 0, 0, 0, 0, 0, 0],
93        len: len as u8,
94    }
95}
96
97/// Debug formats a `char`
98pub const fn char_to_debug(c: char) -> FmtChar {
99    let ([b0, b1, b2, b3], len) = match c {
100        '\t' => (*br#"\t  "#, 2),
101        '\r' => (*br#"\r  "#, 2),
102        '\n' => (*br#"\n  "#, 2),
103        '\\' => (*br#"\\  "#, 2),
104        '\'' => (*br#"\'  "#, 2),
105        '\"' => (*br#""   "#, 1),
106        '\x00'..='\x1F' => {
107            let n = c as u8;
108            (
109                [b'\\', b'x', hex_as_ascii(n >> 4), hex_as_ascii(n & 0b1111)],
110                4,
111            )
112        }
113        _ => char_to_utf8(c),
114    };
115
116    let mut encoded = [b'\'', b0, b1, b2, b3, 0, 0, 0, 0, 0, 0, 0];
117    encoded[len + 1] = b'\'';
118
119    FmtChar {
120        encoded,
121        len: (len as u8) + 2,
122    }
123}
124
125/// An byte slice with a display/debug formatted `char`.
126///
127/// To get the encoded character, you need to do
128/// `&fmt_char.encoded()[..fmt_char.len()]`.
129#[derive(Copy, Clone)]
130pub struct FmtChar {
131    encoded: [u8; 12],
132    len: u8,
133}
134
135impl FmtChar {
136    /// Array which contains the display/debug-formatted  `char`,
137    /// and trailing `0` padding.
138    pub const fn encoded(&self) -> &[u8; 12] {
139        &self.encoded
140    }
141
142    /// The length of the subslice that contains the formatted character.
143    pub const fn len(&self) -> usize {
144        self.len as usize
145    }
146}