Coverage Report

Created: 2026-02-14 06:37

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