Coverage Report

Created: 2025-06-24 06:17

/rust/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.9/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.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
0
    fn default() -> Buffer {
67
0
        Buffer::new()
68
0
    }
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
0
    fn clone(&self) -> Self {
77
0
        Buffer::new()
78
0
    }
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
0
    pub fn new() -> Buffer {
87
0
        let bytes = [MaybeUninit::<u8>::uninit(); I128_MAX_LEN];
88
0
        Buffer { bytes }
89
0
    }
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
0
    pub fn format<I: Integer>(&mut self, i: I) -> &str {
95
0
        i.write(unsafe {
96
0
            &mut *(&mut self.bytes as *mut [MaybeUninit<u8>; I128_MAX_LEN]
97
0
                as *mut <I as private::Sealed>::Buffer)
98
0
        })
99
0
    }
Unexecuted instantiation: <itoa::Buffer>::format::<u64>
Unexecuted instantiation: <itoa::Buffer>::format::<u8>
Unexecuted instantiation: <itoa::Buffer>::format::<u32>
Unexecuted instantiation: <itoa::Buffer>::format::<u16>
Unexecuted instantiation: <itoa::Buffer>::format::<u32>
Unexecuted instantiation: <itoa::Buffer>::format::<_>
Unexecuted instantiation: <itoa::Buffer>::format::<u64>
Unexecuted instantiation: <itoa::Buffer>::format::<i64>
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
0
            fn write(self, buf: &mut [MaybeUninit<u8>; $max_len]) -> &str {
135
0
                let is_nonnegative = self >= 0;
136
0
                let mut n = if is_nonnegative {
137
0
                    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
0
                let mut curr = buf.len() as isize;
143
0
                let buf_ptr = buf.as_mut_ptr() as *mut u8;
144
0
                let lut_ptr = DEC_DIGITS_LUT.as_ptr();
145
0
146
0
                unsafe {
147
0
                    // need at least 16 bits for the 4-characters-at-a-time to work.
148
0
                    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
0
                    }
161
162
                    // if we reach here numbers are <= 9999, so at most 4 chars long
163
0
                    let mut n = n as isize; // possibly reduce 64bit math
164
0
165
0
                    // decode 2 more chars, if > 2 chars
166
0
                    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
0
                    }
172
173
                    // decode last 1 or 2 chars
174
0
                    if n < 10 {
175
0
                        curr -= 1;
176
0
                        *buf_ptr.offset(curr) = (n as u8) + b'0';
177
0
                    } else {
178
0
                        let d1 = n << 1;
179
0
                        curr -= 2;
180
0
                        ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
181
0
                    }
182
183
0
                    if !is_nonnegative {
184
0
                        curr -= 1;
185
0
                        *buf_ptr.offset(curr) = b'-';
186
0
                    }
187
                }
188
189
0
                let len = buf.len() - curr as usize;
190
0
                let bytes = unsafe { slice::from_raw_parts(buf_ptr.offset(curr), len) };
191
0
                unsafe { str::from_utf8_unchecked(bytes) }
192
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
Unexecuted instantiation: <isize as itoa::private::Sealed>::write
Unexecuted instantiation: <usize as itoa::private::Sealed>::write
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
0
            fn write(self, buf: &mut [MaybeUninit<u8>; $max_len]) -> &str {
237
0
                let is_nonnegative = self >= 0;
238
0
                let n = if is_nonnegative {
239
0
                    self as u128
240
                } else {
241
                    // convert the negative num to positive by summing 1 to it's 2 complement
242
0
                    (!(self as u128)).wrapping_add(1)
243
                };
244
0
                let mut curr = buf.len() as isize;
245
0
                let buf_ptr = buf.as_mut_ptr() as *mut u8;
246
0
247
0
                unsafe {
248
0
                    // Divide by 10^19 which is the highest power less than 2^64.
249
0
                    let (n, rem) = udiv128::udivmod_1e19(n);
250
0
                    let buf1 = buf_ptr.offset(curr - U64_MAX_LEN as isize) as *mut [MaybeUninit<u8>; U64_MAX_LEN];
251
0
                    curr -= rem.write(&mut *buf1).len() as isize;
252
0
253
0
                    if n != 0 {
254
                        // Memset the base10 leading zeros of rem.
255
0
                        let target = buf.len() as isize - 19;
256
0
                        ptr::write_bytes(buf_ptr.offset(target), b'0', (curr - target) as usize);
257
0
                        curr = target;
258
0
259
0
                        // Divide by 10^19 again.
260
0
                        let (n, rem) = udiv128::udivmod_1e19(n);
261
0
                        let buf2 = buf_ptr.offset(curr - U64_MAX_LEN as isize) as *mut [MaybeUninit<u8>; U64_MAX_LEN];
262
0
                        curr -= rem.write(&mut *buf2).len() as isize;
263
0
264
0
                        if n != 0 {
265
0
                            // Memset the leading zeros.
266
0
                            let target = buf.len() as isize - 38;
267
0
                            ptr::write_bytes(buf_ptr.offset(target), b'0', (curr - target) as usize);
268
0
                            curr = target;
269
0
270
0
                            // There is at most one digit left
271
0
                            // because u128::max / 10^19 / 10^19 is 3.
272
0
                            curr -= 1;
273
0
                            *buf_ptr.offset(curr) = (n as u8) + b'0';
274
0
                        }
275
0
                    }
276
277
0
                    if !is_nonnegative {
278
0
                        curr -= 1;
279
0
                        *buf_ptr.offset(curr) = b'-';
280
0
                    }
281
282
0
                    let len = buf.len() - curr as usize;
283
0
                    let bytes = slice::from_raw_parts(buf_ptr.offset(curr), len);
284
0
                    str::from_utf8_unchecked(bytes)
285
0
                }
286
0
            }
Unexecuted instantiation: <i128 as itoa::private::Sealed>::write
Unexecuted instantiation: <u128 as itoa::private::Sealed>::write
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);