1 | #[doc (hidden)] |
2 | #[derive (Debug, Copy, Clone, PartialEq, Eq)] |
3 | pub enum Formatting { |
4 | Debug, |
5 | Display, |
6 | } |
7 | |
8 | impl Formatting { |
9 | /// Whether the current variant is `Display` |
10 | #[inline (always)] |
11 | pub const fn is_display(self) -> bool { |
12 | matches!(self, Formatting::Display) |
13 | } |
14 | } |
15 | |
16 | /// How numbers are formatted in debug formatters. |
17 | /// |
18 | /// Hexadecimal or binary formatting in the formatting string from this crate imply |
19 | /// debug formatting. |
20 | /// |
21 | /// |
22 | #[derive (Debug, Copy, Clone, PartialEq, Eq)] |
23 | pub enum NumberFormatting { |
24 | /// Formats numbers as decimal |
25 | Decimal, |
26 | /// Formats numbers as hexadecimal |
27 | Hexadecimal, |
28 | /// Formats numbers as binary |
29 | Binary, |
30 | } |
31 | |
32 | #[doc (hidden)] |
33 | #[derive (Debug, Copy, Clone, PartialEq, Eq)] |
34 | #[repr (u8)] |
35 | pub enum HexFormatting { |
36 | // micro-optimization |
37 | // by having these values for the discriminants, |
38 | // going from a number greater than 9 and smaller than 16 |
39 | // to their ascii digits is as simple as `number + (hex_fmt as u8)` |
40 | Upper = b'A' - 10, |
41 | Lower = b'a' - 10, |
42 | } |
43 | |
44 | impl NumberFormatting { |
45 | #[cfg (test)] |
46 | #[cfg (feature = "fmt" )] |
47 | pub(crate) const ALL: &'static [Self; 3] = &[ |
48 | NumberFormatting::Decimal, |
49 | NumberFormatting::Hexadecimal, |
50 | NumberFormatting::Binary, |
51 | ]; |
52 | } |
53 | |
54 | //////////////////////////////////////////////////////////////////////////////// |
55 | |
56 | /// This type bundles configuration for how to format data into strings, including. |
57 | /// |
58 | /// # Number formatting |
59 | /// |
60 | /// How numbers are formatted in debug formatters, |
61 | /// It can be accessed with the `num_fmt` method, and set with the `set_num_fmt` method. |
62 | /// |
63 | /// Each type of number formatting corresponds to a [`NumberFormatting`] variant: |
64 | /// |
65 | /// - `NumberFormatting::Decimal` (eg: `formatc!("{:?}", FOO)`): |
66 | /// formats numbers as decimal. |
67 | /// |
68 | /// - `NumberFormatting::Hexadecimal` (eg: `formatc!("{:x}", FOO)`): |
69 | /// formats numbers as hexadecimal. |
70 | /// |
71 | /// - `NumberFormatting::Binary` (eg: `formatc!("{:b}", FOO)`): |
72 | /// formats numbers as binary. |
73 | /// |
74 | /// Hexadecimal or binary formatting in the formatting string from this crate imply |
75 | /// debug formatting, |
76 | /// and can be used to for example print an array of binary numbers. |
77 | /// |
78 | /// Note: Lowercase hexadecimal formatting requires calling the |
79 | /// [`set_lower_hexadecimal`](#method.set_lower_hexadecimal) method. |
80 | /// |
81 | /// # Alternate flag |
82 | /// |
83 | /// A flag that types can use to be formatted differently when it's enabled, |
84 | /// checked with the `.is_alternate()` method. |
85 | /// |
86 | /// The default behavior when it is enabled is this: |
87 | /// |
88 | /// - The Debug formater (eg: `formatc!("{:#?}", FOO)`): |
89 | /// pretty print structs and enums. |
90 | /// |
91 | /// - The hexadecimal formater (eg: `formatc!("{:#x}", FOO)`): |
92 | /// prefixes numbers with `0x`. |
93 | /// |
94 | /// - The binary formater (eg: `formatc!("{:#b}", FOO)`): |
95 | /// prefixes numbers with `0b`.` |
96 | /// |
97 | /// [`Formatter`]: ./struct.Formatter.html |
98 | /// |
99 | #[must_use ] |
100 | #[derive (Debug, Copy, Clone)] |
101 | pub struct FormattingFlags { |
102 | num_fmt: NumberFormatting, |
103 | // Whether the `NumberFormatting` prints hexadecimal digits in lowercase |
104 | // (e.g: 0xf00, 0xF00) |
105 | // |
106 | // move this in 0.3.0 to `NumberFormatting`. |
107 | hex_fmt: HexFormatting, |
108 | is_alternate: bool, |
109 | } |
110 | |
111 | #[doc (hidden)] |
112 | impl FormattingFlags { |
113 | pub const __REG: Self = Self::NEW.set_alternate(is_alternate:false).set_decimal(); |
114 | pub const __HEX: Self = Self::NEW.set_alternate(is_alternate:false).set_hexadecimal(); |
115 | pub const __LOWHEX: Self = Self::NEW.set_alternate(is_alternate:false).set_lower_hexadecimal(); |
116 | pub const __BIN: Self = Self::NEW.set_alternate(is_alternate:false).set_binary(); |
117 | |
118 | pub const __A_REG: Self = Self::NEW.set_alternate(is_alternate:true).set_decimal(); |
119 | pub const __A_HEX: Self = Self::NEW.set_alternate(is_alternate:true).set_hexadecimal(); |
120 | pub const __A_LOWHEX: Self = Self::NEW.set_alternate(is_alternate:true).set_lower_hexadecimal(); |
121 | pub const __A_BIN: Self = Self::NEW.set_alternate(is_alternate:true).set_binary(); |
122 | } |
123 | impl FormattingFlags { |
124 | #[doc (hidden)] |
125 | pub const DEFAULT: Self = Self { |
126 | num_fmt: NumberFormatting::Decimal, |
127 | hex_fmt: HexFormatting::Upper, |
128 | is_alternate: false, |
129 | }; |
130 | |
131 | /// Constructs a `FormattingFlags` with these values: |
132 | /// |
133 | /// - number formatting: NumberFormatting::Decimal |
134 | /// |
135 | /// - is alternate: false |
136 | /// |
137 | pub const NEW: Self = Self { |
138 | num_fmt: NumberFormatting::Decimal, |
139 | hex_fmt: HexFormatting::Upper, |
140 | is_alternate: false, |
141 | }; |
142 | |
143 | /// Constructs a `FormattingFlags` with these values: |
144 | /// |
145 | /// - number formatting: NumberFormatting::Decimal |
146 | /// |
147 | /// - is alternate: false |
148 | /// |
149 | #[inline ] |
150 | pub const fn new() -> Self { |
151 | Self::NEW |
152 | } |
153 | |
154 | /// Sets the integer formatting, |
155 | /// |
156 | /// This usually doesn't affect the outputted text in display formatting. |
157 | #[inline ] |
158 | pub const fn set_num_fmt(mut self, num_fmt: NumberFormatting) -> Self { |
159 | self.num_fmt = num_fmt; |
160 | self |
161 | } |
162 | |
163 | /// Sets the number formatting to `NumberFormatting::Decimal`. |
164 | /// |
165 | /// This means that numbers are written as decimal. |
166 | #[inline ] |
167 | pub const fn set_decimal(mut self) -> Self { |
168 | self.num_fmt = NumberFormatting::Decimal; |
169 | self |
170 | } |
171 | |
172 | /// Sets the number formatting to `NumberFormatting::Hexadecimal`. |
173 | /// |
174 | /// This means that numbers are written as uppercase hexadecimal. |
175 | #[inline ] |
176 | pub const fn set_hexadecimal(mut self) -> Self { |
177 | self.num_fmt = NumberFormatting::Hexadecimal; |
178 | self.hex_fmt = HexFormatting::Upper; |
179 | self |
180 | } |
181 | |
182 | /// Sets the number formatting to `NumberFormatting::Hexadecimal`, |
183 | /// and uses lowercase for alphabetic hexadecimal digits. |
184 | /// |
185 | /// This means that numbers are written as lowercase hexadecimal. |
186 | #[inline ] |
187 | pub const fn set_lower_hexadecimal(mut self) -> Self { |
188 | self.num_fmt = NumberFormatting::Hexadecimal; |
189 | self.hex_fmt = HexFormatting::Lower; |
190 | self |
191 | } |
192 | |
193 | /// Sets the number formatting to `NumberFormatting::Binary`. |
194 | /// |
195 | /// This means that numbers are written as binary. |
196 | #[inline ] |
197 | pub const fn set_binary(mut self) -> Self { |
198 | self.num_fmt = NumberFormatting::Binary; |
199 | self |
200 | } |
201 | |
202 | /// Sets whether the formatting flag is enabled. |
203 | #[inline ] |
204 | pub const fn set_alternate(mut self, is_alternate: bool) -> Self { |
205 | self.is_alternate = is_alternate; |
206 | self |
207 | } |
208 | |
209 | /// Gets the current `NumberFormatting`. |
210 | #[inline ] |
211 | pub const fn num_fmt(self) -> NumberFormatting { |
212 | self.num_fmt |
213 | } |
214 | |
215 | /// Gets whether the alternate flag is enabled |
216 | #[inline ] |
217 | pub const fn is_alternate(self) -> bool { |
218 | self.is_alternate |
219 | } |
220 | |
221 | pub(crate) const fn hex_fmt(self) -> HexFormatting { |
222 | self.hex_fmt |
223 | } |
224 | } |
225 | |
226 | //////////////////////////////////////////////////////////////////////////////// |
227 | |
228 | #[doc (hidden)] |
229 | /// For writing into an array from the start |
230 | pub struct LenAndArray<T: ?Sized> { |
231 | /// The amount of elements written in `array` |
232 | pub len: usize, |
233 | pub array: T, |
234 | } |
235 | |
236 | #[doc (hidden)] |
237 | /// For writing into an array from the end |
238 | pub struct StartAndArray<T: ?Sized> { |
239 | /// The first element in `array` |
240 | pub start: usize, |
241 | pub array: T, |
242 | } |
243 | |
244 | //////////////////////////////////////////////////////////////////////////////// |
245 | |
246 | #[doc (hidden)] |
247 | pub struct ForEscaping { |
248 | pub is_escaped: u128, |
249 | pub is_backslash_escaped: u128, |
250 | pub escape_char: [u8; 16], |
251 | } |
252 | |
253 | impl ForEscaping { |
254 | /// Gets the backslash escape for a character that is kwown to be escaped with a backslash. |
255 | #[inline (always)] |
256 | pub const fn get_backslash_escape(b: u8) -> u8 { |
257 | FOR_ESCAPING.escape_char[(b & 0b1111) as usize] |
258 | } |
259 | } |
260 | |
261 | #[doc (hidden)] |
262 | /// Converts 0..=0xF to its ascii representation of '0'..='9' and 'A'..='F' |
263 | #[inline (always)] |
264 | pub const fn hex_as_ascii(n: u8, hex_fmt: HexFormatting) -> u8 { |
265 | if n < 10 { |
266 | n + b'0' |
267 | } else { |
268 | n + (hex_fmt as u8) |
269 | } |
270 | } |
271 | |
272 | #[doc (hidden)] |
273 | // Really clippy? Array indexing can panic you know. |
274 | #[allow (clippy::no_effect)] |
275 | pub const FOR_ESCAPING: &ForEscaping = { |
276 | let mut is_backslash_escaped = 0; |
277 | |
278 | let escaped = [ |
279 | (b' \t' , b't' ), |
280 | (b' \n' , b'n' ), |
281 | (b' \r' , b'r' ), |
282 | (b' \'' , b' \'' ), |
283 | (b'"' , b'"' ), |
284 | (b' \\' , b' \\' ), |
285 | ]; |
286 | |
287 | // Using the fact that the characters above all have different bit patterns for |
288 | // the lowest 4 bits. |
289 | let mut escape_char = [0u8; 16]; |
290 | |
291 | __for_range! {i in 0..escaped.len() => |
292 | let (code, escape) = escaped[i]; |
293 | is_backslash_escaped |= 1 << code; |
294 | |
295 | let ei = (code & 0b1111) as usize; |
296 | let prev_escape = escape_char[ei] as usize; |
297 | ["Oh no, some escaped character uses the same 4 lower bits as another" ][prev_escape]; |
298 | escape_char[ei] = escape; |
299 | } |
300 | |
301 | // Setting all the control characters as being escaped. |
302 | let is_escaped = is_backslash_escaped | 0xFFFF_FFFF; |
303 | |
304 | &ForEscaping { |
305 | escape_char, |
306 | is_backslash_escaped, |
307 | is_escaped, |
308 | } |
309 | }; |
310 | |