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