Coverage Report

Created: 2026-03-31 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
//! ![performance](https://raw.githubusercontent.com/dtolnay/itoa/master/itoa-benchmark.png)
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
}