1 //! [![github]](https://github.com/dtolnay/itoa) [![crates-io]](https://crates.io/crates/itoa) [![docs-rs]](https://docs.rs/itoa)
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
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`].
13 //! See also [`ryu`] for printing floating point primitives.
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
23 //! let mut buffer = itoa::Buffer::new();
24 //! let printed = buffer.format(128u64);
25 //! assert_eq!(printed, "128");
29 //! # Performance (lower is better)
31 //! ![performance](https://raw.githubusercontent.com/dtolnay/itoa/master/performance.png)
33 #![doc(html_root_url = "https://docs.rs/itoa/1.0.4")]
36 clippy
::cast_lossless
,
37 clippy
::cast_possible_truncation
,
38 clippy
::must_use_candidate
,
39 clippy
::unreadable_literal
44 use core
::mem
::{self, MaybeUninit}
;
45 use core
::{ptr, slice, str}
;
46 #[cfg(feature = "no-panic")]
47 use no_panic
::no_panic
;
49 /// A correctly sized stack allocation for the formatted integer to be written
55 /// let mut buffer = itoa::Buffer::new();
56 /// let printed = buffer.format(1234);
57 /// assert_eq!(printed, "1234");
60 bytes
: [MaybeUninit
<u8>; I128_MAX_LEN
],
63 impl Default
for Buffer
{
65 fn default() -> Buffer
{
70 impl Clone
for Buffer
{
72 fn clone(&self) -> Self {
78 /// This is a cheap operation; you don't need to worry about reusing buffers
81 #[cfg_attr(feature = "no-panic", no_panic)]
82 pub fn new() -> Buffer
{
83 let bytes
= [MaybeUninit
::<u8>::uninit(); I128_MAX_LEN
];
87 /// Print an integer into this buffer and return a reference to its string
88 /// representation within the buffer.
89 #[cfg_attr(feature = "no-panic", no_panic)]
90 pub fn format
<I
: Integer
>(&mut self, i
: I
) -> &str {
92 &mut *(&mut self.bytes
as *mut [MaybeUninit
<u8>; I128_MAX_LEN
]
93 as *mut <I
as private
::Sealed
>::Buffer
)
98 /// An integer that can be written into an [`itoa::Buffer`][Buffer].
100 /// This trait is sealed and cannot be implemented for types outside of itoa.
101 pub trait Integer
: private
::Sealed {}
103 // Seal to prevent downstream implementations of the Integer trait.
105 pub trait Sealed
: Copy
{
106 type Buffer
: '
static;
107 fn write(self, buf
: &mut Self::Buffer
) -> &str;
111 const DEC_DIGITS_LUT
: &[u8] = b
"\
112 0001020304050607080910111213141516171819\
113 2021222324252627282930313233343536373839\
114 4041424344454647484950515253545556575859\
115 6061626364656667686970717273747576777879\
116 8081828384858687888990919293949596979899";
118 // Adaptation of the original implementation at
119 // https://github.com/rust-lang/rust/blob/b8214dc6c6fc20d0a660fb5700dca9ebf51ebe89/src/libcore/fmt/num.rs#L188-L266
120 macro_rules
! impl_Integer
{
121 ($
($max_len
:expr
=> $t
:ident
),* as $conv_fn
:ident
) => {$
(
122 impl Integer
for $t {}
124 impl private
::Sealed
for $t
{
125 type Buffer
= [MaybeUninit
<u8>; $max_len
];
127 #[allow(unused_comparisons)]
129 #[cfg_attr(feature = "no-panic", no_panic)]
130 fn write(self, buf
: &mut [MaybeUninit
<u8>; $max_len
]) -> &str {
131 let is_nonnegative
= self >= 0;
132 let mut n
= if is_nonnegative
{
135 // convert the negative num to positive by summing 1 to it's 2 complement
136 (!(self as $conv_fn
)).wrapping_add(1)
138 let mut curr
= buf
.len() as isize;
139 let buf_ptr
= buf
.as_mut_ptr() as *mut u8;
140 let lut_ptr
= DEC_DIGITS_LUT
.as_ptr();
143 // need at least 16 bits for the 4-characters-at-a-time to work.
144 if mem
::size_of
::<$t
>() >= 2 {
145 // eagerly decode 4 characters at a time
147 let rem
= (n
% 10000) as isize;
150 let d1
= (rem
/ 100) << 1;
151 let d2
= (rem
% 100) << 1;
153 ptr
::copy_nonoverlapping(lut_ptr
.offset(d1
), buf_ptr
.offset(curr
), 2);
154 ptr
::copy_nonoverlapping(lut_ptr
.offset(d2
), buf_ptr
.offset(curr
+ 2), 2);
158 // if we reach here numbers are <= 9999, so at most 4 chars long
159 let mut n
= n
as isize; // possibly reduce 64bit math
161 // decode 2 more chars, if > 2 chars
163 let d1
= (n
% 100) << 1;
166 ptr
::copy_nonoverlapping(lut_ptr
.offset(d1
), buf_ptr
.offset(curr
), 2);
169 // decode last 1 or 2 chars
172 *buf_ptr
.offset(curr
) = (n
as u8) + b'
0'
;
176 ptr
::copy_nonoverlapping(lut_ptr
.offset(d1
), buf_ptr
.offset(curr
), 2);
181 *buf_ptr
.offset(curr
) = b'
-'
;
185 let len
= buf
.len() - curr
as usize;
186 let bytes
= unsafe { slice::from_raw_parts(buf_ptr.offset(curr), len) }
;
187 unsafe { str::from_utf8_unchecked(bytes) }
193 const I8_MAX_LEN
: usize = 4;
194 const U8_MAX_LEN
: usize = 3;
195 const I16_MAX_LEN
: usize = 6;
196 const U16_MAX_LEN
: usize = 5;
197 const I32_MAX_LEN
: usize = 11;
198 const U32_MAX_LEN
: usize = 10;
199 const I64_MAX_LEN
: usize = 20;
200 const U64_MAX_LEN
: usize = 20;
211 impl_Integer
!(I64_MAX_LEN
=> i64, U64_MAX_LEN
=> u64 as u64);
213 #[cfg(target_pointer_width = "16")]
214 impl_Integer
!(I16_MAX_LEN
=> isize, U16_MAX_LEN
=> usize as u16);
216 #[cfg(target_pointer_width = "32")]
217 impl_Integer
!(I32_MAX_LEN
=> isize, U32_MAX_LEN
=> usize as u32);
219 #[cfg(target_pointer_width = "64")]
220 impl_Integer
!(I64_MAX_LEN
=> isize, U64_MAX_LEN
=> usize as u64);
222 macro_rules
! impl_Integer128
{
223 ($
($max_len
:expr
=> $t
:ident
),*) => {$
(
224 impl Integer
for $t {}
226 impl private
::Sealed
for $t
{
227 type Buffer
= [MaybeUninit
<u8>; $max_len
];
229 #[allow(unused_comparisons)]
231 #[cfg_attr(feature = "no-panic", no_panic)]
232 fn write(self, buf
: &mut [MaybeUninit
<u8>; $max_len
]) -> &str {
233 let is_nonnegative
= self >= 0;
234 let n
= if is_nonnegative
{
237 // convert the negative num to positive by summing 1 to it's 2 complement
238 (!(self as u128
)).wrapping_add(1)
240 let mut curr
= buf
.len() as isize;
241 let buf_ptr
= buf
.as_mut_ptr() as *mut u8;
244 // Divide by 10^19 which is the highest power less than 2^64.
245 let (n
, rem
) = udiv128
::udivmod_1e19(n
);
246 let buf1
= buf_ptr
.offset(curr
- U64_MAX_LEN
as isize) as *mut [MaybeUninit
<u8>; U64_MAX_LEN
];
247 curr
-= rem
.write(&mut *buf1
).len() as isize;
250 // Memset the base10 leading zeros of rem.
251 let target
= buf
.len() as isize - 19;
252 ptr
::write_bytes(buf_ptr
.offset(target
), b'
0'
, (curr
- target
) as usize);
255 // Divide by 10^19 again.
256 let (n
, rem
) = udiv128
::udivmod_1e19(n
);
257 let buf2
= buf_ptr
.offset(curr
- U64_MAX_LEN
as isize) as *mut [MaybeUninit
<u8>; U64_MAX_LEN
];
258 curr
-= rem
.write(&mut *buf2
).len() as isize;
261 // Memset the leading zeros.
262 let target
= buf
.len() as isize - 38;
263 ptr
::write_bytes(buf_ptr
.offset(target
), b'
0'
, (curr
- target
) as usize);
266 // There is at most one digit left
267 // because u128::max / 10^19 / 10^19 is 3.
269 *buf_ptr
.offset(curr
) = (n
as u8) + b'
0'
;
275 *buf_ptr
.offset(curr
) = b'
-'
;
278 let len
= buf
.len() - curr
as usize;
279 let bytes
= slice
::from_raw_parts(buf_ptr
.offset(curr
), len
);
280 str::from_utf8_unchecked(bytes
)
287 const U128_MAX_LEN
: usize = 39;
288 const I128_MAX_LEN
: usize = 40;
290 impl_Integer128
!(I128_MAX_LEN
=> i128
, U128_MAX_LEN
=> u128
);