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&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
6 //!
7 //! <br>
8 //!
9 //! This crate provides fast functions for printing integer primitives to an
10 //! [`io::Write`] or a [`fmt::Write`]. The implementation comes straight from
11 //! [libcore] but avoids the performance penalty of going through
12 //! [`fmt::Formatter`].
13 //!
14 //! See also [`dtoa`] for printing floating point primitives.
15 //!
16 //! [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
17 //! [`fmt::Write`]: https://doc.rust-lang.org/core/fmt/trait.Write.html
18 //! [libcore]: https://github.com/rust-lang/rust/blob/b8214dc6c6fc20d0a660fb5700dca9ebf51ebe89/src/libcore/fmt/num.rs#L201-L254
19 //! [`fmt::Formatter`]: https://doc.rust-lang.org/std/fmt/struct.Formatter.html
20 //! [`dtoa`]: https://github.com/dtolnay/dtoa
21 //!
22 //! <br>
23 //!
24 //! # Performance (lower is better)
25 //!
26 //! 
27 //!
28 //! <br>
29 //!
30 //! # Examples
31 //!
32 //! ```edition2018
33 //! use std::{fmt, io};
34 //!
35 //! fn demo_itoa_write() -> io::Result<()> {
36 //! // Write to a vector or other io::Write.
37 //! let mut buf = Vec::new();
38 //! itoa::write(&mut buf, 128u64)?;
39 //! println!("{:?}", buf);
40 //!
41 //! // Write to a stack buffer.
42 //! let mut bytes = [0u8; 20];
43 //! let n = itoa::write(&mut bytes[..], 128u64)?;
44 //! println!("{:?}", &bytes[..n]);
45 //!
46 //! Ok(())
47 //! }
48 //!
49 //! fn demo_itoa_fmt() -> fmt::Result {
50 //! // Write to a string.
51 //! let mut s = String::new();
52 //! itoa::fmt(&mut s, 128u64)?;
53 //! println!("{}", s);
54 //!
55 //! Ok(())
56 //! }
57 //! ```
58
59 #![doc(html_root_url = "https://docs.rs/itoa/0.4.8")]
60 #![cfg_attr(not(feature = "std"), no_std)]
61 #![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
62 #![cfg_attr(
63 feature = "cargo-clippy",
64 allow(
65 expl_impl_clone_on_copy,
66 missing_errors_doc,
67 must_use_candidate,
68 transmute_ptr_to_ptr
69 )
70 )]
71
72 #[cfg(feature = "i128")]
73 mod udiv128;
74
75 #[cfg(feature = "std")]
76 use std::{fmt, io, mem, ptr, slice, str};
77
78 #[cfg(not(feature = "std"))]
79 use core::{fmt, mem, ptr, slice, str};
80
81 /// Write integer to an `io::Write`.
82 #[cfg(feature = "std")]
83 #[inline]
write<W: io::Write, V: Integer>(mut wr: W, value: V) -> io::Result<usize>84 pub fn write<W: io::Write, V: Integer>(mut wr: W, value: V) -> io::Result<usize> {
85 let mut buf = Buffer::new();
86 let s = buf.format(value);
87 match wr.write_all(s.as_bytes()) {
88 Ok(()) => Ok(s.len()),
89 Err(e) => Err(e),
90 }
91 }
92
93 /// Write integer to an `fmt::Write`.
94 #[inline]
fmt<W: fmt::Write, V: Integer>(mut wr: W, value: V) -> fmt::Result95 pub fn fmt<W: fmt::Write, V: Integer>(mut wr: W, value: V) -> fmt::Result {
96 let mut buf = Buffer::new();
97 wr.write_str(buf.format(value))
98 }
99
100 /// A safe API for formatting integers to text.
101 ///
102 /// # Example
103 ///
104 /// ```
105 /// let mut buffer = itoa::Buffer::new();
106 /// let printed = buffer.format(1234);
107 /// assert_eq!(printed, "1234");
108 /// ```
109 #[derive(Copy)]
110 pub struct Buffer {
111 bytes: [u8; I128_MAX_LEN],
112 }
113
114 impl Default for Buffer {
115 #[inline]
default() -> Buffer116 fn default() -> Buffer {
117 Buffer::new()
118 }
119 }
120
121 impl Clone for Buffer {
122 #[inline]
clone(&self) -> Self123 fn clone(&self) -> Self {
124 Buffer::new()
125 }
126 }
127
128 impl Buffer {
129 /// This is a cheap operation; you don't need to worry about reusing buffers
130 /// for efficiency.
131 #[inline]
132 #[allow(deprecated)]
new() -> Buffer133 pub fn new() -> Buffer {
134 Buffer {
135 bytes: unsafe { mem::uninitialized() },
136 }
137 }
138
139 /// Print an integer into this buffer and return a reference to its string representation
140 /// within the buffer.
format<I: Integer>(&mut self, i: I) -> &str141 pub fn format<I: Integer>(&mut self, i: I) -> &str {
142 i.write(self)
143 }
144 }
145
146 // Seal to prevent downstream implementations of the Integer trait.
147 mod private {
148 pub trait Sealed {}
149 }
150
151 /// An integer that can be formatted by `itoa::write` and `itoa::fmt`.
152 ///
153 /// This trait is sealed and cannot be implemented for types outside of itoa.
154 pub trait Integer: private::Sealed {
155 // Not public API.
156 #[doc(hidden)]
write(self, buf: &mut Buffer) -> &str157 fn write(self, buf: &mut Buffer) -> &str;
158 }
159
160 trait IntegerPrivate<B> {
write_to(self, buf: &mut B) -> &[u8]161 fn write_to(self, buf: &mut B) -> &[u8];
162 }
163
164 const DEC_DIGITS_LUT: &'static [u8] = b"\
165 0001020304050607080910111213141516171819\
166 2021222324252627282930313233343536373839\
167 4041424344454647484950515253545556575859\
168 6061626364656667686970717273747576777879\
169 8081828384858687888990919293949596979899";
170
171 // Adaptation of the original implementation at
172 // https://github.com/rust-lang/rust/blob/b8214dc6c6fc20d0a660fb5700dca9ebf51ebe89/src/libcore/fmt/num.rs#L188-L266
173 macro_rules! impl_IntegerCommon {
174 ($max_len:expr, $t:ident) => {
175 impl Integer for $t {
176 #[inline]
177 fn write(self, buf: &mut Buffer) -> &str {
178 unsafe {
179 debug_assert!($max_len <= I128_MAX_LEN);
180 let buf = mem::transmute::<&mut [u8; I128_MAX_LEN], &mut [u8; $max_len]>(
181 &mut buf.bytes,
182 );
183 let bytes = self.write_to(buf);
184 str::from_utf8_unchecked(bytes)
185 }
186 }
187 }
188
189 impl private::Sealed for $t {}
190 };
191 }
192
193 macro_rules! impl_Integer {
194 ($($max_len:expr => $t:ident),* as $conv_fn:ident) => {$(
195 impl_IntegerCommon!($max_len, $t);
196
197 impl IntegerPrivate<[u8; $max_len]> for $t {
198 #[allow(unused_comparisons)]
199 #[inline]
200 fn write_to(self, buf: &mut [u8; $max_len]) -> &[u8] {
201 let is_nonnegative = self >= 0;
202 let mut n = if is_nonnegative {
203 self as $conv_fn
204 } else {
205 // convert the negative num to positive by summing 1 to it's 2 complement
206 (!(self as $conv_fn)).wrapping_add(1)
207 };
208 let mut curr = buf.len() as isize;
209 let buf_ptr = buf.as_mut_ptr();
210 let lut_ptr = DEC_DIGITS_LUT.as_ptr();
211
212 unsafe {
213 // need at least 16 bits for the 4-characters-at-a-time to work.
214 if mem::size_of::<$t>() >= 2 {
215 // eagerly decode 4 characters at a time
216 while n >= 10000 {
217 let rem = (n % 10000) as isize;
218 n /= 10000;
219
220 let d1 = (rem / 100) << 1;
221 let d2 = (rem % 100) << 1;
222 curr -= 4;
223 ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
224 ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
225 }
226 }
227
228 // if we reach here numbers are <= 9999, so at most 4 chars long
229 let mut n = n as isize; // possibly reduce 64bit math
230
231 // decode 2 more chars, if > 2 chars
232 if n >= 100 {
233 let d1 = (n % 100) << 1;
234 n /= 100;
235 curr -= 2;
236 ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
237 }
238
239 // decode last 1 or 2 chars
240 if n < 10 {
241 curr -= 1;
242 *buf_ptr.offset(curr) = (n as u8) + b'0';
243 } else {
244 let d1 = n << 1;
245 curr -= 2;
246 ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
247 }
248
249 if !is_nonnegative {
250 curr -= 1;
251 *buf_ptr.offset(curr) = b'-';
252 }
253 }
254
255 let len = buf.len() - curr as usize;
256 unsafe { slice::from_raw_parts(buf_ptr.offset(curr), len) }
257 }
258 }
259 )*};
260 }
261
262 const I8_MAX_LEN: usize = 4;
263 const U8_MAX_LEN: usize = 3;
264 const I16_MAX_LEN: usize = 6;
265 const U16_MAX_LEN: usize = 5;
266 const I32_MAX_LEN: usize = 11;
267 const U32_MAX_LEN: usize = 10;
268 const I64_MAX_LEN: usize = 20;
269 const U64_MAX_LEN: usize = 20;
270
271 impl_Integer!(
272 I8_MAX_LEN => i8,
273 U8_MAX_LEN => u8,
274 I16_MAX_LEN => i16,
275 U16_MAX_LEN => u16,
276 I32_MAX_LEN => i32,
277 U32_MAX_LEN => u32
278 as u32);
279
280 impl_Integer!(I64_MAX_LEN => i64, U64_MAX_LEN => u64 as u64);
281
282 #[cfg(target_pointer_width = "16")]
283 impl_Integer!(I16_MAX_LEN => isize, U16_MAX_LEN => usize as u16);
284
285 #[cfg(target_pointer_width = "32")]
286 impl_Integer!(I32_MAX_LEN => isize, U32_MAX_LEN => usize as u32);
287
288 #[cfg(target_pointer_width = "64")]
289 impl_Integer!(I64_MAX_LEN => isize, U64_MAX_LEN => usize as u64);
290
291 #[cfg(all(feature = "i128"))]
292 macro_rules! impl_Integer128 {
293 ($($max_len:expr => $t:ident),*) => {$(
294 impl_IntegerCommon!($max_len, $t);
295
296 impl IntegerPrivate<[u8; $max_len]> for $t {
297 #[allow(unused_comparisons)]
298 #[inline]
299 fn write_to(self, buf: &mut [u8; $max_len]) -> &[u8] {
300 let is_nonnegative = self >= 0;
301 let n = if is_nonnegative {
302 self as u128
303 } else {
304 // convert the negative num to positive by summing 1 to it's 2 complement
305 (!(self as u128)).wrapping_add(1)
306 };
307 let mut curr = buf.len() as isize;
308 let buf_ptr = buf.as_mut_ptr();
309
310 unsafe {
311 // Divide by 10^19 which is the highest power less than 2^64.
312 let (n, rem) = udiv128::udivmod_1e19(n);
313 let buf1 = buf_ptr.offset(curr - U64_MAX_LEN as isize) as *mut [u8; U64_MAX_LEN];
314 curr -= rem.write_to(&mut *buf1).len() as isize;
315
316 if n != 0 {
317 // Memset the base10 leading zeros of rem.
318 let target = buf.len() as isize - 19;
319 ptr::write_bytes(buf_ptr.offset(target), b'0', (curr - target) as usize);
320 curr = target;
321
322 // Divide by 10^19 again.
323 let (n, rem) = udiv128::udivmod_1e19(n);
324 let buf2 = buf_ptr.offset(curr - U64_MAX_LEN as isize) as *mut [u8; U64_MAX_LEN];
325 curr -= rem.write_to(&mut *buf2).len() as isize;
326
327 if n != 0 {
328 // Memset the leading zeros.
329 let target = buf.len() as isize - 38;
330 ptr::write_bytes(buf_ptr.offset(target), b'0', (curr - target) as usize);
331 curr = target;
332
333 // There is at most one digit left
334 // because u128::max / 10^19 / 10^19 is 3.
335 curr -= 1;
336 *buf_ptr.offset(curr) = (n as u8) + b'0';
337 }
338 }
339
340 if !is_nonnegative {
341 curr -= 1;
342 *buf_ptr.offset(curr) = b'-';
343 }
344
345 let len = buf.len() - curr as usize;
346 slice::from_raw_parts(buf_ptr.offset(curr), len)
347 }
348 }
349 }
350 )*};
351 }
352
353 #[cfg(all(feature = "i128"))]
354 const U128_MAX_LEN: usize = 39;
355 const I128_MAX_LEN: usize = 40;
356
357 #[cfg(all(feature = "i128"))]
358 impl_Integer128!(I128_MAX_LEN => i128, U128_MAX_LEN => u128);
359