1use 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 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 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#[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
88pub 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
97pub 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#[derive(Copy, Clone)]
130pub struct FmtChar {
131 encoded: [u8; 12],
132 len: u8,
133}
134
135impl FmtChar {
136 pub const fn encoded(&self) -> &[u8; 12] {
139 &self.encoded
140 }
141
142 pub const fn len(&self) -> usize {
144 self.len as usize
145 }
146}