1#![doc(html_root_url = "https://docs.rs/itoa/1.0.14")]
34#![no_std]
35#![allow(
36 clippy::cast_lossless,
37 clippy::cast_possible_truncation,
38 clippy::cast_possible_wrap,
39 clippy::cast_sign_loss,
40 clippy::expl_impl_clone_on_copy,
41 clippy::must_use_candidate,
42 clippy::needless_doctest_main,
43 clippy::unreadable_literal
44)]
45
46mod udiv128;
47
48use core::hint;
49use core::mem::MaybeUninit;
50use core::{ptr, slice, str};
51#[cfg(feature = "no-panic")]
52use no_panic::no_panic;
53
54pub struct Buffer {
65 bytes: [MaybeUninit<u8>; i128::MAX_STR_LEN],
66}
67
68impl Default for Buffer {
69 #[inline]
70 fn default() -> Buffer {
71 Buffer::new()
72 }
73}
74
75impl Copy for Buffer {}
76
77impl Clone for Buffer {
78 #[inline]
79 #[allow(clippy::non_canonical_clone_impl)] fn clone(&self) -> Self {
81 Buffer::new()
82 }
83}
84
85impl Buffer {
86 #[inline]
89 #[cfg_attr(feature = "no-panic", no_panic)]
90 pub fn new() -> Buffer {
91 let bytes = [MaybeUninit::<u8>::uninit(); i128::MAX_STR_LEN];
92 Buffer { bytes }
93 }
94
95 #[cfg_attr(feature = "no-panic", no_panic)]
98 pub fn format<I: Integer>(&mut self, i: I) -> &str {
99 let string = i.write(unsafe {
100 &mut *(&mut self.bytes as *mut [MaybeUninit<u8>; i128::MAX_STR_LEN]
101 as *mut <I as private::Sealed>::Buffer)
102 });
103 if string.len() > I::MAX_STR_LEN {
104 unsafe { hint::unreachable_unchecked() };
105 }
106 string
107 }
108}
109
110pub trait Integer: private::Sealed {
114 const MAX_STR_LEN: usize;
117}
118
119mod private {
121 #[doc(hidden)]
122 pub trait Sealed: Copy {
123 #[doc(hidden)]
124 type Buffer: 'static;
125 fn write(self, buf: &mut Self::Buffer) -> &str;
126 }
127}
128
129const DEC_DIGITS_LUT: [u8; 200] = *b"\
130 0001020304050607080910111213141516171819\
131 2021222324252627282930313233343536373839\
132 4041424344454647484950515253545556575859\
133 6061626364656667686970717273747576777879\
134 8081828384858687888990919293949596979899";
135
136macro_rules! impl_Integer {
139 ($t:ty[len = $max_len:expr] as $large_unsigned:ty) => {
140 impl Integer for $t {
141 const MAX_STR_LEN: usize = $max_len;
142 }
143
144 impl private::Sealed for $t {
145 type Buffer = [MaybeUninit<u8>; $max_len];
146
147 #[allow(unused_comparisons)]
148 #[inline]
149 #[cfg_attr(feature = "no-panic", no_panic)]
150 fn write(self, buf: &mut [MaybeUninit<u8>; $max_len]) -> &str {
151 let is_nonnegative = self >= 0;
152 let mut n = if is_nonnegative {
153 self as $large_unsigned
154 } else {
155 (!(self as $large_unsigned)).wrapping_add(1)
157 };
158 let mut curr = buf.len();
159 let buf_ptr = buf.as_mut_ptr() as *mut u8;
160 let lut_ptr = DEC_DIGITS_LUT.as_ptr();
161
162 while n >= 10000 {
164 let rem = n % 10000;
165 n /= 10000;
166
167 let d1 = ((rem / 100) << 1) as usize;
168 let d2 = ((rem % 100) << 1) as usize;
169 curr -= 4;
170 unsafe {
171 ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2);
172 ptr::copy_nonoverlapping(lut_ptr.add(d2), buf_ptr.add(curr + 2), 2);
173 }
174 }
175
176 if n >= 100 {
178 let d1 = ((n % 100) << 1) as usize;
179 n /= 100;
180 curr -= 2;
181 unsafe {
182 ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2);
183 }
184 }
185
186 if n < 10 {
188 curr -= 1;
189 unsafe {
190 *buf_ptr.add(curr) = (n as u8) + b'0';
191 }
192 } else {
193 let d1 = (n << 1) as usize;
194 curr -= 2;
195 unsafe {
196 ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2);
197 }
198 }
199
200 if !is_nonnegative {
201 curr -= 1;
202 unsafe {
203 *buf_ptr.add(curr) = b'-';
204 }
205 }
206
207 let len = buf.len() - curr;
208 let bytes = unsafe { slice::from_raw_parts(buf_ptr.add(curr), len) };
209 unsafe { str::from_utf8_unchecked(bytes) }
210 }
211 }
212 };
213}
214
215impl_Integer!(i8[len = 4] as u32);
216impl_Integer!(u8[len = 3] as u32);
217impl_Integer!(i16[len = 6] as u32);
218impl_Integer!(u16[len = 5] as u32);
219impl_Integer!(i32[len = 11] as u32);
220impl_Integer!(u32[len = 10] as u32);
221impl_Integer!(i64[len = 20] as u64);
222impl_Integer!(u64[len = 20] as u64);
223
224macro_rules! impl_Integer_size {
225 ($t:ty as $primitive:ident #[cfg(target_pointer_width = $width:literal)]) => {
226 #[cfg(target_pointer_width = $width)]
227 impl Integer for $t {
228 const MAX_STR_LEN: usize = <$primitive as Integer>::MAX_STR_LEN;
229 }
230
231 #[cfg(target_pointer_width = $width)]
232 impl private::Sealed for $t {
233 type Buffer = <$primitive as private::Sealed>::Buffer;
234
235 #[inline]
236 #[cfg_attr(feature = "no-panic", no_panic)]
237 fn write(self, buf: &mut Self::Buffer) -> &str {
238 (self as $primitive).write(buf)
239 }
240 }
241 };
242}
243
244impl_Integer_size!(isize as i16 #[cfg(target_pointer_width = "16")]);
245impl_Integer_size!(usize as u16 #[cfg(target_pointer_width = "16")]);
246impl_Integer_size!(isize as i32 #[cfg(target_pointer_width = "32")]);
247impl_Integer_size!(usize as u32 #[cfg(target_pointer_width = "32")]);
248impl_Integer_size!(isize as i64 #[cfg(target_pointer_width = "64")]);
249impl_Integer_size!(usize as u64 #[cfg(target_pointer_width = "64")]);
250
251macro_rules! impl_Integer128 {
252 ($t:ty[len = $max_len:expr]) => {
253 impl Integer for $t {
254 const MAX_STR_LEN: usize = $max_len;
255 }
256
257 impl private::Sealed for $t {
258 type Buffer = [MaybeUninit<u8>; $max_len];
259
260 #[allow(unused_comparisons)]
261 #[inline]
262 #[cfg_attr(feature = "no-panic", no_panic)]
263 fn write(self, buf: &mut [MaybeUninit<u8>; $max_len]) -> &str {
264 let is_nonnegative = self >= 0;
265 let n = if is_nonnegative {
266 self as u128
267 } else {
268 (!(self as u128)).wrapping_add(1)
270 };
271 let mut curr = buf.len();
272 let buf_ptr = buf.as_mut_ptr() as *mut u8;
273
274 let (n, rem) = udiv128::udivmod_1e19(n);
276 let buf1 = unsafe {
277 buf_ptr.add(curr - u64::MAX_STR_LEN) as *mut [MaybeUninit<u8>; u64::MAX_STR_LEN]
278 };
279 curr -= rem.write(unsafe { &mut *buf1 }).len();
280
281 if n != 0 {
282 let target = buf.len() - 19;
284 unsafe {
285 ptr::write_bytes(buf_ptr.add(target), b'0', curr - target);
286 }
287 curr = target;
288
289 let (n, rem) = udiv128::udivmod_1e19(n);
291 let buf2 = unsafe {
292 buf_ptr.add(curr - u64::MAX_STR_LEN)
293 as *mut [MaybeUninit<u8>; u64::MAX_STR_LEN]
294 };
295 curr -= rem.write(unsafe { &mut *buf2 }).len();
296
297 if n != 0 {
298 let target = buf.len() - 38;
300 unsafe {
301 ptr::write_bytes(buf_ptr.add(target), b'0', curr - target);
302 }
303 curr = target;
304
305 curr -= 1;
308 unsafe {
309 *buf_ptr.add(curr) = (n as u8) + b'0';
310 }
311 }
312 }
313
314 if !is_nonnegative {
315 curr -= 1;
316 unsafe {
317 *buf_ptr.add(curr) = b'-';
318 }
319 }
320
321 let len = buf.len() - curr;
322 let bytes = unsafe { slice::from_raw_parts(buf_ptr.add(curr), len) };
323 unsafe { str::from_utf8_unchecked(bytes) }
324 }
325 }
326 };
327}
328
329impl_Integer128!(i128[len = 40]);
330impl_Integer128!(u128[len = 39]);