/rust/registry/src/index.crates.io-1949cf8c6b5b557f/itoa-1.0.18/src/lib.rs
Line | Count | Source |
1 | | //! [![github]](https://github.com/dtolnay/itoa) [![crates-io]](https://crates.io/crates/itoa) [![docs-rs]](https://docs.rs/itoa) |
2 | | //! |
3 | | //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github |
4 | | //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust |
5 | | //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs |
6 | | //! |
7 | | //! <br> |
8 | | //! |
9 | | //! This crate provides a fast conversion of integer primitives to decimal |
10 | | //! strings. The implementation comes straight from [libcore] but avoids the |
11 | | //! performance penalty of going through [`core::fmt::Formatter`]. |
12 | | //! |
13 | | //! See also [`zmij`] for printing floating point primitives. |
14 | | //! |
15 | | //! [libcore]: https://github.com/rust-lang/rust/blob/1.92.0/library/core/src/fmt/num.rs#L190-L253 |
16 | | //! [`zmij`]: https://github.com/dtolnay/zmij |
17 | | //! |
18 | | //! # Example |
19 | | //! |
20 | | //! ``` |
21 | | //! fn main() { |
22 | | //! let mut buffer = itoa::Buffer::new(); |
23 | | //! let printed = buffer.format(128u64); |
24 | | //! assert_eq!(printed, "128"); |
25 | | //! } |
26 | | //! ``` |
27 | | //! |
28 | | //! # Performance |
29 | | //! |
30 | | //! The [itoa-benchmark] compares this library and other Rust integer formatting |
31 | | //! implementations across a range of integer sizes. The vertical axis in this |
32 | | //! chart shows nanoseconds taken by a single execution of |
33 | | //! `itoa::Buffer::new().format(value)` so a lower result indicates a faster |
34 | | //! library. |
35 | | //! |
36 | | //! [itoa-benchmark]: https://github.com/dtolnay/itoa-benchmark |
37 | | //! |
38 | | //!  |
39 | | |
40 | | #![doc(html_root_url = "https://docs.rs/itoa/1.0.18")] |
41 | | #![no_std] |
42 | | #![allow( |
43 | | clippy::cast_lossless, |
44 | | clippy::cast_possible_truncation, |
45 | | clippy::cast_sign_loss, |
46 | | clippy::expl_impl_clone_on_copy, |
47 | | clippy::identity_op, |
48 | | clippy::items_after_statements, |
49 | | clippy::must_use_candidate, |
50 | | clippy::needless_doctest_main, |
51 | | clippy::unreadable_literal |
52 | | )] |
53 | | |
54 | | mod u128_ext; |
55 | | |
56 | | use core::hint; |
57 | | use core::mem::{self, MaybeUninit}; |
58 | | use core::str; |
59 | | #[cfg(feature = "no-panic")] |
60 | | use no_panic::no_panic; |
61 | | |
62 | | /// A correctly sized stack allocation for the formatted integer to be written |
63 | | /// into. |
64 | | /// |
65 | | /// # Example |
66 | | /// |
67 | | /// ``` |
68 | | /// let mut buffer = itoa::Buffer::new(); |
69 | | /// let printed = buffer.format(1234); |
70 | | /// assert_eq!(printed, "1234"); |
71 | | /// ``` |
72 | | pub struct Buffer { |
73 | | bytes: [MaybeUninit<u8>; i128::MAX_STR_LEN], |
74 | | } |
75 | | |
76 | | impl Default for Buffer { |
77 | | #[inline] |
78 | 0 | fn default() -> Buffer { |
79 | 0 | Buffer::new() |
80 | 0 | } |
81 | | } |
82 | | |
83 | | impl Copy for Buffer {} |
84 | | |
85 | | #[allow(clippy::non_canonical_clone_impl)] |
86 | | impl Clone for Buffer { |
87 | | #[inline] |
88 | 0 | fn clone(&self) -> Self { |
89 | 0 | Buffer::new() |
90 | 0 | } |
91 | | } |
92 | | |
93 | | impl Buffer { |
94 | | /// This is a cheap operation; you don't need to worry about reusing buffers |
95 | | /// for efficiency. |
96 | | #[inline] |
97 | | #[cfg_attr(feature = "no-panic", no_panic)] |
98 | 0 | pub fn new() -> Buffer { |
99 | 0 | let bytes = [MaybeUninit::<u8>::uninit(); i128::MAX_STR_LEN]; |
100 | 0 | Buffer { bytes } |
101 | 0 | } |
102 | | |
103 | | /// Print an integer into this buffer and return a reference to its string |
104 | | /// representation within the buffer. |
105 | | #[cfg_attr(feature = "no-panic", no_panic)] |
106 | 0 | pub fn format<I: Integer>(&mut self, i: I) -> &str { |
107 | 0 | let buf_ptr = self.bytes.as_mut_ptr().cast::<I::Buffer>(); |
108 | 0 | let string = i.write(unsafe { &mut *buf_ptr }); |
109 | 0 | if string.len() > I::MAX_STR_LEN { |
110 | 0 | unsafe { hint::unreachable_unchecked() }; |
111 | 0 | } |
112 | 0 | string |
113 | 0 | } Unexecuted instantiation: <itoa::Buffer>::format::<u64> Unexecuted instantiation: <itoa::Buffer>::format::<u32> Unexecuted instantiation: <itoa::Buffer>::format::<i64> Unexecuted instantiation: <itoa::Buffer>::format::<_> Unexecuted instantiation: <itoa::Buffer>::format::<u64> Unexecuted instantiation: <itoa::Buffer>::format::<u32> Unexecuted instantiation: <itoa::Buffer>::format::<i64> Unexecuted instantiation: <itoa::Buffer>::format::<u64> Unexecuted instantiation: <itoa::Buffer>::format::<u32> Unexecuted instantiation: <itoa::Buffer>::format::<i64> Unexecuted instantiation: <itoa::Buffer>::format::<u32> Unexecuted instantiation: <itoa::Buffer>::format::<i64> Unexecuted instantiation: <itoa::Buffer>::format::<usize> Unexecuted instantiation: <itoa::Buffer>::format::<i32> Unexecuted instantiation: <itoa::Buffer>::format::<u32> Unexecuted instantiation: <itoa::Buffer>::format::<u64> Unexecuted instantiation: <itoa::Buffer>::format::<u64> |
114 | | } |
115 | | |
116 | | /// An integer that can be written into an [`itoa::Buffer`][Buffer]. |
117 | | /// |
118 | | /// This trait is sealed and cannot be implemented for types outside of itoa. |
119 | | pub trait Integer: private::Sealed { |
120 | | /// The maximum length of string that formatting an integer of this type can |
121 | | /// produce on the current target platform. |
122 | | const MAX_STR_LEN: usize; |
123 | | } |
124 | | |
125 | | // Seal to prevent downstream implementations of the Integer trait. |
126 | | mod private { |
127 | | #[doc(hidden)] |
128 | | pub trait Sealed: Copy { |
129 | | #[doc(hidden)] |
130 | | type Buffer: 'static; |
131 | | fn write(self, buf: &mut Self::Buffer) -> &str; |
132 | | } |
133 | | } |
134 | | |
135 | | macro_rules! impl_Integer { |
136 | | ($Signed:ident, $Unsigned:ident) => { |
137 | | const _: () = { |
138 | | assert!($Signed::MIN < 0, "need signed"); |
139 | | assert!($Unsigned::MIN == 0, "need unsigned"); |
140 | | assert!($Signed::BITS == $Unsigned::BITS, "need counterparts"); |
141 | | }; |
142 | | |
143 | | impl Integer for $Unsigned { |
144 | | const MAX_STR_LEN: usize = $Unsigned::MAX.ilog10() as usize + 1; |
145 | | } |
146 | | |
147 | | impl private::Sealed for $Unsigned { |
148 | | type Buffer = [MaybeUninit<u8>; Self::MAX_STR_LEN]; |
149 | | |
150 | | #[inline] |
151 | | #[cfg_attr(feature = "no-panic", no_panic)] |
152 | 0 | fn write(self, buf: &mut Self::Buffer) -> &str { |
153 | 0 | let offset = Unsigned::fmt(self, buf); |
154 | | // SAFETY: Starting from `offset`, all elements of the slice have been set. |
155 | 0 | unsafe { slice_buffer_to_str(buf, offset) } |
156 | 0 | } Unexecuted instantiation: <u64 as itoa::private::Sealed>::write Unexecuted instantiation: <u32 as itoa::private::Sealed>::write Unexecuted instantiation: <u8 as itoa::private::Sealed>::write Unexecuted instantiation: <u16 as itoa::private::Sealed>::write Unexecuted instantiation: <u128 as itoa::private::Sealed>::write |
157 | | } |
158 | | |
159 | | impl Integer for $Signed { |
160 | | const MAX_STR_LEN: usize = $Signed::MAX.ilog10() as usize + 2; |
161 | | } |
162 | | |
163 | | impl private::Sealed for $Signed { |
164 | | type Buffer = [MaybeUninit<u8>; Self::MAX_STR_LEN]; |
165 | | |
166 | | #[inline] |
167 | | #[cfg_attr(feature = "no-panic", no_panic)] |
168 | 0 | fn write(self, buf: &mut Self::Buffer) -> &str { |
169 | 0 | let mut offset = Self::MAX_STR_LEN - $Unsigned::MAX_STR_LEN; |
170 | 0 | offset += Unsigned::fmt( |
171 | 0 | self.unsigned_abs(), |
172 | 0 | (&mut buf[offset..]).try_into().unwrap(), |
173 | 0 | ); |
174 | 0 | if self < 0 { |
175 | 0 | offset -= 1; |
176 | 0 | buf[offset].write(b'-'); |
177 | 0 | } |
178 | | // SAFETY: Starting from `offset`, all elements of the slice have been set. |
179 | 0 | unsafe { slice_buffer_to_str(buf, offset) } |
180 | 0 | } Unexecuted instantiation: <i64 as itoa::private::Sealed>::write Unexecuted instantiation: <i8 as itoa::private::Sealed>::write Unexecuted instantiation: <i16 as itoa::private::Sealed>::write Unexecuted instantiation: <i32 as itoa::private::Sealed>::write Unexecuted instantiation: <i128 as itoa::private::Sealed>::write |
181 | | } |
182 | | }; |
183 | | } |
184 | | |
185 | | impl_Integer!(i8, u8); |
186 | | impl_Integer!(i16, u16); |
187 | | impl_Integer!(i32, u32); |
188 | | impl_Integer!(i64, u64); |
189 | | impl_Integer!(i128, u128); |
190 | | |
191 | | macro_rules! impl_Integer_size { |
192 | | ($t:ty as $primitive:ident #[cfg(target_pointer_width = $width:literal)]) => { |
193 | | #[cfg(target_pointer_width = $width)] |
194 | | impl Integer for $t { |
195 | | const MAX_STR_LEN: usize = <$primitive as Integer>::MAX_STR_LEN; |
196 | | } |
197 | | |
198 | | #[cfg(target_pointer_width = $width)] |
199 | | impl private::Sealed for $t { |
200 | | type Buffer = <$primitive as private::Sealed>::Buffer; |
201 | | |
202 | | #[inline] |
203 | | #[cfg_attr(feature = "no-panic", no_panic)] |
204 | 0 | fn write(self, buf: &mut Self::Buffer) -> &str { |
205 | 0 | (self as $primitive).write(buf) |
206 | 0 | } Unexecuted instantiation: <isize as itoa::private::Sealed>::write Unexecuted instantiation: <usize as itoa::private::Sealed>::write |
207 | | } |
208 | | }; |
209 | | } |
210 | | |
211 | | impl_Integer_size!(isize as i16 #[cfg(target_pointer_width = "16")]); |
212 | | impl_Integer_size!(usize as u16 #[cfg(target_pointer_width = "16")]); |
213 | | impl_Integer_size!(isize as i32 #[cfg(target_pointer_width = "32")]); |
214 | | impl_Integer_size!(usize as u32 #[cfg(target_pointer_width = "32")]); |
215 | | impl_Integer_size!(isize as i64 #[cfg(target_pointer_width = "64")]); |
216 | | impl_Integer_size!(usize as u64 #[cfg(target_pointer_width = "64")]); |
217 | | |
218 | | #[repr(C, align(2))] |
219 | | struct DecimalPairs([u8; 200]); |
220 | | |
221 | | // The string of all two-digit numbers in range 00..99 is used as a lookup table. |
222 | | static DECIMAL_PAIRS: DecimalPairs = DecimalPairs( |
223 | | *b"0001020304050607080910111213141516171819\ |
224 | | 2021222324252627282930313233343536373839\ |
225 | | 4041424344454647484950515253545556575859\ |
226 | | 6061626364656667686970717273747576777879\ |
227 | | 8081828384858687888990919293949596979899", |
228 | | ); |
229 | | |
230 | | // Returns {value / 100, value % 100} correct for values of up to 4 digits. |
231 | 0 | fn divmod100(value: u32) -> (u32, u32) { |
232 | 0 | debug_assert!(value < 10_000); |
233 | | const EXP: u32 = 19; // 19 is faster or equal to 12 even for 3 digits. |
234 | | const SIG: u32 = (1 << EXP) / 100 + 1; |
235 | 0 | let div = (value * SIG) >> EXP; // value / 100 |
236 | 0 | (div, value - div * 100) |
237 | 0 | } |
238 | | |
239 | | /// This function converts a slice of ascii characters into a `&str` starting |
240 | | /// from `offset`. |
241 | | /// |
242 | | /// # Safety |
243 | | /// |
244 | | /// `buf` content starting from `offset` index MUST BE initialized and MUST BE |
245 | | /// ascii characters. |
246 | | #[cfg_attr(feature = "no-panic", no_panic)] |
247 | 0 | unsafe fn slice_buffer_to_str(buf: &[MaybeUninit<u8>], offset: usize) -> &str { |
248 | | // SAFETY: `offset` is always included between 0 and `buf`'s length. |
249 | 0 | let written = unsafe { buf.get_unchecked(offset..) }; |
250 | | // SAFETY: (`assume_init_ref`) All buf content since offset is set. |
251 | | // SAFETY: (`from_utf8_unchecked`) Writes use ASCII from the lookup table exclusively. |
252 | 0 | unsafe { str::from_utf8_unchecked(&*(written as *const [MaybeUninit<u8>] as *const [u8])) } |
253 | 0 | } |
254 | | |
255 | | trait Unsigned: Integer { |
256 | | fn fmt(self, buf: &mut Self::Buffer) -> usize; |
257 | | } |
258 | | |
259 | | macro_rules! impl_Unsigned { |
260 | | ($Unsigned:ident) => { |
261 | | impl Unsigned for $Unsigned { |
262 | | #[cfg_attr(feature = "no-panic", no_panic)] |
263 | 0 | fn fmt(self, buf: &mut Self::Buffer) -> usize { |
264 | | // Count the number of bytes in buf that are not initialized. |
265 | 0 | let mut offset = buf.len(); |
266 | | // Consume the least-significant decimals from a working copy. |
267 | 0 | let mut remain = self; |
268 | | |
269 | | // Format per four digits from the lookup table. |
270 | | // Four digits need a 16-bit $Unsigned or wider. |
271 | 0 | while mem::size_of::<Self>() > 1 |
272 | 0 | && remain |
273 | 0 | > 999 |
274 | 0 | .try_into() |
275 | 0 | .expect("branch is not hit for types that cannot fit 999 (u8)") |
276 | | { |
277 | 0 | offset -= 4; |
278 | | |
279 | | // pull two pairs |
280 | 0 | let scale: Self = 1_00_00 |
281 | 0 | .try_into() |
282 | 0 | .expect("branch is not hit for types that cannot fit 1E4 (u8)"); |
283 | 0 | let quad = remain % scale; |
284 | 0 | remain /= scale; |
285 | 0 | let (pair1, pair2) = divmod100(quad as u32); |
286 | 0 | unsafe { |
287 | 0 | buf[offset + 0] |
288 | 0 | .write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 0)); |
289 | 0 | buf[offset + 1] |
290 | 0 | .write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 1)); |
291 | 0 | buf[offset + 2] |
292 | 0 | .write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 0)); |
293 | 0 | buf[offset + 3] |
294 | 0 | .write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 1)); |
295 | 0 | } |
296 | | } |
297 | | |
298 | | // Format per two digits from the lookup table. |
299 | 0 | if remain > 9 { |
300 | 0 | offset -= 2; |
301 | | |
302 | 0 | let (last, pair) = divmod100(remain as u32); |
303 | 0 | remain = last as Self; |
304 | 0 | unsafe { |
305 | 0 | buf[offset + 0] |
306 | 0 | .write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 + 0)); |
307 | 0 | buf[offset + 1] |
308 | 0 | .write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 + 1)); |
309 | 0 | } |
310 | 0 | } |
311 | | |
312 | | // Format the last remaining digit, if any. |
313 | 0 | if remain != 0 || self == 0 { |
314 | 0 | offset -= 1; |
315 | 0 |
|
316 | 0 | // Either the compiler sees that remain < 10, or it prevents |
317 | 0 | // a boundary check up next. |
318 | 0 | let last = remain as u8 & 15; |
319 | 0 | buf[offset].write(b'0' + last); |
320 | 0 | // not used: remain = 0; |
321 | 0 | } |
322 | | |
323 | 0 | offset |
324 | 0 | } Unexecuted instantiation: <u8 as itoa::Unsigned>::fmt Unexecuted instantiation: <u16 as itoa::Unsigned>::fmt Unexecuted instantiation: <u32 as itoa::Unsigned>::fmt Unexecuted instantiation: <u64 as itoa::Unsigned>::fmt |
325 | | } |
326 | | }; |
327 | | } |
328 | | |
329 | | impl_Unsigned!(u8); |
330 | | impl_Unsigned!(u16); |
331 | | impl_Unsigned!(u32); |
332 | | impl_Unsigned!(u64); |
333 | | |
334 | | impl Unsigned for u128 { |
335 | | #[cfg_attr(feature = "no-panic", no_panic)] |
336 | 0 | fn fmt(self, buf: &mut Self::Buffer) -> usize { |
337 | | // Optimize common-case zero, which would also need special treatment due to |
338 | | // its "leading" zero. |
339 | 0 | if self == 0 { |
340 | 0 | let offset = buf.len() - 1; |
341 | 0 | buf[offset].write(b'0'); |
342 | 0 | return offset; |
343 | 0 | } |
344 | | // Take the 16 least-significant decimals. |
345 | 0 | let (quot_1e16, mod_1e16) = div_rem_1e16(self); |
346 | 0 | let (mut remain, mut offset) = if quot_1e16 == 0 { |
347 | 0 | (mod_1e16, u128::MAX_STR_LEN) |
348 | | } else { |
349 | | // Write digits at buf[23..39]. |
350 | 0 | enc_16lsd::<{ u128::MAX_STR_LEN - 16 }>(buf, mod_1e16); |
351 | | |
352 | | // Take another 16 decimals. |
353 | 0 | let (quot2, mod2) = div_rem_1e16(quot_1e16); |
354 | 0 | if quot2 == 0 { |
355 | 0 | (mod2, u128::MAX_STR_LEN - 16) |
356 | | } else { |
357 | | // Write digits at buf[7..23]. |
358 | 0 | enc_16lsd::<{ u128::MAX_STR_LEN - 32 }>(buf, mod2); |
359 | | // Quot2 has at most 7 decimals remaining after two 1e16 divisions. |
360 | 0 | (quot2 as u64, u128::MAX_STR_LEN - 32) |
361 | | } |
362 | | }; |
363 | | |
364 | | // Format per four digits from the lookup table. |
365 | 0 | while remain > 999 { |
366 | 0 | offset -= 4; |
367 | | |
368 | | // pull two pairs |
369 | 0 | let quad = remain % 1_00_00; |
370 | 0 | remain /= 1_00_00; |
371 | 0 | let (pair1, pair2) = divmod100(quad as u32); |
372 | 0 | unsafe { |
373 | 0 | buf[offset + 0].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 0)); |
374 | 0 | buf[offset + 1].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 1)); |
375 | 0 | buf[offset + 2].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 0)); |
376 | 0 | buf[offset + 3].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 1)); |
377 | 0 | } |
378 | | } |
379 | | |
380 | | // Format per two digits from the lookup table. |
381 | 0 | if remain > 9 { |
382 | 0 | offset -= 2; |
383 | | |
384 | 0 | let (last, pair) = divmod100(remain as u32); |
385 | 0 | remain = last as u64; |
386 | 0 | unsafe { |
387 | 0 | buf[offset + 0].write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 + 0)); |
388 | 0 | buf[offset + 1].write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 + 1)); |
389 | 0 | } |
390 | 0 | } |
391 | | |
392 | | // Format the last remaining digit, if any. |
393 | 0 | if remain != 0 { |
394 | 0 | offset -= 1; |
395 | 0 |
|
396 | 0 | // Either the compiler sees that remain < 10, or it prevents |
397 | 0 | // a boundary check up next. |
398 | 0 | let last = remain as u8 & 15; |
399 | 0 | buf[offset].write(b'0' + last); |
400 | 0 | // not used: remain = 0; |
401 | 0 | } |
402 | 0 | offset |
403 | 0 | } |
404 | | } |
405 | | |
406 | | // Encodes the 16 least-significant decimals of n into `buf[OFFSET..OFFSET + 16]`. |
407 | | #[cfg_attr(feature = "no-panic", no_panic)] |
408 | 0 | fn enc_16lsd<const OFFSET: usize>(buf: &mut [MaybeUninit<u8>], n: u64) { |
409 | | // Consume the least-significant decimals from a working copy. |
410 | 0 | let mut remain = n; |
411 | | |
412 | | // Format per four digits from the lookup table. |
413 | 0 | for quad_index in (1..4).rev() { |
414 | | // pull two pairs |
415 | 0 | let quad = remain % 1_00_00; |
416 | 0 | remain /= 1_00_00; |
417 | 0 | let (pair1, pair2) = divmod100(quad as u32); |
418 | 0 | unsafe { |
419 | 0 | buf[quad_index * 4 + OFFSET + 0] |
420 | 0 | .write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 0)); |
421 | 0 | buf[quad_index * 4 + OFFSET + 1] |
422 | 0 | .write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 1)); |
423 | 0 | buf[quad_index * 4 + OFFSET + 2] |
424 | 0 | .write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 0)); |
425 | 0 | buf[quad_index * 4 + OFFSET + 3] |
426 | 0 | .write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 1)); |
427 | 0 | } |
428 | | } |
429 | | |
430 | | // final two pairs |
431 | 0 | let (pair1, pair2) = divmod100(remain as u32); |
432 | 0 | unsafe { |
433 | 0 | buf[OFFSET + 0].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 0)); |
434 | 0 | buf[OFFSET + 1].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 1)); |
435 | 0 | buf[OFFSET + 2].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 0)); |
436 | 0 | buf[OFFSET + 3].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 1)); |
437 | 0 | } |
438 | 0 | } Unexecuted instantiation: itoa::enc_16lsd::<23> Unexecuted instantiation: itoa::enc_16lsd::<7> |
439 | | |
440 | | // Euclidean division plus remainder with constant 1E16 basically consumes 16 |
441 | | // decimals from n. |
442 | | // |
443 | | // The integer division algorithm is based on the following paper: |
444 | | // |
445 | | // T. Granlund and P. Montgomery, “Division by Invariant Integers Using Multiplication” |
446 | | // in Proc. of the SIGPLAN94 Conference on Programming Language Design and |
447 | | // Implementation, 1994, pp. 61–72 |
448 | | // |
449 | | #[cfg_attr(feature = "no-panic", no_panic)] |
450 | 0 | fn div_rem_1e16(n: u128) -> (u128, u64) { |
451 | | const D: u128 = 1_0000_0000_0000_0000; |
452 | | // The check inlines well with the caller flow. |
453 | 0 | if n < D { |
454 | 0 | return (0, n as u64); |
455 | 0 | } |
456 | | |
457 | | // These constant values are computed with the CHOOSE_MULTIPLIER procedure |
458 | | // from the Granlund & Montgomery paper, using N=128, prec=128 and d=1E16. |
459 | | const M_HIGH: u128 = 76624777043294442917917351357515459181; |
460 | | const SH_POST: u8 = 51; |
461 | | |
462 | | // n.widening_mul(M_HIGH).1 >> SH_POST |
463 | 0 | let quot = u128_ext::mulhi(n, M_HIGH) >> SH_POST; |
464 | 0 | let rem = n - quot * D; |
465 | 0 | (quot, rem as u64) |
466 | 0 | } |