1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#[doc(hidden)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Formatting {
    Debug,
    Display,
}

impl Formatting {
    /// Whether the current variant is `Display`
    #[inline(always)]
    pub const fn is_display(self) -> bool {
        matches!(self, Formatting::Display)
    }
}

/// How numbers are formatted in debug formatters.
///
/// Hexadecimal or binary formatting in the formatting string from this crate imply
/// debug formatting.
///
///
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum NumberFormatting {
    /// Formats numbers as decimal
    Decimal,
    /// Formats numbers as hexadecimal
    Hexadecimal,
    /// Formats numbers as binary
    Binary,
}

impl NumberFormatting {
    #[cfg(test)]
    #[cfg(feature = "fmt")]
    pub(crate) const ALL: &'static [Self; 3] = &[
        NumberFormatting::Decimal,
        NumberFormatting::Hexadecimal,
        NumberFormatting::Binary,
    ];
}

////////////////////////////////////////////////////////////////////////////////

/// This type bundles configuration for how to format data into strings, including.
///
/// # Number formatting
///
/// How numbers are formatted in debug formatters,
/// It can be accessed with the `num_fmt` method, and set with the `set_num_fmt` method.
///
/// Each type of number formatting corresponds to a [`NumberFormatting`] variant:
///
/// - `NumberFormatting::Decimal` (eg: `formatc!("{:?}", FOO)`):
/// formats numbers as decimal.
///
/// - `NumberFormatting::Hexadecimal`  (eg: `formatc!("{:x}", FOO)`):
/// formats numbers as hexadecimal.
///
/// - `NumberFormatting::Binary` (eg: `formatc!("{:b}", FOO)`):
/// formats numbers as binary.
///
/// Hexadecimal or binary formatting in the formatting string from this crate imply
/// debug formatting,
/// and can be used to for example print an array of binary numbers.
///
/// # Alternate flag
///
/// A flag that types can use to be formatted differently when it's enabled,
/// checked with the `.is_alternate()` method.
///
/// The default behavior when it is enabled is this:
///
/// - The Debug formater (eg: `formatc!("{:#?}", FOO)`):
/// pretty print structs and enums.
///
/// - The hexadecimal formater (eg: `formatc!("{:#x}", FOO)`):
/// prefixes numbers with `0x`.
///
/// - The binary formater (eg: `formatc!("{:#b}", FOO)`):
/// prefixes numbers with `0b`.`
///
/// [`Formatter`]: ./struct.Formatter.html
///
#[must_use]
#[derive(Debug, Copy, Clone)]
pub struct FormattingFlags {
    num_fmt: NumberFormatting,
    is_alternate: bool,
}

#[doc(hidden)]
impl FormattingFlags {
    pub const __REG: Self = Self::NEW.set_alternate(false).set_decimal();
    pub const __HEX: Self = Self::NEW.set_alternate(false).set_hexadecimal();
    pub const __BIN: Self = Self::NEW.set_alternate(false).set_binary();

    pub const __A_REG: Self = Self::NEW.set_alternate(true).set_decimal();
    pub const __A_HEX: Self = Self::NEW.set_alternate(true).set_hexadecimal();
    pub const __A_BIN: Self = Self::NEW.set_alternate(true).set_binary();
}
impl FormattingFlags {
    #[doc(hidden)]
    pub const DEFAULT: Self = Self {
        num_fmt: NumberFormatting::Decimal,
        is_alternate: false,
    };

    /// Constructs a `FormattingFlags` with these values:
    ///
    /// - number formatting: NumberFormatting::Decimal
    ///
    /// - is alternate: false
    ///
    pub const NEW: Self = Self {
        num_fmt: NumberFormatting::Decimal,
        is_alternate: false,
    };

    /// Constructs a `FormattingFlags` with these values:
    ///
    /// - number formatting: NumberFormatting::Decimal
    ///
    /// - is alternate: false
    ///
    #[inline]
    pub const fn new() -> Self {
        Self::NEW
    }

    /// Sets the integer formatting num_fmt,
    ///
    /// This usually doesn't affect the outputted text in display formatting.
    #[inline]
    pub const fn set_num_fmt(mut self, num_fmt: NumberFormatting) -> Self {
        self.num_fmt = num_fmt;
        self
    }

    /// Sets the formatting num_fmt to `NumberFormatting::Decimal`.
    ///
    /// This means that numbers are written as decimal.
    #[inline]
    pub const fn set_decimal(mut self) -> Self {
        self.num_fmt = NumberFormatting::Decimal;
        self
    }

    /// Sets the formatting num_fmt to `NumberFormatting::Hexadecimal`.
    ///
    /// This means that numbers are written as hexadecimal.
    #[inline]
    pub const fn set_hexadecimal(mut self) -> Self {
        self.num_fmt = NumberFormatting::Hexadecimal;
        self
    }

    /// Sets the formatting num_fmt to `NumberFormatting::Binary`.
    ///
    /// This means that numbers are written as binary.
    #[inline]
    pub const fn set_binary(mut self) -> Self {
        self.num_fmt = NumberFormatting::Binary;
        self
    }

    /// Sets whether the formatting flag is enabled.
    #[inline]
    pub const fn set_alternate(mut self, is_alternate: bool) -> Self {
        self.is_alternate = is_alternate;
        self
    }

    /// Gets the current `NumberFormatting`.
    #[inline]
    pub const fn num_fmt(self) -> NumberFormatting {
        self.num_fmt
    }

    /// Gets whether the alternate flag is enabled
    #[inline]
    pub const fn is_alternate(self) -> bool {
        self.is_alternate
    }
}

////////////////////////////////////////////////////////////////////////////////

#[doc(hidden)]
/// For writing into an array from the start
pub struct LenAndArray<T: ?Sized> {
    /// The amount of elements written in `array`
    pub len: usize,
    pub array: T,
}

#[doc(hidden)]
/// For writing into an array from the end
pub struct StartAndArray<T: ?Sized> {
    /// The first element in `array`
    pub start: usize,
    pub array: T,
}

////////////////////////////////////////////////////////////////////////////////

#[doc(hidden)]
pub struct ForEscaping {
    pub is_escaped: u128,
    pub is_backslash_escaped: u128,
    pub escape_char: [u8; 16],
}

impl ForEscaping {
    /// Gets the backslash escape for a character that is kwown to be escaped with a backslash.
    #[inline(always)]
    pub const fn get_backslash_escape(b: u8) -> u8 {
        FOR_ESCAPING.escape_char[(b & 0b1111) as usize]
    }
}

#[doc(hidden)]
/// Converts 0..=0xF to its ascii representation of '0'..='9' and 'A'..='F'
#[inline(always)]
pub const fn hex_as_ascii(n: u8) -> u8 {
    if n < 10 {
        n + b'0'
    } else {
        n - 10 + b'A'
    }
}

#[doc(hidden)]
pub const FOR_ESCAPING: &ForEscaping = {
    let mut is_backslash_escaped = 0;

    let escaped = [
        (b'\t', b't'),
        (b'\n', b'n'),
        (b'\r', b'r'),
        (b'\'', b'\''),
        (b'"', b'"'),
        (b'\\', b'\\'),
    ];

    // Using the fact that the characters above all have different bit patterns for
    // the lowest 4 bits.
    let mut escape_char = [0u8; 16];

    __for_range! {i in 0..escaped.len() =>
        let (code, escape) = escaped[i];
        is_backslash_escaped |= 1 << code;

        let ei = (code & 0b1111) as usize;
        let prev_escape = escape_char[ei] as usize;
        ["Oh no, some escaped character uses the same 4 lower bits as another"][prev_escape];
        escape_char[ei] = escape;
    }

    // Setting all the control characters as being escaped.
    let is_escaped = is_backslash_escaped | 0xFFFF_FFFF;

    &ForEscaping {
        escape_char,
        is_backslash_escaped,
        is_escaped,
    }
};