Coverage Report

Created: 2026-08-13 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/time-0.3.55/src/num_fmt.rs
Line
Count
Source
1
//! Formatting utilities for numbers.
2
//!
3
//! These functions are low-level, but are designed to be _extremely_ fast for their designed use
4
//! cases. They have strict requirements, and may not return the most ergonomic types to avoid
5
//! unnecessary allocations and copying.
6
7
use core::mem::MaybeUninit;
8
use core::ops::Deref;
9
use core::{ptr, slice};
10
11
#[cfg(feature = "formatting")]
12
use deranged::ru64;
13
use deranged::{ru8, ru16, ru32};
14
15
static SINGLE_DIGITS: [u8; 10] = *b"0123456789";
16
17
static ZERO_PADDED_PAIRS: [u8; 200] = *b"0001020304050607080910111213141516171819\
18
                                         2021222324252627282930313233343536373839\
19
                                         4041424344454647484950515253545556575859\
20
                                         6061626364656667686970717273747576777879\
21
                                         8081828384858687888990919293949596979899";
22
23
#[cfg(feature = "formatting")]
24
static SPACE_PADDED_PAIRS: [u8; 200] = *b" 0 1 2 3 4 5 6 7 8 910111213141516171819\
25
                                          2021222324252627282930313233343536373839\
26
                                          4041424344454647484950515253545556575859\
27
                                          6061626364656667686970717273747576777879\
28
                                          8081828384858687888990919293949596979899";
29
30
/// A string type with a maximum length known at compile time, stored on the stack.
31
///
32
/// Note that while the _maximum_ length is known at compile time, the string may be shorter. This
33
/// information is stored inline.
34
#[derive(Clone, Copy)]
35
pub(crate) struct StackStr<const MAX_LEN: usize> {
36
    buf: [MaybeUninit<u8>; MAX_LEN],
37
    len: usize,
38
}
39
40
impl<const MAX_LEN: usize> StackStr<MAX_LEN> {
41
    /// # Safety:
42
    ///
43
    /// - `buf` must be initialized for at least `len` bytes.
44
    /// - The first `len` bytes of `buf` must be valid UTF-8.
45
    #[inline]
46
1.55k
    pub(crate) const unsafe fn new(buf: [MaybeUninit<u8>; MAX_LEN], len: usize) -> Self {
47
1.55k
        debug_assert!(len <= MAX_LEN);
48
1.55k
        Self { buf, len }
49
1.55k
    }
50
}
51
52
impl<const MAX_LEN: usize> Deref for StackStr<MAX_LEN> {
53
    type Target = str;
54
55
    #[inline]
56
4.67k
    fn deref(&self) -> &Self::Target {
57
        // Safety: This type can only be constructed when the caller asserts that the buffer is
58
        // valid UTF-8 for the first `len` bytes.
59
4.67k
        unsafe { str_from_raw_parts(self.buf.as_ptr().cast(), self.len) }
60
4.67k
    }
61
}
62
63
/// A string type with a maximum length known at compile time, stored on the stack.
64
///
65
/// Note that while the _maximum_ length is known at compile time, the string may be shorter. This
66
/// information is stored inline.
67
#[derive(Clone, Copy)]
68
pub(crate) struct StackTrailingStr<const MAX_LEN: usize> {
69
    buf: [MaybeUninit<u8>; MAX_LEN],
70
    start_index: usize,
71
}
72
73
impl<const MAX_LEN: usize> StackTrailingStr<MAX_LEN> {
74
    /// # Safety:
75
    ///
76
    /// - The last `MAX_LEN - start_index` bytes of `buf` must be initialized and valid UTF-8.
77
    #[inline]
78
0
    pub(crate) const unsafe fn new(buf: [MaybeUninit<u8>; MAX_LEN], start_index: usize) -> Self {
79
0
        debug_assert!(start_index <= MAX_LEN);
80
0
        Self { buf, start_index }
81
0
    }
82
83
    /// Return the length of `self` in bytes.
84
    #[inline]
85
0
    pub(crate) const fn len(&self) -> usize {
86
0
        let len = MAX_LEN - self.start_index;
87
        // Safety: `self.start_index` is an unsigned integer, so `len` cannot be larger than
88
        // `MAX_LEN` with the arithmetic above.
89
0
        unsafe { core::hint::assert_unchecked(len <= MAX_LEN) };
90
0
        len
91
0
    }
92
}
93
94
impl<const MAX_LEN: usize> Deref for StackTrailingStr<MAX_LEN> {
95
    type Target = str;
96
97
    #[inline]
98
0
    fn deref(&self) -> &Self::Target {
99
        // Safety: This type can only be constructed when the caller asserts that the buffer is
100
        // valid UTF-8 for the last `len` bytes.
101
        unsafe {
102
0
            str_from_raw_parts(
103
0
                self.buf.as_ptr().add(self.start_index).cast(),
104
0
                MAX_LEN - self.start_index,
105
0
            )
106
        }
107
0
    }
108
}
109
110
/// Write a two digit integer to `buf` at `offset` and `offset + 1`.
111
///
112
/// # Safety
113
///
114
/// `buf` must be at least `offset + 2` bytes long.
115
#[inline]
116
0
const unsafe fn write_two_digits(buf: &mut [MaybeUninit<u8>], offset: usize, value: ru8<0, 99>) {
117
    // Safety: `buf` is at least `offset + 2` bytes long.
118
0
    unsafe {
119
0
        ptr::copy_nonoverlapping(
120
0
            two_digits_zero_padded(value).as_ptr().cast(),
121
0
            buf.as_mut_ptr().add(offset),
122
0
            2,
123
0
        );
124
0
    }
125
0
}
126
127
/// Write a single digit integer to `buf` at `offset`.
128
///
129
/// # Safety
130
///
131
/// `buf` must be at least `offset` bytes long.
132
#[inline]
133
0
const unsafe fn write_one_digit(buf: &mut [MaybeUninit<u8>], offset: usize, value: ru8<0, 9>) {
134
    // Safety: `buf` is at least `offset` bytes long.
135
0
    unsafe {
136
0
        ptr::copy_nonoverlapping(
137
0
            single_digit(value).as_ptr().cast(),
138
0
            buf.as_mut_ptr().add(offset),
139
0
            1,
140
0
        );
141
0
    }
142
0
}
143
144
/// # Safety
145
///
146
/// - `ptr` must be non-null and point to `len` initialized bytes of UTF-8 data.
147
/// - `ptr` is valid for (and not mutated during) lifetime `'a`.
148
#[inline]
149
31.1k
pub(crate) const unsafe fn str_from_raw_parts<'a>(ptr: *const u8, len: usize) -> &'a str {
150
    // Safety: The caller must ensure that `ptr` is valid for `len` bytes and that the bytes are
151
    // valid UTF-8. The caller must also ensure that the lifetime `'a` is valid for the returned
152
    // string.
153
31.1k
    unsafe { str::from_utf8_unchecked(slice::from_raw_parts(ptr, len)) }
154
31.1k
}
time::num_fmt::str_from_raw_parts
Line
Count
Source
149
26.4k
pub(crate) const unsafe fn str_from_raw_parts<'a>(ptr: *const u8, len: usize) -> &'a str {
150
    // Safety: The caller must ensure that `ptr` is valid for `len` bytes and that the bytes are
151
    // valid UTF-8. The caller must also ensure that the lifetime `'a` is valid for the returned
152
    // string.
153
26.4k
    unsafe { str::from_utf8_unchecked(slice::from_raw_parts(ptr, len)) }
154
26.4k
}
time::num_fmt::str_from_raw_parts
Line
Count
Source
149
4.67k
pub(crate) const unsafe fn str_from_raw_parts<'a>(ptr: *const u8, len: usize) -> &'a str {
150
    // Safety: The caller must ensure that `ptr` is valid for `len` bytes and that the bytes are
151
    // valid UTF-8. The caller must also ensure that the lifetime `'a` is valid for the returned
152
    // string.
153
4.67k
    unsafe { str::from_utf8_unchecked(slice::from_raw_parts(ptr, len)) }
154
4.67k
}
155
156
#[inline]
157
4.67k
const fn div_100(n: ru16<0, 9_999>) -> [ru8<0, 99>; 2] {
158
    const EXP: u32 = 19; // 19 is faster or equal to 12 even for 3 digits.
159
    const SIG: u32 = (1 << EXP) / 100 + 1;
160
161
4.67k
    let n = n.get();
162
163
4.67k
    let high = (n as u32 * SIG) >> EXP; // value / 100
164
4.67k
    let low = n as u32 - high * 100;
165
166
    // Safety: `high` is guaranteed to be less than 100 and `low` is guaranteed to be less than 100
167
    // due to the arithmetic above.
168
    unsafe {
169
4.67k
        [
170
4.67k
            ru8::new_unchecked(high as u8),
171
4.67k
            ru8::new_unchecked(low as u8),
172
4.67k
        ]
173
    }
174
4.67k
}
time::num_fmt::div_100
Line
Count
Source
157
4.67k
const fn div_100(n: ru16<0, 9_999>) -> [ru8<0, 99>; 2] {
158
    const EXP: u32 = 19; // 19 is faster or equal to 12 even for 3 digits.
159
    const SIG: u32 = (1 << EXP) / 100 + 1;
160
161
4.67k
    let n = n.get();
162
163
4.67k
    let high = (n as u32 * SIG) >> EXP; // value / 100
164
4.67k
    let low = n as u32 - high * 100;
165
166
    // Safety: `high` is guaranteed to be less than 100 and `low` is guaranteed to be less than 100
167
    // due to the arithmetic above.
168
    unsafe {
169
4.67k
        [
170
4.67k
            ru8::new_unchecked(high as u8),
171
4.67k
            ru8::new_unchecked(low as u8),
172
4.67k
        ]
173
    }
174
4.67k
}
Unexecuted instantiation: time::num_fmt::div_100
175
176
/// Obtain a string containing a single ASCII digit representing `n`.
177
#[inline]
178
1.55k
pub(crate) const fn single_digit(n: ru8<0, 9>) -> &'static str {
179
    // Safety: We're staying within the bounds of the array. The array contains only ASCII
180
    // characters, so it's valid UTF-8.
181
1.55k
    unsafe { str_from_raw_parts(SINGLE_DIGITS.as_ptr().add(n.get() as usize), 1) }
182
1.55k
}
time::num_fmt::single_digit
Line
Count
Source
178
1.55k
pub(crate) const fn single_digit(n: ru8<0, 9>) -> &'static str {
179
    // Safety: We're staying within the bounds of the array. The array contains only ASCII
180
    // characters, so it's valid UTF-8.
181
1.55k
    unsafe { str_from_raw_parts(SINGLE_DIGITS.as_ptr().add(n.get() as usize), 1) }
182
1.55k
}
Unexecuted instantiation: time::num_fmt::single_digit
183
184
/// Obtain a string of one or two ASCII digits representing `n`. No leading zeros or spaces are
185
/// included.
186
#[inline]
187
1.55k
pub(crate) const fn one_to_two_digits_no_padding(n: ru8<0, 99>) -> &'static str {
188
1.55k
    let n = n.get();
189
1.55k
    let is_single_digit = n < 10;
190
    // Safety: We're staying within the bounds of the array. The array contains only ASCII
191
    // characters, so it's valid UTF-8.
192
    unsafe {
193
1.55k
        str_from_raw_parts(
194
1.55k
            ZERO_PADDED_PAIRS
195
1.55k
                .as_ptr()
196
1.55k
                .add((n as usize) * 2 + is_single_digit as usize),
197
1.55k
            2 - is_single_digit as usize,
198
1.55k
        )
199
    }
200
1.55k
}
time::num_fmt::one_to_two_digits_no_padding
Line
Count
Source
187
1.55k
pub(crate) const fn one_to_two_digits_no_padding(n: ru8<0, 99>) -> &'static str {
188
1.55k
    let n = n.get();
189
1.55k
    let is_single_digit = n < 10;
190
    // Safety: We're staying within the bounds of the array. The array contains only ASCII
191
    // characters, so it's valid UTF-8.
192
    unsafe {
193
1.55k
        str_from_raw_parts(
194
1.55k
            ZERO_PADDED_PAIRS
195
1.55k
                .as_ptr()
196
1.55k
                .add((n as usize) * 2 + is_single_digit as usize),
197
1.55k
            2 - is_single_digit as usize,
198
1.55k
        )
199
    }
200
1.55k
}
Unexecuted instantiation: time::num_fmt::one_to_two_digits_no_padding
201
202
/// Obtain a string of two ASCII digits representing `n`. This includes a leading zero if `n` is
203
/// less than 10.
204
#[inline]
205
20.2k
pub(crate) const fn two_digits_zero_padded(n: ru8<0, 99>) -> &'static str {
206
    // Safety: We're staying within the bounds of the array. The array contains only ASCII
207
    // characters, so it's valid UTF-8.
208
20.2k
    unsafe { str_from_raw_parts(ZERO_PADDED_PAIRS.as_ptr().add((n.get() as usize) * 2), 2) }
209
20.2k
}
time::num_fmt::two_digits_zero_padded
Line
Count
Source
205
20.2k
pub(crate) const fn two_digits_zero_padded(n: ru8<0, 99>) -> &'static str {
206
    // Safety: We're staying within the bounds of the array. The array contains only ASCII
207
    // characters, so it's valid UTF-8.
208
20.2k
    unsafe { str_from_raw_parts(ZERO_PADDED_PAIRS.as_ptr().add((n.get() as usize) * 2), 2) }
209
20.2k
}
Unexecuted instantiation: time::num_fmt::two_digits_zero_padded
210
211
/// Obtain a string of two ASCII digits representing `n`. This includes a leading space if `n` is
212
/// less than 10.
213
#[inline]
214
#[cfg(feature = "formatting")]
215
0
pub(crate) const fn two_digits_space_padded(n: ru8<0, 99>) -> &'static str {
216
    // Safety: We're staying within the bounds of the array. The array contains only ASCII
217
    // characters, so it's valid UTF-8.
218
0
    unsafe { str_from_raw_parts(SPACE_PADDED_PAIRS.as_ptr().add((n.get() as usize) * 2), 2) }
219
0
}
Unexecuted instantiation: time::num_fmt::two_digits_space_padded
Unexecuted instantiation: time::num_fmt::two_digits_space_padded
220
221
/// Obtain two strings of ASCII digits representing `n`. The first string is most significant. No
222
/// leading zeros or spaces are included.
223
#[inline]
224
#[cfg(feature = "formatting")]
225
0
pub(crate) fn one_to_three_digits_no_padding(n: ru16<0, 999>) -> [&'static str; 2] {
226
0
    if let Some(n) = n.narrow::<0, 99>() {
227
0
        crate::hint::cold_path();
228
0
        ["", one_to_two_digits_no_padding(n.into())]
229
    } else {
230
0
        three_digits_zero_padded(n)
231
    }
232
0
}
233
234
/// Obtain two strings of ASCII digits representing `n`. The first string is the most significant.
235
/// Leading zeros are included if the number has fewer than 3 digits.
236
#[inline]
237
#[cfg(feature = "formatting")]
238
0
pub(crate) const fn three_digits_zero_padded(n: ru16<0, 999>) -> [&'static str; 2] {
239
0
    let [high, low] = div_100(n.expand());
240
0
    [
241
0
        // Safety: `high` is guaranteed to be less than 10 due to the range of the input.
242
0
        single_digit(unsafe { high.narrow_unchecked() }),
243
0
        two_digits_zero_padded(low),
244
0
    ]
245
0
}
246
247
/// Obtain two strings of ASCII digits representing `n`. The first string is the most significant.
248
/// Leading spaces are included if the number has fewer than 3 digits.
249
#[inline]
250
#[cfg(feature = "formatting")]
251
0
pub(crate) const fn three_digits_space_padded(n: ru16<0, 999>) -> [&'static str; 2] {
252
0
    let [high, low] = div_100(n.expand());
253
254
0
    if let Some(high) = high.narrow::<1, 9>() {
255
0
        [single_digit(high.expand()), two_digits_zero_padded(low)]
256
    } else {
257
0
        [" ", two_digits_space_padded(low)]
258
    }
259
0
}
260
261
/// Obtain two strings of ASCII digits representing `n`. The first string is the most significant.
262
/// No leading zeros or spaces are included.
263
#[inline]
264
#[cfg(feature = "formatting")]
265
0
pub(crate) fn one_to_four_digits_no_padding(n: ru16<0, 9_999>) -> [&'static str; 2] {
266
0
    if let Some(n) = n.narrow::<0, 999>() {
267
0
        crate::hint::cold_path();
268
0
        one_to_three_digits_no_padding(n)
269
    } else {
270
0
        four_digits_zero_padded(n)
271
    }
272
0
}
273
274
/// Obtain two strings of two ASCII digits each representing `n`. The first string is the most
275
/// significant. Leading zeros are included if the number has fewer than 4 digits.
276
#[inline]
277
4.67k
pub(crate) const fn four_digits_zero_padded(n: ru16<0, 9_999>) -> [&'static str; 2] {
278
4.67k
    let [high, low] = div_100(n);
279
4.67k
    [two_digits_zero_padded(high), two_digits_zero_padded(low)]
280
4.67k
}
time::num_fmt::four_digits_zero_padded
Line
Count
Source
277
4.67k
pub(crate) const fn four_digits_zero_padded(n: ru16<0, 9_999>) -> [&'static str; 2] {
278
4.67k
    let [high, low] = div_100(n);
279
4.67k
    [two_digits_zero_padded(high), two_digits_zero_padded(low)]
280
4.67k
}
Unexecuted instantiation: time::num_fmt::four_digits_zero_padded
281
282
/// Obtain two strings of two ASCII digits each representing `n`. The first string is the most
283
/// significant. Leading spaces are included if the number has fewer than 4 digits.
284
#[inline]
285
#[cfg(feature = "formatting")]
286
0
pub(crate) const fn four_digits_space_padded(n: ru16<0, 9_999>) -> [&'static str; 2] {
287
0
    let [high, low] = div_100(n);
288
289
0
    if high.get() == 0 {
290
0
        ["  ", two_digits_space_padded(low)]
291
    } else {
292
0
        [two_digits_space_padded(high), two_digits_zero_padded(low)]
293
    }
294
0
}
295
296
/// Obtain three strings which together represent `n`. The first string is the most significant.
297
/// Leading zeros are included if the number has fewer than 4 digits. The first string will be empty
298
/// if `n` is less than 10,000.
299
#[inline]
300
1.55k
pub(crate) const fn four_to_six_digits(n: ru32<0, 999_999>) -> [&'static str; 3] {
301
1.55k
    let n = n.get();
302
303
1.55k
    let (first_two, remaining) = (n / 10_000, n % 10_000);
304
305
1.55k
    let size = 2 - (first_two < 10) as usize - (first_two == 0) as usize;
306
1.55k
    let offset = first_two as usize * 2 + 2 - size;
307
308
    // Safety: `offset` is within the bounds of the array. The array contains only ASCII characters,
309
    // so it's valid UTF-8.
310
1.55k
    let first_two = unsafe { str_from_raw_parts(ZERO_PADDED_PAIRS.as_ptr().add(offset), size) };
311
    // Safety: `remaining` is guaranteed to be less than 10,000 due to the modulus above.
312
1.55k
    let [second_two, last_two] =
313
1.55k
        four_digits_zero_padded(unsafe { ru16::new_unchecked(remaining as u16) });
314
1.55k
    [first_two, second_two, last_two]
315
1.55k
}
time::num_fmt::four_to_six_digits
Line
Count
Source
300
1.55k
pub(crate) const fn four_to_six_digits(n: ru32<0, 999_999>) -> [&'static str; 3] {
301
1.55k
    let n = n.get();
302
303
1.55k
    let (first_two, remaining) = (n / 10_000, n % 10_000);
304
305
1.55k
    let size = 2 - (first_two < 10) as usize - (first_two == 0) as usize;
306
1.55k
    let offset = first_two as usize * 2 + 2 - size;
307
308
    // Safety: `offset` is within the bounds of the array. The array contains only ASCII characters,
309
    // so it's valid UTF-8.
310
1.55k
    let first_two = unsafe { str_from_raw_parts(ZERO_PADDED_PAIRS.as_ptr().add(offset), size) };
311
    // Safety: `remaining` is guaranteed to be less than 10,000 due to the modulus above.
312
1.55k
    let [second_two, last_two] =
313
1.55k
        four_digits_zero_padded(unsafe { ru16::new_unchecked(remaining as u16) });
314
1.55k
    [first_two, second_two, last_two]
315
1.55k
}
Unexecuted instantiation: time::num_fmt::four_to_six_digits
316
317
/// Obtain three strings which together represent `n`. The first string is the most significant.
318
/// Leading zeros are included if the number has fewer than 5 digits. The first string will be empty
319
/// if `n` is less than 10,000.
320
#[inline]
321
#[cfg(feature = "formatting")]
322
0
pub(crate) const fn five_digits_zero_padded(n: ru32<0, 99_999>) -> [&'static str; 3] {
323
0
    let n = n.get();
324
325
0
    let (first_one, remaining) = (n / 10_000, n % 10_000);
326
327
    // Safety: `first_one` is guaranteed to be less than 10 due to the division above.
328
0
    let first_one = single_digit(unsafe { ru8::new_unchecked(first_one as u8) });
329
    // Safety: `remaining` is guaranteed to be less than 10,000 due to the modulus above.
330
0
    let [second_two, last_two] =
331
0
        four_digits_zero_padded(unsafe { ru16::new_unchecked(remaining as u16) });
332
0
    [first_one, second_two, last_two]
333
0
}
334
335
/// Obtain three strings which together represent `n`. The first string is the most significant.
336
/// Leading zeroes are included if the number has fewer than 6 digits.
337
#[inline]
338
#[cfg(feature = "formatting")]
339
0
pub(crate) const fn six_digits_zero_padded(n: ru32<0, 999_999>) -> [&'static str; 3] {
340
0
    let n = n.get();
341
342
0
    let (first_two, remaining) = (n / 10_000, n % 10_000);
343
344
    // Safety: `first_two` is guaranteed to be less than 100 due to the division above.
345
0
    let first_two = two_digits_zero_padded(unsafe { ru8::new_unchecked(first_two as u8) });
346
    // Safety: `remaining` is guaranteed to be less than 10,000 due to the modulus above.
347
0
    let [second_two, last_two] =
348
0
        four_digits_zero_padded(unsafe { ru16::new_unchecked(remaining as u16) });
349
0
    [first_two, second_two, last_two]
350
0
}
351
352
/// Obtain five strings which together represent `n`, which is a number of nanoseconds.
353
///
354
/// This value is intended to be used after a decimal point to represent a fractional second. The
355
/// first string will always contain exactly one digit; the remaining four will contain two digits
356
/// each.
357
#[inline]
358
1.55k
pub(crate) const fn subsecond_from_nanos(n: ru32<0, 999_999_999>) -> [&'static str; 5] {
359
1.55k
    let n = n.get();
360
1.55k
    let (digits_1_thru_5, digits_6_thru_9) = (n / 10_000, n % 10_000);
361
1.55k
    let (digit_1, digits_2_thru_5) = (digits_1_thru_5 / 10_000, digits_1_thru_5 % 10_000);
362
363
    // Safety: The type of `n` ensures that `n` is less than 1,000,000,000. Combined with the
364
    // arithmetic above, this guarantees that all values are in the required ranges.
365
    unsafe {
366
1.55k
        let digit_1 = single_digit(ru8::new_unchecked(digit_1 as u8));
367
1.55k
        let [digits_2_and_3, digits_4_and_5] =
368
1.55k
            four_digits_zero_padded(ru16::new_unchecked(digits_2_thru_5 as u16));
369
1.55k
        let [digits_6_and_7, digits_8_and_9] =
370
1.55k
            four_digits_zero_padded(ru16::new_unchecked(digits_6_thru_9 as u16));
371
372
1.55k
        [
373
1.55k
            digit_1,
374
1.55k
            digits_2_and_3,
375
1.55k
            digits_4_and_5,
376
1.55k
            digits_6_and_7,
377
1.55k
            digits_8_and_9,
378
1.55k
        ]
379
    }
380
1.55k
}
time::num_fmt::subsecond_from_nanos
Line
Count
Source
358
1.55k
pub(crate) const fn subsecond_from_nanos(n: ru32<0, 999_999_999>) -> [&'static str; 5] {
359
1.55k
    let n = n.get();
360
1.55k
    let (digits_1_thru_5, digits_6_thru_9) = (n / 10_000, n % 10_000);
361
1.55k
    let (digit_1, digits_2_thru_5) = (digits_1_thru_5 / 10_000, digits_1_thru_5 % 10_000);
362
363
    // Safety: The type of `n` ensures that `n` is less than 1,000,000,000. Combined with the
364
    // arithmetic above, this guarantees that all values are in the required ranges.
365
    unsafe {
366
1.55k
        let digit_1 = single_digit(ru8::new_unchecked(digit_1 as u8));
367
1.55k
        let [digits_2_and_3, digits_4_and_5] =
368
1.55k
            four_digits_zero_padded(ru16::new_unchecked(digits_2_thru_5 as u16));
369
1.55k
        let [digits_6_and_7, digits_8_and_9] =
370
1.55k
            four_digits_zero_padded(ru16::new_unchecked(digits_6_thru_9 as u16));
371
372
1.55k
        [
373
1.55k
            digit_1,
374
1.55k
            digits_2_and_3,
375
1.55k
            digits_4_and_5,
376
1.55k
            digits_6_and_7,
377
1.55k
            digits_8_and_9,
378
1.55k
        ]
379
    }
380
1.55k
}
Unexecuted instantiation: time::num_fmt::subsecond_from_nanos
381
382
/// Obtain a string of 1 to 9 ASCII digits representing `n`, which is a number of nanoseconds.
383
///
384
/// This value is intended to be used after a decimal point to represent a fractional second.
385
/// Trailing zeros are truncated, but at least one digit is always present.
386
#[inline]
387
1.55k
pub(crate) const fn truncated_subsecond_from_nanos(n: ru32<0, 999_999_999>) -> StackStr<9> {
388
    #[repr(C, align(8))]
389
    #[derive(Clone, Copy)]
390
    struct Digits {
391
        _padding: MaybeUninit<[u8; 7]>,
392
        digit_1: u8,
393
        digits_2_thru_9: [u8; 8],
394
    }
395
396
    let [
397
1.55k
        digit_1,
398
1.55k
        digits_2_and_3,
399
1.55k
        digits_4_and_5,
400
1.55k
        digits_6_and_7,
401
1.55k
        digits_8_and_9,
402
1.55k
    ] = subsecond_from_nanos(n);
403
404
    // Ensure that digits 2 thru 9 are stored as a single array that is 8-aligned. This allows the
405
    // conversion to a `u64` to be zero cost, resulting in a nontrivial performance improvement.
406
1.55k
    let buf = Digits {
407
1.55k
        _padding: MaybeUninit::uninit(),
408
1.55k
        digit_1: digit_1.as_bytes()[0],
409
1.55k
        digits_2_thru_9: [
410
1.55k
            digits_2_and_3.as_bytes()[0],
411
1.55k
            digits_2_and_3.as_bytes()[1],
412
1.55k
            digits_4_and_5.as_bytes()[0],
413
1.55k
            digits_4_and_5.as_bytes()[1],
414
1.55k
            digits_6_and_7.as_bytes()[0],
415
1.55k
            digits_6_and_7.as_bytes()[1],
416
1.55k
            digits_8_and_9.as_bytes()[0],
417
1.55k
            digits_8_and_9.as_bytes()[1],
418
1.55k
        ],
419
1.55k
    };
420
421
    // By converting the bytes into a single integer, we can effectively perform an equality check
422
    // against b'0' for all bytes at once. This is actually faster than using portable SIMD (even
423
    // with `-Ctarget-cpu=native`).
424
1.55k
    let bitmask = u64::from_le_bytes(buf.digits_2_thru_9) ^ u64::from_le_bytes([b'0'; 8]);
425
1.55k
    let digits_to_truncate = bitmask.leading_zeros() / 8;
426
1.55k
    let len = 9 - digits_to_truncate as usize;
427
428
    // Safety: All bytes are initialized and valid UTF-8, and `len` represents the number of bytes
429
    // we wish to display (that is between 1 and 9 inclusive). `Digits` is `#[repr(C)]`, so the
430
    // layout is guaranteed.
431
    unsafe {
432
1.55k
        StackStr::new(
433
1.55k
            *(&raw const buf)
434
1.55k
                .byte_add(core::mem::offset_of!(Digits, digit_1))
435
1.55k
                .cast(),
436
1.55k
            len,
437
        )
438
    }
439
1.55k
}
time::num_fmt::truncated_subsecond_from_nanos
Line
Count
Source
387
1.55k
pub(crate) const fn truncated_subsecond_from_nanos(n: ru32<0, 999_999_999>) -> StackStr<9> {
388
    #[repr(C, align(8))]
389
    #[derive(Clone, Copy)]
390
    struct Digits {
391
        _padding: MaybeUninit<[u8; 7]>,
392
        digit_1: u8,
393
        digits_2_thru_9: [u8; 8],
394
    }
395
396
    let [
397
1.55k
        digit_1,
398
1.55k
        digits_2_and_3,
399
1.55k
        digits_4_and_5,
400
1.55k
        digits_6_and_7,
401
1.55k
        digits_8_and_9,
402
1.55k
    ] = subsecond_from_nanos(n);
403
404
    // Ensure that digits 2 thru 9 are stored as a single array that is 8-aligned. This allows the
405
    // conversion to a `u64` to be zero cost, resulting in a nontrivial performance improvement.
406
1.55k
    let buf = Digits {
407
1.55k
        _padding: MaybeUninit::uninit(),
408
1.55k
        digit_1: digit_1.as_bytes()[0],
409
1.55k
        digits_2_thru_9: [
410
1.55k
            digits_2_and_3.as_bytes()[0],
411
1.55k
            digits_2_and_3.as_bytes()[1],
412
1.55k
            digits_4_and_5.as_bytes()[0],
413
1.55k
            digits_4_and_5.as_bytes()[1],
414
1.55k
            digits_6_and_7.as_bytes()[0],
415
1.55k
            digits_6_and_7.as_bytes()[1],
416
1.55k
            digits_8_and_9.as_bytes()[0],
417
1.55k
            digits_8_and_9.as_bytes()[1],
418
1.55k
        ],
419
1.55k
    };
420
421
    // By converting the bytes into a single integer, we can effectively perform an equality check
422
    // against b'0' for all bytes at once. This is actually faster than using portable SIMD (even
423
    // with `-Ctarget-cpu=native`).
424
1.55k
    let bitmask = u64::from_le_bytes(buf.digits_2_thru_9) ^ u64::from_le_bytes([b'0'; 8]);
425
1.55k
    let digits_to_truncate = bitmask.leading_zeros() / 8;
426
1.55k
    let len = 9 - digits_to_truncate as usize;
427
428
    // Safety: All bytes are initialized and valid UTF-8, and `len` represents the number of bytes
429
    // we wish to display (that is between 1 and 9 inclusive). `Digits` is `#[repr(C)]`, so the
430
    // layout is guaranteed.
431
    unsafe {
432
1.55k
        StackStr::new(
433
1.55k
            *(&raw const buf)
434
1.55k
                .byte_add(core::mem::offset_of!(Digits, digit_1))
435
1.55k
                .cast(),
436
1.55k
            len,
437
        )
438
    }
439
1.55k
}
Unexecuted instantiation: time::num_fmt::truncated_subsecond_from_nanos
440
441
/// Format a `u64` into a string with no padding.
442
#[inline]
443
0
pub(crate) const fn u64_pad_none(value: u64) -> StackTrailingStr<20> {
444
0
    let mut bytes = [MaybeUninit::uninit(); 20];
445
446
0
    let mut offset = 20;
447
0
    let mut remain = value;
448
449
0
    while remain > 999 {
450
0
        offset -= 4;
451
0
        let quad = remain % 1_00_00;
452
0
        remain /= 1_00_00;
453
        // Safety: `quad` is guaranteed to be less than 10,000 due to the modulus above.
454
0
        let [pair1, pair2] = div_100(unsafe { ru16::new_unchecked(quad as u16) });
455
        // Safety: `buf` is at least `offset + 4` bytes long.
456
0
        unsafe {
457
0
            write_two_digits(&mut bytes, offset, pair1);
458
0
            write_two_digits(&mut bytes, offset + 2, pair2);
459
0
        }
460
    }
461
462
0
    if remain > 9 {
463
0
        offset -= 2;
464
0
465
0
        // Safety: `remain` is guaranteed to be less than 10,000 due to the loop above.
466
0
        let [last, pair] = div_100(unsafe { ru16::new_unchecked(remain as u16) });
467
0
        remain = last.get() as u64;
468
0
        // Safety: `buf` is at least `offset + 2` bytes long.
469
0
        unsafe { write_two_digits(&mut bytes, offset, pair) };
470
0
    }
471
472
0
    if remain != 0 || value == 0 {
473
0
        offset -= 1;
474
0
475
0
        let last = remain as u8 & 15;
476
0
        // Safety: `offset` is known to be in bounds, and the value is known to be less than 10 due
477
0
        // to the conditionals and bitwise AND above.
478
0
        unsafe { write_one_digit(&mut bytes, offset, ru8::new_unchecked(last)) };
479
0
    }
480
481
    // Safety: All bytes starting at `offset` are initialized and valid UTF-8.
482
0
    unsafe { StackTrailingStr::new(bytes, offset) }
483
0
}
484
485
/// Format a `u128` into a string with no padding.
486
#[inline]
487
#[cfg(feature = "formatting")]
488
0
pub(crate) const fn u128_pad_none(value: u128) -> StackTrailingStr<39> {
489
0
    let mut bytes = [MaybeUninit::uninit(); 39];
490
491
    // Take the 16 least-significant decimals.
492
0
    let (quot_1e16, mod_1e16) = div_rem_1e16(value);
493
0
    let (mut remain, mut offset) = if quot_1e16 == 0 {
494
0
        (mod_1e16.get(), 39)
495
    } else {
496
        // Write digits at buf[23..39].
497
        // Safety: `bytes` is 39 bytes long, so writing at offset 23 for 16 bytes is sound.
498
0
        unsafe { enc_16lsd::<23>(&mut bytes, mod_1e16) };
499
500
        // Take another 16 decimals.
501
0
        let (quot2, mod2) = div_rem_1e16(quot_1e16);
502
0
        if quot2 == 0 {
503
0
            (mod2.get(), 23)
504
        } else {
505
            // Write digits at buf[7..23].
506
            // Safety: `bytes` is 39 bytes long, so writing at offset 7 for 16 bytes is sound.
507
0
            unsafe { enc_16lsd::<7>(&mut bytes, mod2) };
508
            // Safety: `quot2`` has at most 7 decimals remaining after two 1e16 divisions.
509
0
            (quot2 as u64, 7)
510
        }
511
    };
512
513
    // Format per four digits from the lookup table.
514
0
    while remain > 999 {
515
0
        offset -= 4;
516
517
        // pull two pairs
518
0
        let quad = remain % 1_00_00;
519
0
        remain /= 1_00_00;
520
        // Safety: `quad` is guaranteed to be less than 10,000 due to the modulus above.
521
0
        let [pair1, pair2] = div_100(unsafe { ru16::new_unchecked(quad as u16) });
522
        // Safety: `buf` is at least `offset + 4` bytes long.
523
0
        unsafe {
524
0
            write_two_digits(&mut bytes, offset, pair1);
525
0
            write_two_digits(&mut bytes, offset + 2, pair2);
526
0
        }
527
    }
528
529
    // Format per two digits from the lookup table.
530
0
    if remain > 9 {
531
0
        offset -= 2;
532
0
533
0
        // Safety: `remain` is guaranteed to be less than 10,000 due to the loop above.
534
0
        let [last, pair] = div_100(unsafe { ru16::new_unchecked(remain as u16) });
535
0
        remain = last.get() as u64;
536
0
        // Safety: `buf` is at least `offset + 2` bytes long.
537
0
        unsafe { write_two_digits(&mut bytes, offset, pair) };
538
0
    }
539
540
    // Format the last remaining digit, if any.
541
0
    if remain != 0 || value == 0 {
542
0
        offset -= 1;
543
0
544
0
        // Either the compiler sees that remain < 10, or it prevents a boundary check up next.
545
0
        let last = remain as u8 & 15;
546
0
        // Safety: `offset` is known to be in bounds, and the value is known to be less than 10 due
547
0
        // to the conditionals and bitwise AND above.
548
0
        unsafe { write_one_digit(&mut bytes, offset, ru8::new_unchecked(last)) };
549
0
    }
550
551
    // Safety: All bytes starting at `offset` are initialized and valid UTF-8.
552
0
    unsafe { StackTrailingStr::new(bytes, offset) }
553
0
}
554
555
/// Encodes the 16 least-significant decimals of n into `buf[OFFSET..OFFSET + 16]`.
556
///
557
/// # Safety
558
///
559
/// - `buf` must be at least `OFFSET + 16` bytes long.
560
#[cfg(feature = "formatting")]
561
0
const unsafe fn enc_16lsd<const OFFSET: usize>(
562
0
    buf: &mut [MaybeUninit<u8>],
563
0
    n: ru64<0, 9999_9999_9999_9999>,
564
0
) {
565
    // Consume the least-significant decimals from a working copy.
566
0
    let mut remain = n.get();
567
568
0
    let mut quad_index = 3;
569
0
    while quad_index >= 1 {
570
0
        let quad = remain % 1_00_00;
571
0
        remain /= 1_00_00;
572
0
        // Safety: `quad` is guaranteed to be less than 10,000 due to the modulus above.
573
0
        let [pair1, pair2] = div_100(unsafe { ru16::new_unchecked(quad as u16) });
574
0
        // Safety: `buf` is at least `quad_index * 4 + OFFSET + 4` bytes long.
575
0
        unsafe {
576
0
            write_two_digits(buf, quad_index * 4 + OFFSET, pair1);
577
0
            write_two_digits(buf, quad_index * 4 + OFFSET + 2, pair2);
578
0
        }
579
0
        quad_index -= 1;
580
0
    }
581
582
    // Safety: `remain` is guaranteed to be less than 10,000 due the range of `n` and the arithmetic
583
    // in the loop above.
584
0
    let [pair1, pair2] = div_100(unsafe { ru16::new_unchecked(remain as u16) });
585
    // Safety: `buf` is at least `OFFSET + 4` bytes long.
586
0
    unsafe {
587
0
        write_two_digits(buf, OFFSET, pair1);
588
0
        write_two_digits(buf, OFFSET + 2, pair2);
589
0
    }
590
0
}
591
592
// Euclidean division plus remainder with constant 1e16 basically consumes 16
593
// decimals from n.
594
#[cfg(feature = "formatting")]
595
0
const fn div_rem_1e16(n: u128) -> (u128, ru64<0, 9999_9999_9999_9999>) {
596
    const D: u128 = 1_0000_0000_0000_0000;
597
0
    if n < D {
598
        // Safety: We just checked that `n` is in range.
599
0
        return (0, unsafe { ru64::new_unchecked(n as u64) });
600
0
    }
601
602
    const M_HIGH: u128 = 76_624_777_043_294_442_917_917_351_357_515_459_181;
603
    const SH_POST: u8 = 51;
604
605
    // n.widening_mul(M_HIGH).1 >> SH_POST
606
0
    let quot = mulhi(n, M_HIGH) >> SH_POST;
607
0
    let rem = n - quot * D;
608
    // Safety: The arithmetic above ensures that `rem` is in range.
609
0
    (quot, unsafe { ru64::new_unchecked(rem as u64) })
610
0
}
611
612
/// Multiply unsigned 128 bit integers, return upper 128 bits of the result
613
#[inline]
614
#[cfg(feature = "formatting")]
615
0
const fn mulhi(x: u128, y: u128) -> u128 {
616
0
    let x_lo = x as u64;
617
0
    let x_hi = (x >> 64) as u64;
618
0
    let y_lo = y as u64;
619
0
    let y_hi = (y >> 64) as u64;
620
621
    // handle possibility of overflow
622
0
    let carry = (x_lo as u128 * y_lo as u128) >> 64;
623
0
    let m = x_lo as u128 * y_hi as u128 + carry;
624
0
    let high1 = m >> 64;
625
626
0
    let m_lo = m as u64;
627
0
    let high2 = (x_hi as u128 * y_lo as u128 + m_lo as u128) >> 64;
628
629
0
    x_hi as u128 * y_hi as u128 + high1 + high2
630
0
}