| 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 | |