Coverage Report

Created: 2025-06-02 07:01

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