blob: 8d4582e023bfb6ff73ad51badff1540cb097150f [file] [log] [blame]
Yi Kong8884bbe2020-08-31 01:13:13 +08001//! [![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//! ![performance](https://raw.githubusercontent.com/dtolnay/itoa/master/performance.png)
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
Joel Galenson2aa19112021-09-22 11:09:12 -070059#![doc(html_root_url = "https://docs.rs/itoa/0.4.8")]
Yi Kong8884bbe2020-08-31 01:13:13 +080060#![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",
Joel Galenson2aa19112021-09-22 11:09:12 -070064 allow(
65 expl_impl_clone_on_copy,
66 missing_errors_doc,
67 must_use_candidate,
68 transmute_ptr_to_ptr
69 )
Yi Kong8884bbe2020-08-31 01:13:13 +080070)]
71
72#[cfg(feature = "i128")]
73mod udiv128;
74
75#[cfg(feature = "std")]
76use std::{fmt, io, mem, ptr, slice, str};
77
78#[cfg(not(feature = "std"))]
79use core::{fmt, mem, ptr, slice, str};
80
81/// Write integer to an `io::Write`.
82#[cfg(feature = "std")]
83#[inline]
84pub 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]
95pub 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)]
110pub struct Buffer {
111 bytes: [u8; I128_MAX_LEN],
112}
113
114impl Default for Buffer {
115 #[inline]
116 fn default() -> Buffer {
117 Buffer::new()
118 }
119}
120
121impl Clone for Buffer {
122 #[inline]
123 fn clone(&self) -> Self {
124 Buffer::new()
125 }
126}
127
128impl Buffer {
129 /// This is a cheap operation; you don't need to worry about reusing buffers
130 /// for efficiency.
131 #[inline]
132 #[allow(deprecated)]
133 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.
141 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.
147mod 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.
154pub trait Integer: private::Sealed {
155 // Not public API.
156 #[doc(hidden)]
157 fn write(self, buf: &mut Buffer) -> &str;
158}
159
160trait IntegerPrivate<B> {
161 fn write_to(self, buf: &mut B) -> &[u8];
162}
163
164const 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
173macro_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
193macro_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
262const I8_MAX_LEN: usize = 4;
263const U8_MAX_LEN: usize = 3;
264const I16_MAX_LEN: usize = 6;
265const U16_MAX_LEN: usize = 5;
266const I32_MAX_LEN: usize = 11;
267const U32_MAX_LEN: usize = 10;
268const I64_MAX_LEN: usize = 20;
269const U64_MAX_LEN: usize = 20;
270
271impl_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
280impl_Integer!(I64_MAX_LEN => i64, U64_MAX_LEN => u64 as u64);
281
282#[cfg(target_pointer_width = "16")]
283impl_Integer!(I16_MAX_LEN => isize, U16_MAX_LEN => usize as u16);
284
285#[cfg(target_pointer_width = "32")]
286impl_Integer!(I32_MAX_LEN => isize, U32_MAX_LEN => usize as u32);
287
288#[cfg(target_pointer_width = "64")]
289impl_Integer!(I64_MAX_LEN => isize, U64_MAX_LEN => usize as u64);
290
291#[cfg(all(feature = "i128"))]
292macro_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"))]
354const U128_MAX_LEN: usize = 39;
355const I128_MAX_LEN: usize = 40;
356
357#[cfg(all(feature = "i128"))]
358impl_Integer128!(I128_MAX_LEN => i128, U128_MAX_LEN => u128);