Coverage Report

Created: 2025-11-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/itoa-1.0.9/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 [`ryu`] for printing floating point primitives.
14
//!
15
//! [libcore]: https://github.com/rust-lang/rust/blob/b8214dc6c6fc20d0a660fb5700dca9ebf51ebe89/src/libcore/fmt/num.rs#L201-L254
16
//! [`core::fmt::Formatter`]: https://doc.rust-lang.org/std/fmt/struct.Formatter.html
17
//! [`ryu`]: https://github.com/dtolnay/ryu
18
//!
19
//! # Example
20
//!
21
//! ```
22
//! fn main() {
23
//!     let mut buffer = itoa::Buffer::new();
24
//!     let printed = buffer.format(128u64);
25
//!     assert_eq!(printed, "128");
26
//! }
27
//! ```
28
//!
29
//! # Performance (lower is better)
30
//!
31
//! ![performance](https://raw.githubusercontent.com/dtolnay/itoa/master/performance.png)
32
33
#![doc(html_root_url = "https://docs.rs/itoa/1.0.9")]
34
#![no_std]
35
#![allow(
36
    clippy::cast_lossless,
37
    clippy::cast_possible_truncation,
38
    clippy::expl_impl_clone_on_copy,
39
    clippy::must_use_candidate,
40
    clippy::unreadable_literal
41
)]
42
43
mod udiv128;
44
45
use core::mem::{self, MaybeUninit};
46
use core::{ptr, slice, str};
47
#[cfg(feature = "no-panic")]
48
use no_panic::no_panic;
49
50
/// A correctly sized stack allocation for the formatted integer to be written
51
/// into.
52
///
53
/// # Example
54
///
55
/// ```
56
/// let mut buffer = itoa::Buffer::new();
57
/// let printed = buffer.format(1234);
58
/// assert_eq!(printed, "1234");
59
/// ```
60
pub struct Buffer {
61
    bytes: [MaybeUninit<u8>; I128_MAX_LEN],
62
}
63
64
impl Default for Buffer {
65
    #[inline]
66
    fn default() -> Buffer {
67
        Buffer::new()
68
    }
69
}
70
71
impl Copy for Buffer {}
72
73
impl Clone for Buffer {
74
    #[inline]
75
    #[allow(clippy::incorrect_clone_impl_on_copy_type)] // false positive https://github.com/rust-lang/rust-clippy/issues/11072
76
    fn clone(&self) -> Self {
77
        Buffer::new()
78
    }
79
}
80
81
impl Buffer {
82
    /// This is a cheap operation; you don't need to worry about reusing buffers
83
    /// for efficiency.
84
    #[inline]
85
    #[cfg_attr(feature = "no-panic", no_panic)]
86
34.9k
    pub fn new() -> Buffer {
87
34.9k
        let bytes = [MaybeUninit::<u8>::uninit(); I128_MAX_LEN];
88
34.9k
        Buffer { bytes }
89
34.9k
    }
90
91
    /// Print an integer into this buffer and return a reference to its string
92
    /// representation within the buffer.
93
    #[cfg_attr(feature = "no-panic", no_panic)]
94
34.9k
    pub fn format<I: Integer>(&mut self, i: I) -> &str {
95
34.9k
        i.write(unsafe {
96
34.9k
            &mut *(&mut self.bytes as *mut [MaybeUninit<u8>; I128_MAX_LEN]
97
34.9k
                as *mut <I as private::Sealed>::Buffer)
98
34.9k
        })
99
34.9k
    }
<itoa::Buffer>::format::<u8>
Line
Count
Source
94
29.1k
    pub fn format<I: Integer>(&mut self, i: I) -> &str {
95
29.1k
        i.write(unsafe {
96
29.1k
            &mut *(&mut self.bytes as *mut [MaybeUninit<u8>; I128_MAX_LEN]
97
29.1k
                as *mut <I as private::Sealed>::Buffer)
98
29.1k
        })
99
29.1k
    }
<itoa::Buffer>::format::<u32>
Line
Count
Source
94
5.82k
    pub fn format<I: Integer>(&mut self, i: I) -> &str {
95
5.82k
        i.write(unsafe {
96
5.82k
            &mut *(&mut self.bytes as *mut [MaybeUninit<u8>; I128_MAX_LEN]
97
5.82k
                as *mut <I as private::Sealed>::Buffer)
98
5.82k
        })
99
5.82k
    }
Unexecuted instantiation: <itoa::Buffer>::format::<u16>
Unexecuted instantiation: <itoa::Buffer>::format::<u8>
Unexecuted instantiation: <itoa::Buffer>::format::<u32>
Unexecuted instantiation: <itoa::Buffer>::format::<u16>
100
}
101
102
/// An integer that can be written into an [`itoa::Buffer`][Buffer].
103
///
104
/// This trait is sealed and cannot be implemented for types outside of itoa.
105
pub trait Integer: private::Sealed {}
106
107
// Seal to prevent downstream implementations of the Integer trait.
108
mod private {
109
    pub trait Sealed: Copy {
110
        type Buffer: 'static;
111
        fn write(self, buf: &mut Self::Buffer) -> &str;
112
    }
113
}
114
115
const DEC_DIGITS_LUT: &[u8] = b"\
116
      0001020304050607080910111213141516171819\
117
      2021222324252627282930313233343536373839\
118
      4041424344454647484950515253545556575859\
119
      6061626364656667686970717273747576777879\
120
      8081828384858687888990919293949596979899";
121
122
// Adaptation of the original implementation at
123
// https://github.com/rust-lang/rust/blob/b8214dc6c6fc20d0a660fb5700dca9ebf51ebe89/src/libcore/fmt/num.rs#L188-L266
124
macro_rules! impl_Integer {
125
    ($($max_len:expr => $t:ident),* as $conv_fn:ident) => {$(
126
        impl Integer for $t {}
127
128
        impl private::Sealed for $t {
129
            type Buffer = [MaybeUninit<u8>; $max_len];
130
131
            #[allow(unused_comparisons)]
132
            #[inline]
133
            #[cfg_attr(feature = "no-panic", no_panic)]
134
34.9k
            fn write(self, buf: &mut [MaybeUninit<u8>; $max_len]) -> &str {
135
34.9k
                let is_nonnegative = self >= 0;
136
34.9k
                let mut n = if is_nonnegative {
137
5.82k
                    self as $conv_fn
138
                } else {
139
                    // convert the negative num to positive by summing 1 to it's 2 complement
140
0
                    (!(self as $conv_fn)).wrapping_add(1)
141
                };
142
34.9k
                let mut curr = buf.len() as isize;
143
34.9k
                let buf_ptr = buf.as_mut_ptr() as *mut u8;
144
34.9k
                let lut_ptr = DEC_DIGITS_LUT.as_ptr();
145
146
                unsafe {
147
                    // need at least 16 bits for the 4-characters-at-a-time to work.
148
34.9k
                    if mem::size_of::<$t>() >= 2 {
149
                        // eagerly decode 4 characters at a time
150
5.82k
                        while n >= 10000 {
151
0
                            let rem = (n % 10000) as isize;
152
0
                            n /= 10000;
153
0
154
0
                            let d1 = (rem / 100) << 1;
155
0
                            let d2 = (rem % 100) << 1;
156
0
                            curr -= 4;
157
0
                            ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
158
0
                            ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
159
0
                        }
160
29.1k
                    }
161
162
                    // if we reach here numbers are <= 9999, so at most 4 chars long
163
34.9k
                    let mut n = n as isize; // possibly reduce 64bit math
164
165
                    // decode 2 more chars, if > 2 chars
166
34.9k
                    if n >= 100 {
167
5.82k
                        let d1 = (n % 100) << 1;
168
5.82k
                        n /= 100;
169
5.82k
                        curr -= 2;
170
5.82k
                        ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
171
29.1k
                    }
172
173
                    // decode last 1 or 2 chars
174
34.9k
                    if n < 10 {
175
15.2k
                        curr -= 1;
176
15.2k
                        *buf_ptr.offset(curr) = (n as u8) + b'0';
177
19.7k
                    } else {
178
19.7k
                        let d1 = n << 1;
179
19.7k
                        curr -= 2;
180
19.7k
                        ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
181
19.7k
                    }
182
183
34.9k
                    if !is_nonnegative {
184
0
                        curr -= 1;
185
0
                        *buf_ptr.offset(curr) = b'-';
186
34.9k
                    }
187
                }
188
189
34.9k
                let len = buf.len() - curr as usize;
190
34.9k
                let bytes = unsafe { slice::from_raw_parts(buf_ptr.offset(curr), len) };
191
34.9k
                unsafe { str::from_utf8_unchecked(bytes) }
192
34.9k
            }
<u8 as itoa::private::Sealed>::write
Line
Count
Source
134
29.1k
            fn write(self, buf: &mut [MaybeUninit<u8>; $max_len]) -> &str {
135
29.1k
                let is_nonnegative = self >= 0;
136
29.1k
                let mut n = if is_nonnegative {
137
29.1k
                    self as $conv_fn
138
                } else {
139
                    // convert the negative num to positive by summing 1 to it's 2 complement
140
0
                    (!(self as $conv_fn)).wrapping_add(1)
141
                };
142
29.1k
                let mut curr = buf.len() as isize;
143
29.1k
                let buf_ptr = buf.as_mut_ptr() as *mut u8;
144
29.1k
                let lut_ptr = DEC_DIGITS_LUT.as_ptr();
145
146
                unsafe {
147
                    // need at least 16 bits for the 4-characters-at-a-time to work.
148
29.1k
                    if mem::size_of::<$t>() >= 2 {
149
                        // eagerly decode 4 characters at a time
150
0
                        while n >= 10000 {
151
0
                            let rem = (n % 10000) as isize;
152
0
                            n /= 10000;
153
0
154
0
                            let d1 = (rem / 100) << 1;
155
0
                            let d2 = (rem % 100) << 1;
156
0
                            curr -= 4;
157
0
                            ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
158
0
                            ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
159
0
                        }
160
29.1k
                    }
161
162
                    // if we reach here numbers are <= 9999, so at most 4 chars long
163
29.1k
                    let mut n = n as isize; // possibly reduce 64bit math
164
165
                    // decode 2 more chars, if > 2 chars
166
29.1k
                    if n >= 100 {
167
0
                        let d1 = (n % 100) << 1;
168
0
                        n /= 100;
169
0
                        curr -= 2;
170
0
                        ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
171
29.1k
                    }
172
173
                    // decode last 1 or 2 chars
174
29.1k
                    if n < 10 {
175
15.2k
                        curr -= 1;
176
15.2k
                        *buf_ptr.offset(curr) = (n as u8) + b'0';
177
15.2k
                    } else {
178
13.9k
                        let d1 = n << 1;
179
13.9k
                        curr -= 2;
180
13.9k
                        ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
181
13.9k
                    }
182
183
29.1k
                    if !is_nonnegative {
184
0
                        curr -= 1;
185
0
                        *buf_ptr.offset(curr) = b'-';
186
29.1k
                    }
187
                }
188
189
29.1k
                let len = buf.len() - curr as usize;
190
29.1k
                let bytes = unsafe { slice::from_raw_parts(buf_ptr.offset(curr), len) };
191
29.1k
                unsafe { str::from_utf8_unchecked(bytes) }
192
29.1k
            }
Unexecuted instantiation: <u16 as itoa::private::Sealed>::write
<u32 as itoa::private::Sealed>::write
Line
Count
Source
134
5.82k
            fn write(self, buf: &mut [MaybeUninit<u8>; $max_len]) -> &str {
135
5.82k
                let is_nonnegative = self >= 0;
136
5.82k
                let mut n = if is_nonnegative {
137
5.82k
                    self as $conv_fn
138
                } else {
139
                    // convert the negative num to positive by summing 1 to it's 2 complement
140
0
                    (!(self as $conv_fn)).wrapping_add(1)
141
                };
142
5.82k
                let mut curr = buf.len() as isize;
143
5.82k
                let buf_ptr = buf.as_mut_ptr() as *mut u8;
144
5.82k
                let lut_ptr = DEC_DIGITS_LUT.as_ptr();
145
146
                unsafe {
147
                    // need at least 16 bits for the 4-characters-at-a-time to work.
148
5.82k
                    if mem::size_of::<$t>() >= 2 {
149
                        // eagerly decode 4 characters at a time
150
5.82k
                        while n >= 10000 {
151
0
                            let rem = (n % 10000) as isize;
152
0
                            n /= 10000;
153
0
154
0
                            let d1 = (rem / 100) << 1;
155
0
                            let d2 = (rem % 100) << 1;
156
0
                            curr -= 4;
157
0
                            ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
158
0
                            ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
159
0
                        }
160
0
                    }
161
162
                    // if we reach here numbers are <= 9999, so at most 4 chars long
163
5.82k
                    let mut n = n as isize; // possibly reduce 64bit math
164
165
                    // decode 2 more chars, if > 2 chars
166
5.82k
                    if n >= 100 {
167
5.82k
                        let d1 = (n % 100) << 1;
168
5.82k
                        n /= 100;
169
5.82k
                        curr -= 2;
170
5.82k
                        ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
171
5.82k
                    }
172
173
                    // decode last 1 or 2 chars
174
5.82k
                    if n < 10 {
175
0
                        curr -= 1;
176
0
                        *buf_ptr.offset(curr) = (n as u8) + b'0';
177
5.82k
                    } else {
178
5.82k
                        let d1 = n << 1;
179
5.82k
                        curr -= 2;
180
5.82k
                        ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
181
5.82k
                    }
182
183
5.82k
                    if !is_nonnegative {
184
0
                        curr -= 1;
185
0
                        *buf_ptr.offset(curr) = b'-';
186
5.82k
                    }
187
                }
188
189
5.82k
                let len = buf.len() - curr as usize;
190
5.82k
                let bytes = unsafe { slice::from_raw_parts(buf_ptr.offset(curr), len) };
191
5.82k
                unsafe { str::from_utf8_unchecked(bytes) }
192
5.82k
            }
193
        }
194
    )*};
195
}
196
197
const I8_MAX_LEN: usize = 4;
198
const U8_MAX_LEN: usize = 3;
199
const I16_MAX_LEN: usize = 6;
200
const U16_MAX_LEN: usize = 5;
201
const I32_MAX_LEN: usize = 11;
202
const U32_MAX_LEN: usize = 10;
203
const I64_MAX_LEN: usize = 20;
204
const U64_MAX_LEN: usize = 20;
205
206
impl_Integer!(
207
    I8_MAX_LEN => i8,
208
    U8_MAX_LEN => u8,
209
    I16_MAX_LEN => i16,
210
    U16_MAX_LEN => u16,
211
    I32_MAX_LEN => i32,
212
    U32_MAX_LEN => u32
213
    as u32);
214
215
impl_Integer!(I64_MAX_LEN => i64, U64_MAX_LEN => u64 as u64);
216
217
#[cfg(target_pointer_width = "16")]
218
impl_Integer!(I16_MAX_LEN => isize, U16_MAX_LEN => usize as u16);
219
220
#[cfg(target_pointer_width = "32")]
221
impl_Integer!(I32_MAX_LEN => isize, U32_MAX_LEN => usize as u32);
222
223
#[cfg(target_pointer_width = "64")]
224
impl_Integer!(I64_MAX_LEN => isize, U64_MAX_LEN => usize as u64);
225
226
macro_rules! impl_Integer128 {
227
    ($($max_len:expr => $t:ident),*) => {$(
228
        impl Integer for $t {}
229
230
        impl private::Sealed for $t {
231
            type Buffer = [MaybeUninit<u8>; $max_len];
232
233
            #[allow(unused_comparisons)]
234
            #[inline]
235
            #[cfg_attr(feature = "no-panic", no_panic)]
236
            fn write(self, buf: &mut [MaybeUninit<u8>; $max_len]) -> &str {
237
                let is_nonnegative = self >= 0;
238
                let n = if is_nonnegative {
239
                    self as u128
240
                } else {
241
                    // convert the negative num to positive by summing 1 to it's 2 complement
242
                    (!(self as u128)).wrapping_add(1)
243
                };
244
                let mut curr = buf.len() as isize;
245
                let buf_ptr = buf.as_mut_ptr() as *mut u8;
246
247
                unsafe {
248
                    // Divide by 10^19 which is the highest power less than 2^64.
249
                    let (n, rem) = udiv128::udivmod_1e19(n);
250
                    let buf1 = buf_ptr.offset(curr - U64_MAX_LEN as isize) as *mut [MaybeUninit<u8>; U64_MAX_LEN];
251
                    curr -= rem.write(&mut *buf1).len() as isize;
252
253
                    if n != 0 {
254
                        // Memset the base10 leading zeros of rem.
255
                        let target = buf.len() as isize - 19;
256
                        ptr::write_bytes(buf_ptr.offset(target), b'0', (curr - target) as usize);
257
                        curr = target;
258
259
                        // Divide by 10^19 again.
260
                        let (n, rem) = udiv128::udivmod_1e19(n);
261
                        let buf2 = buf_ptr.offset(curr - U64_MAX_LEN as isize) as *mut [MaybeUninit<u8>; U64_MAX_LEN];
262
                        curr -= rem.write(&mut *buf2).len() as isize;
263
264
                        if n != 0 {
265
                            // Memset the leading zeros.
266
                            let target = buf.len() as isize - 38;
267
                            ptr::write_bytes(buf_ptr.offset(target), b'0', (curr - target) as usize);
268
                            curr = target;
269
270
                            // There is at most one digit left
271
                            // because u128::max / 10^19 / 10^19 is 3.
272
                            curr -= 1;
273
                            *buf_ptr.offset(curr) = (n as u8) + b'0';
274
                        }
275
                    }
276
277
                    if !is_nonnegative {
278
                        curr -= 1;
279
                        *buf_ptr.offset(curr) = b'-';
280
                    }
281
282
                    let len = buf.len() - curr as usize;
283
                    let bytes = slice::from_raw_parts(buf_ptr.offset(curr), len);
284
                    str::from_utf8_unchecked(bytes)
285
                }
286
            }
287
        }
288
    )*};
289
}
290
291
const U128_MAX_LEN: usize = 39;
292
const I128_MAX_LEN: usize = 40;
293
294
impl_Integer128!(I128_MAX_LEN => i128, U128_MAX_LEN => u128);