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&logo=docs.rs 6 //! 7 //! <br> 8 //! 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`]. 12 //! 13 //! See also [`ryu`] for printing floating point primitives. 14 //! 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 18 //! 19 //! # Example 20 //! 21 //! ``` 22 //! fn main() { 23 //! let mut buffer = itoa::Buffer::new(); 24 //! let printed = buffer.format(128u64); 25 //! assert_eq!(printed, "128"); 26 //! } 27 //! ``` 28 //! 29 //! # Performance (lower is better) 30 //! 31 //!  32 33 #![doc(html_root_url = "https://docs.rs/itoa/1.0.11")] 34 #![no_std] 35 #![allow( 36 clippy::cast_lossless, 37 clippy::cast_possible_truncation, 38 clippy::expl_impl_clone_on_copy, 39 clippy::must_use_candidate, 40 clippy::needless_doctest_main, 41 clippy::unreadable_literal 42 )] 43 44 mod udiv128; 45 46 use core::mem::{self, MaybeUninit}; 47 use core::{ptr, slice, str}; 48 #[cfg(feature = "no-panic")] 49 use no_panic::no_panic; 50 51 /// A correctly sized stack allocation for the formatted integer to be written 52 /// into. 53 /// 54 /// # Example 55 /// 56 /// ``` 57 /// let mut buffer = itoa::Buffer::new(); 58 /// let printed = buffer.format(1234); 59 /// assert_eq!(printed, "1234"); 60 /// ``` 61 pub struct Buffer { 62 bytes: [MaybeUninit<u8>; I128_MAX_LEN], 63 } 64 65 impl Default for Buffer { 66 #[inline] default() -> Buffer67 fn default() -> Buffer { 68 Buffer::new() 69 } 70 } 71 72 impl Copy for Buffer {} 73 74 impl Clone for Buffer { 75 #[inline] 76 #[allow(clippy::non_canonical_clone_impl)] // false positive https://github.com/rust-lang/rust-clippy/issues/11072 clone(&self) -> Self77 fn clone(&self) -> Self { 78 Buffer::new() 79 } 80 } 81 82 impl Buffer { 83 /// This is a cheap operation; you don't need to worry about reusing buffers 84 /// for efficiency. 85 #[inline] 86 #[cfg_attr(feature = "no-panic", no_panic)] new() -> Buffer87 pub fn new() -> Buffer { 88 let bytes = [MaybeUninit::<u8>::uninit(); I128_MAX_LEN]; 89 Buffer { bytes } 90 } 91 92 /// Print an integer into this buffer and return a reference to its string 93 /// representation within the buffer. 94 #[cfg_attr(feature = "no-panic", no_panic)] format<I: Integer>(&mut self, i: I) -> &str95 pub fn format<I: Integer>(&mut self, i: I) -> &str { 96 i.write(unsafe { 97 &mut *(&mut self.bytes as *mut [MaybeUninit<u8>; I128_MAX_LEN] 98 as *mut <I as private::Sealed>::Buffer) 99 }) 100 } 101 } 102 103 /// An integer that can be written into an [`itoa::Buffer`][Buffer]. 104 /// 105 /// This trait is sealed and cannot be implemented for types outside of itoa. 106 pub trait Integer: private::Sealed {} 107 108 // Seal to prevent downstream implementations of the Integer trait. 109 mod private { 110 pub trait Sealed: Copy { 111 type Buffer: 'static; write(self, buf: &mut Self::Buffer) -> &str112 fn write(self, buf: &mut Self::Buffer) -> &str; 113 } 114 } 115 116 const DEC_DIGITS_LUT: &[u8] = b"\ 117 0001020304050607080910111213141516171819\ 118 2021222324252627282930313233343536373839\ 119 4041424344454647484950515253545556575859\ 120 6061626364656667686970717273747576777879\ 121 8081828384858687888990919293949596979899"; 122 123 // Adaptation of the original implementation at 124 // https://github.com/rust-lang/rust/blob/b8214dc6c6fc20d0a660fb5700dca9ebf51ebe89/src/libcore/fmt/num.rs#L188-L266 125 macro_rules! impl_Integer { 126 ($($max_len:expr => $t:ident),* as $conv_fn:ident) => {$( 127 impl Integer for $t {} 128 129 impl private::Sealed for $t { 130 type Buffer = [MaybeUninit<u8>; $max_len]; 131 132 #[allow(unused_comparisons)] 133 #[inline] 134 #[cfg_attr(feature = "no-panic", no_panic)] 135 fn write(self, buf: &mut [MaybeUninit<u8>; $max_len]) -> &str { 136 let is_nonnegative = self >= 0; 137 let mut n = if is_nonnegative { 138 self as $conv_fn 139 } else { 140 // Convert negative number to positive by summing 1 to its two's complement. 141 (!(self as $conv_fn)).wrapping_add(1) 142 }; 143 let mut curr = buf.len() as isize; 144 let buf_ptr = buf.as_mut_ptr() as *mut u8; 145 let lut_ptr = DEC_DIGITS_LUT.as_ptr(); 146 147 // Need at least 16 bits for the 4-digits-at-a-time to work. 148 if mem::size_of::<$t>() >= 2 { 149 // Eagerly decode 4 digits at a time. 150 while n >= 10000 { 151 let rem = (n % 10000) as isize; 152 n /= 10000; 153 154 let d1 = (rem / 100) << 1; 155 let d2 = (rem % 100) << 1; 156 curr -= 4; 157 unsafe { 158 ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); 159 ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2); 160 } 161 } 162 } 163 164 // If we reach here, numbers are <=9999 so at most 4 digits long. 165 let mut n = n as isize; // Possibly reduce 64-bit math. 166 167 // Decode 2 more digits, if >2 digits. 168 if n >= 100 { 169 let d1 = (n % 100) << 1; 170 n /= 100; 171 curr -= 2; 172 unsafe { 173 ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); 174 } 175 } 176 177 // Decode last 1 or 2 digits. 178 if n < 10 { 179 curr -= 1; 180 unsafe { 181 *buf_ptr.offset(curr) = (n as u8) + b'0'; 182 } 183 } else { 184 let d1 = n << 1; 185 curr -= 2; 186 unsafe { 187 ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); 188 } 189 } 190 191 if !is_nonnegative { 192 curr -= 1; 193 unsafe { 194 *buf_ptr.offset(curr) = b'-'; 195 } 196 } 197 198 let len = buf.len() - curr as usize; 199 let bytes = unsafe { slice::from_raw_parts(buf_ptr.offset(curr), len) }; 200 unsafe { str::from_utf8_unchecked(bytes) } 201 } 202 } 203 )*}; 204 } 205 206 const I8_MAX_LEN: usize = 4; 207 const U8_MAX_LEN: usize = 3; 208 const I16_MAX_LEN: usize = 6; 209 const U16_MAX_LEN: usize = 5; 210 const I32_MAX_LEN: usize = 11; 211 const U32_MAX_LEN: usize = 10; 212 const I64_MAX_LEN: usize = 20; 213 const U64_MAX_LEN: usize = 20; 214 215 impl_Integer!( 216 I8_MAX_LEN => i8, 217 U8_MAX_LEN => u8, 218 I16_MAX_LEN => i16, 219 U16_MAX_LEN => u16, 220 I32_MAX_LEN => i32, 221 U32_MAX_LEN => u32 222 as u32); 223 224 impl_Integer!(I64_MAX_LEN => i64, U64_MAX_LEN => u64 as u64); 225 226 #[cfg(target_pointer_width = "16")] 227 impl_Integer!(I16_MAX_LEN => isize, U16_MAX_LEN => usize as u16); 228 229 #[cfg(target_pointer_width = "32")] 230 impl_Integer!(I32_MAX_LEN => isize, U32_MAX_LEN => usize as u32); 231 232 #[cfg(target_pointer_width = "64")] 233 impl_Integer!(I64_MAX_LEN => isize, U64_MAX_LEN => usize as u64); 234 235 macro_rules! impl_Integer128 { 236 ($($max_len:expr => $t:ident),*) => {$( 237 impl Integer for $t {} 238 239 impl private::Sealed for $t { 240 type Buffer = [MaybeUninit<u8>; $max_len]; 241 242 #[allow(unused_comparisons)] 243 #[inline] 244 #[cfg_attr(feature = "no-panic", no_panic)] 245 fn write(self, buf: &mut [MaybeUninit<u8>; $max_len]) -> &str { 246 let is_nonnegative = self >= 0; 247 let n = if is_nonnegative { 248 self as u128 249 } else { 250 // Convert negative number to positive by summing 1 to its two's complement. 251 (!(self as u128)).wrapping_add(1) 252 }; 253 let mut curr = buf.len() as isize; 254 let buf_ptr = buf.as_mut_ptr() as *mut u8; 255 256 // Divide by 10^19 which is the highest power less than 2^64. 257 let (n, rem) = udiv128::udivmod_1e19(n); 258 let buf1 = unsafe { buf_ptr.offset(curr - U64_MAX_LEN as isize) as *mut [MaybeUninit<u8>; U64_MAX_LEN] }; 259 curr -= rem.write(unsafe { &mut *buf1 }).len() as isize; 260 261 if n != 0 { 262 // Memset the base10 leading zeros of rem. 263 let target = buf.len() as isize - 19; 264 unsafe { 265 ptr::write_bytes(buf_ptr.offset(target), b'0', (curr - target) as usize); 266 } 267 curr = target; 268 269 // Divide by 10^19 again. 270 let (n, rem) = udiv128::udivmod_1e19(n); 271 let buf2 = unsafe { buf_ptr.offset(curr - U64_MAX_LEN as isize) as *mut [MaybeUninit<u8>; U64_MAX_LEN] }; 272 curr -= rem.write(unsafe { &mut *buf2 }).len() as isize; 273 274 if n != 0 { 275 // Memset the leading zeros. 276 let target = buf.len() as isize - 38; 277 unsafe { 278 ptr::write_bytes(buf_ptr.offset(target), b'0', (curr - target) as usize); 279 } 280 curr = target; 281 282 // There is at most one digit left 283 // because u128::MAX / 10^19 / 10^19 is 3. 284 curr -= 1; 285 unsafe { 286 *buf_ptr.offset(curr) = (n as u8) + b'0'; 287 } 288 } 289 } 290 291 if !is_nonnegative { 292 curr -= 1; 293 unsafe { 294 *buf_ptr.offset(curr) = b'-'; 295 } 296 } 297 298 let len = buf.len() - curr as usize; 299 let bytes = unsafe { slice::from_raw_parts(buf_ptr.offset(curr), len) }; 300 unsafe { str::from_utf8_unchecked(bytes) } 301 } 302 } 303 )*}; 304 } 305 306 const U128_MAX_LEN: usize = 39; 307 const I128_MAX_LEN: usize = 40; 308 309 impl_Integer128!(I128_MAX_LEN => i128, U128_MAX_LEN => u128); 310