1#![doc(html_root_url = "https://docs.rs/itoa/0.4.4")]
52#![cfg_attr(not(feature = "std"), no_std)]
53#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
54#![cfg_attr(
55 feature = "cargo-clippy",
56 allow(const_static_lifetime, transmute_ptr_to_ptr),
57)]
58
59#[cfg(feature = "i128")]
60mod udiv128;
61
62#[cfg(feature = "std")]
63use std::{fmt, io, mem, ptr, slice, str};
64
65#[cfg(not(feature = "std"))]
66use core::{fmt, mem, ptr, slice, str};
67
68#[cfg(feature = "std")]
70#[inline]
71pub fn write<W: io::Write, V: Integer>(mut wr: W, value: V) -> io::Result<usize> {
72 let mut buf = Buffer::new();
73 let s = buf.format(value);
74 try!(wr.write_all(s.as_bytes()));
75 Ok(s.len())
76}
77
78#[inline]
80pub fn fmt<W: fmt::Write, V: Integer>(mut wr: W, value: V) -> fmt::Result {
81 let mut buf = Buffer::new();
82 wr.write_str(buf.format(value))
83}
84
85#[derive(Copy)]
95pub struct Buffer {
96 bytes: [u8; I128_MAX_LEN],
97}
98
99impl Default for Buffer {
100 #[inline]
101 fn default() -> Buffer {
102 Buffer::new()
103 }
104}
105
106impl Clone for Buffer {
107 #[inline]
108 fn clone(&self) -> Self {
109 Buffer::new()
110 }
111}
112
113impl Buffer {
114 #[inline]
117 pub fn new() -> Buffer {
118 Buffer {
119 bytes: unsafe { mem::uninitialized() },
120 }
121 }
122
123 pub fn format<I: Integer>(&mut self, i: I) -> &str {
126 i.write(self)
127 }
128}
129
130mod private {
132 pub trait Sealed {}
133}
134
135pub trait Integer: private::Sealed {
139 #[doc(hidden)]
141 fn write(self, buf: &mut Buffer) -> &str;
142}
143
144trait IntegerPrivate<B> {
145 fn write_to(self, buf: &mut B) -> &[u8];
146}
147
148const DEC_DIGITS_LUT: &'static [u8] = b"\
149 0001020304050607080910111213141516171819\
150 2021222324252627282930313233343536373839\
151 4041424344454647484950515253545556575859\
152 6061626364656667686970717273747576777879\
153 8081828384858687888990919293949596979899";
154
155macro_rules! impl_IntegerCommon {
158 ($max_len:expr, $t:ident) => {
159 impl Integer for $t {
160 #[inline]
161 fn write(self, buf: &mut Buffer) -> &str {
162 unsafe {
163 debug_assert!($max_len <= I128_MAX_LEN);
164 let buf = mem::transmute::<&mut [u8; I128_MAX_LEN], &mut [u8; $max_len]>(
165 &mut buf.bytes,
166 );
167 let bytes = self.write_to(buf);
168 str::from_utf8_unchecked(bytes)
169 }
170 }
171 }
172
173 impl private::Sealed for $t {}
174 };
175}
176
177macro_rules! impl_Integer {
178 ($($max_len:expr => $t:ident),* as $conv_fn:ident) => {$(
179 impl_IntegerCommon!($max_len, $t);
180
181 impl IntegerPrivate<[u8; $max_len]> for $t {
182 #[allow(unused_comparisons)]
183 #[inline]
184 fn write_to(self, buf: &mut [u8; $max_len]) -> &[u8] {
185 let is_nonnegative = self >= 0;
186 let mut n = if is_nonnegative {
187 self as $conv_fn
188 } else {
189 (!(self as $conv_fn)).wrapping_add(1)
191 };
192 let mut curr = buf.len() as isize;
193 let buf_ptr = buf.as_mut_ptr();
194 let lut_ptr = DEC_DIGITS_LUT.as_ptr();
195
196 unsafe {
197 if mem::size_of::<$t>() >= 2 {
199 while n >= 10000 {
201 let rem = (n % 10000) as isize;
202 n /= 10000;
203
204 let d1 = (rem / 100) << 1;
205 let d2 = (rem % 100) << 1;
206 curr -= 4;
207 ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
208 ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
209 }
210 }
211
212 let mut n = n as isize; if n >= 100 {
217 let d1 = (n % 100) << 1;
218 n /= 100;
219 curr -= 2;
220 ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
221 }
222
223 if n < 10 {
225 curr -= 1;
226 *buf_ptr.offset(curr) = (n as u8) + b'0';
227 } else {
228 let d1 = n << 1;
229 curr -= 2;
230 ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
231 }
232
233 if !is_nonnegative {
234 curr -= 1;
235 *buf_ptr.offset(curr) = b'-';
236 }
237 }
238
239 let len = buf.len() - curr as usize;
240 unsafe { slice::from_raw_parts(buf_ptr.offset(curr), len) }
241 }
242 }
243 )*};
244}
245
246const I8_MAX_LEN: usize = 4;
247const U8_MAX_LEN: usize = 3;
248const I16_MAX_LEN: usize = 6;
249const U16_MAX_LEN: usize = 5;
250const I32_MAX_LEN: usize = 11;
251const U32_MAX_LEN: usize = 10;
252const I64_MAX_LEN: usize = 20;
253const U64_MAX_LEN: usize = 20;
254
255impl_Integer!(
256 I8_MAX_LEN => i8,
257 U8_MAX_LEN => u8,
258 I16_MAX_LEN => i16,
259 U16_MAX_LEN => u16,
260 I32_MAX_LEN => i32,
261 U32_MAX_LEN => u32
262 as u32);
263
264impl_Integer!(I64_MAX_LEN => i64, U64_MAX_LEN => u64 as u64);
265
266#[cfg(target_pointer_width = "16")]
267impl_Integer!(I16_MAX_LEN => isize, U16_MAX_LEN => usize as u16);
268
269#[cfg(target_pointer_width = "32")]
270impl_Integer!(I32_MAX_LEN => isize, U32_MAX_LEN => usize as u32);
271
272#[cfg(target_pointer_width = "64")]
273impl_Integer!(I64_MAX_LEN => isize, U64_MAX_LEN => usize as u64);
274
275#[cfg(all(feature = "i128"))]
276macro_rules! impl_Integer128 {
277 ($($max_len:expr => $t:ident),*) => {$(
278 impl_IntegerCommon!($max_len, $t);
279
280 impl IntegerPrivate<[u8; $max_len]> for $t {
281 #[allow(unused_comparisons)]
282 #[inline]
283 fn write_to(self, buf: &mut [u8; $max_len]) -> &[u8] {
284 let is_nonnegative = self >= 0;
285 let n = if is_nonnegative {
286 self as u128
287 } else {
288 (!(self as u128)).wrapping_add(1)
290 };
291 let mut curr = buf.len() as isize;
292 let buf_ptr = buf.as_mut_ptr();
293
294 unsafe {
295 let (n, rem) = udiv128::udivmod_1e19(n);
297 let buf1 = buf_ptr.offset(curr - U64_MAX_LEN as isize) as *mut [u8; U64_MAX_LEN];
298 curr -= rem.write_to(&mut *buf1).len() as isize;
299
300 if n != 0 {
301 let target = buf.len() as isize - 19;
303 ptr::write_bytes(buf_ptr.offset(target), b'0', (curr - target) as usize);
304 curr = target;
305
306 let (n, rem) = udiv128::udivmod_1e19(n);
308 let buf2 = buf_ptr.offset(curr - U64_MAX_LEN as isize) as *mut [u8; U64_MAX_LEN];
309 curr -= rem.write_to(&mut *buf2).len() as isize;
310
311 if n != 0 {
312 let target = buf.len() as isize - 38;
314 ptr::write_bytes(buf_ptr.offset(target), b'0', (curr - target) as usize);
315 curr = target;
316
317 curr -= 1;
320 *buf_ptr.offset(curr) = (n as u8) + b'0';
321 }
322 }
323
324 if !is_nonnegative {
325 curr -= 1;
326 *buf_ptr.offset(curr) = b'-';
327 }
328
329 let len = buf.len() - curr as usize;
330 slice::from_raw_parts(buf_ptr.offset(curr), len)
331 }
332 }
333 }
334 )*};
335}
336
337#[cfg(all(feature = "i128"))]
338const U128_MAX_LEN: usize = 39;
339const I128_MAX_LEN: usize = 40;
340
341#[cfg(all(feature = "i128"))]
342impl_Integer128!(I128_MAX_LEN => i128, U128_MAX_LEN => u128);