| 1 | //! Integer and floating-point number formatting |
| 2 | |
| 3 | use crate::mem::MaybeUninit; |
| 4 | use crate::num::fmt as numfmt; |
| 5 | use crate::ops::{Div, Rem, Sub}; |
| 6 | use crate::{fmt, ptr, slice, str}; |
| 7 | |
| 8 | #[doc (hidden)] |
| 9 | trait DisplayInt: |
| 10 | PartialEq + PartialOrd + Div<Output = Self> + Rem<Output = Self> + Sub<Output = Self> + Copy |
| 11 | { |
| 12 | fn zero() -> Self; |
| 13 | fn from_u8(u: u8) -> Self; |
| 14 | fn to_u8(&self) -> u8; |
| 15 | #[cfg (not(any(target_pointer_width = "64" , target_arch = "wasm32" )))] |
| 16 | fn to_u32(&self) -> u32; |
| 17 | fn to_u64(&self) -> u64; |
| 18 | fn to_u128(&self) -> u128; |
| 19 | } |
| 20 | |
| 21 | macro_rules! impl_int { |
| 22 | ($($t:ident)*) => ( |
| 23 | $(impl DisplayInt for $t { |
| 24 | fn zero() -> Self { 0 } |
| 25 | fn from_u8(u: u8) -> Self { u as Self } |
| 26 | fn to_u8(&self) -> u8 { *self as u8 } |
| 27 | #[cfg(not(any(target_pointer_width = "64" , target_arch = "wasm32" )))] |
| 28 | fn to_u32(&self) -> u32 { *self as u32 } |
| 29 | fn to_u64(&self) -> u64 { *self as u64 } |
| 30 | fn to_u128(&self) -> u128 { *self as u128 } |
| 31 | })* |
| 32 | ) |
| 33 | } |
| 34 | |
| 35 | impl_int! { |
| 36 | i8 i16 i32 i64 i128 isize |
| 37 | u8 u16 u32 u64 u128 usize |
| 38 | } |
| 39 | |
| 40 | /// A type that represents a specific radix |
| 41 | /// |
| 42 | /// # Safety |
| 43 | /// |
| 44 | /// `digit` must return an ASCII character. |
| 45 | #[doc (hidden)] |
| 46 | unsafe trait GenericRadix: Sized { |
| 47 | /// The number of digits. |
| 48 | const BASE: u8; |
| 49 | |
| 50 | /// A radix-specific prefix string. |
| 51 | const PREFIX: &'static str; |
| 52 | |
| 53 | /// Converts an integer to corresponding radix digit. |
| 54 | fn digit(x: u8) -> u8; |
| 55 | |
| 56 | /// Format an integer using the radix using a formatter. |
| 57 | fn fmt_int<T: DisplayInt>(&self, mut x: T, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 58 | // The radix can be as low as 2, so we need a buffer of at least 128 |
| 59 | // characters for a base 2 number. |
| 60 | let zero = T::zero(); |
| 61 | let is_nonnegative = x >= zero; |
| 62 | let mut buf = [MaybeUninit::<u8>::uninit(); 128]; |
| 63 | let mut curr = buf.len(); |
| 64 | let base = T::from_u8(Self::BASE); |
| 65 | if is_nonnegative { |
| 66 | // Accumulate each digit of the number from the least significant |
| 67 | // to the most significant figure. |
| 68 | loop { |
| 69 | let n = x % base; // Get the current place value. |
| 70 | x = x / base; // Deaccumulate the number. |
| 71 | curr -= 1; |
| 72 | buf[curr].write(Self::digit(n.to_u8())); // Store the digit in the buffer. |
| 73 | if x == zero { |
| 74 | // No more digits left to accumulate. |
| 75 | break; |
| 76 | }; |
| 77 | } |
| 78 | } else { |
| 79 | // Do the same as above, but accounting for two's complement. |
| 80 | loop { |
| 81 | let n = zero - (x % base); // Get the current place value. |
| 82 | x = x / base; // Deaccumulate the number. |
| 83 | curr -= 1; |
| 84 | buf[curr].write(Self::digit(n.to_u8())); // Store the digit in the buffer. |
| 85 | if x == zero { |
| 86 | // No more digits left to accumulate. |
| 87 | break; |
| 88 | }; |
| 89 | } |
| 90 | } |
| 91 | // SAFETY: `curr` is initialized to `buf.len()` and is only decremented, so it can't overflow. It is |
| 92 | // decremented exactly once for each digit. Since u128 is the widest fixed width integer format supported, |
| 93 | // the maximum number of digits (bits) is 128 for base-2, so `curr` won't underflow as well. |
| 94 | let buf = unsafe { buf.get_unchecked(curr..) }; |
| 95 | // SAFETY: The only chars in `buf` are created by `Self::digit` which are assumed to be |
| 96 | // valid UTF-8 |
| 97 | let buf = unsafe { |
| 98 | str::from_utf8_unchecked(slice::from_raw_parts( |
| 99 | MaybeUninit::slice_as_ptr(buf), |
| 100 | buf.len(), |
| 101 | )) |
| 102 | }; |
| 103 | f.pad_integral(is_nonnegative, Self::PREFIX, buf) |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | /// A binary (base 2) radix |
| 108 | #[derive (Clone, PartialEq)] |
| 109 | struct Binary; |
| 110 | |
| 111 | /// An octal (base 8) radix |
| 112 | #[derive (Clone, PartialEq)] |
| 113 | struct Octal; |
| 114 | |
| 115 | /// A hexadecimal (base 16) radix, formatted with lower-case characters |
| 116 | #[derive (Clone, PartialEq)] |
| 117 | struct LowerHex; |
| 118 | |
| 119 | /// A hexadecimal (base 16) radix, formatted with upper-case characters |
| 120 | #[derive (Clone, PartialEq)] |
| 121 | struct UpperHex; |
| 122 | |
| 123 | macro_rules! radix { |
| 124 | ($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => { |
| 125 | unsafe impl GenericRadix for $T { |
| 126 | const BASE: u8 = $base; |
| 127 | const PREFIX: &'static str = $prefix; |
| 128 | fn digit(x: u8) -> u8 { |
| 129 | match x { |
| 130 | $($x => $conv,)+ |
| 131 | x => panic!("number not in the range 0..={}: {}" , Self::BASE - 1, x), |
| 132 | } |
| 133 | } |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | radix! { Binary, 2, "0b" , x @ 0 ..= 1 => b'0' + x } |
| 139 | radix! { Octal, 8, "0o" , x @ 0 ..= 7 => b'0' + x } |
| 140 | radix! { LowerHex, 16, "0x" , x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'a' + (x - 10) } |
| 141 | radix! { UpperHex, 16, "0x" , x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'A' + (x - 10) } |
| 142 | |
| 143 | macro_rules! int_base { |
| 144 | (fmt::$Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { |
| 145 | #[stable(feature = "rust1" , since = "1.0.0" )] |
| 146 | impl fmt::$Trait for $T { |
| 147 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 148 | $Radix.fmt_int(*self as $U, f) |
| 149 | } |
| 150 | } |
| 151 | }; |
| 152 | } |
| 153 | |
| 154 | macro_rules! integer { |
| 155 | ($Int:ident, $Uint:ident) => { |
| 156 | int_base! { fmt::Binary for $Int as $Uint -> Binary } |
| 157 | int_base! { fmt::Octal for $Int as $Uint -> Octal } |
| 158 | int_base! { fmt::LowerHex for $Int as $Uint -> LowerHex } |
| 159 | int_base! { fmt::UpperHex for $Int as $Uint -> UpperHex } |
| 160 | |
| 161 | int_base! { fmt::Binary for $Uint as $Uint -> Binary } |
| 162 | int_base! { fmt::Octal for $Uint as $Uint -> Octal } |
| 163 | int_base! { fmt::LowerHex for $Uint as $Uint -> LowerHex } |
| 164 | int_base! { fmt::UpperHex for $Uint as $Uint -> UpperHex } |
| 165 | }; |
| 166 | } |
| 167 | integer! { isize, usize } |
| 168 | integer! { i8, u8 } |
| 169 | integer! { i16, u16 } |
| 170 | integer! { i32, u32 } |
| 171 | integer! { i64, u64 } |
| 172 | integer! { i128, u128 } |
| 173 | |
| 174 | macro_rules! impl_Debug { |
| 175 | ($($T:ident)*) => { |
| 176 | $( |
| 177 | #[stable(feature = "rust1" , since = "1.0.0" )] |
| 178 | impl fmt::Debug for $T { |
| 179 | #[inline] |
| 180 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 181 | if f.debug_lower_hex() { |
| 182 | fmt::LowerHex::fmt(self, f) |
| 183 | } else if f.debug_upper_hex() { |
| 184 | fmt::UpperHex::fmt(self, f) |
| 185 | } else { |
| 186 | fmt::Display::fmt(self, f) |
| 187 | } |
| 188 | } |
| 189 | } |
| 190 | )* |
| 191 | }; |
| 192 | } |
| 193 | |
| 194 | // 2 digit decimal look up table |
| 195 | static DEC_DIGITS_LUT: &[u8; 200] = b"\ |
| 196 | 0001020304050607080910111213141516171819\ |
| 197 | 2021222324252627282930313233343536373839\ |
| 198 | 4041424344454647484950515253545556575859\ |
| 199 | 6061626364656667686970717273747576777879\ |
| 200 | 8081828384858687888990919293949596979899" ; |
| 201 | |
| 202 | macro_rules! impl_Display { |
| 203 | ($($signed:ident, $unsigned:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => { |
| 204 | |
| 205 | $( |
| 206 | #[stable(feature = "rust1" , since = "1.0.0" )] |
| 207 | impl fmt::Display for $unsigned { |
| 208 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 209 | #[cfg(not(feature = "optimize_for_size" ))] |
| 210 | { |
| 211 | const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1; |
| 212 | // Buffer decimals for $unsigned with right alignment. |
| 213 | let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N]; |
| 214 | |
| 215 | f.pad_integral(true, "" , self._fmt(&mut buf)) |
| 216 | } |
| 217 | #[cfg(feature = "optimize_for_size" )] |
| 218 | { |
| 219 | $gen_name(self.$conv_fn(), true, f) |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | #[stable(feature = "rust1" , since = "1.0.0" )] |
| 225 | impl fmt::Display for $signed { |
| 226 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 227 | #[cfg(not(feature = "optimize_for_size" ))] |
| 228 | { |
| 229 | const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1; |
| 230 | // Buffer decimals for $unsigned with right alignment. |
| 231 | let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N]; |
| 232 | |
| 233 | f.pad_integral(*self >= 0, "" , self.unsigned_abs()._fmt(&mut buf)) |
| 234 | } |
| 235 | #[cfg(feature = "optimize_for_size" )] |
| 236 | { |
| 237 | return $gen_name(self.unsigned_abs().$conv_fn(), *self >= 0, f); |
| 238 | } |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | #[cfg(not(feature = "optimize_for_size" ))] |
| 243 | impl $unsigned { |
| 244 | #[doc(hidden)] |
| 245 | #[unstable( |
| 246 | feature = "fmt_internals" , |
| 247 | reason = "specialized method meant to only be used by `SpecToString` implementation" , |
| 248 | issue = "none" |
| 249 | )] |
| 250 | pub fn _fmt<'a>(self, buf: &'a mut [MaybeUninit::<u8>]) -> &'a str { |
| 251 | // Count the number of bytes in buf that are not initialized. |
| 252 | let mut offset = buf.len(); |
| 253 | // Consume the least-significant decimals from a working copy. |
| 254 | let mut remain = self; |
| 255 | |
| 256 | // Format per four digits from the lookup table. |
| 257 | // Four digits need a 16-bit $unsigned or wider. |
| 258 | while size_of::<Self>() > 1 && remain > 999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)" ) { |
| 259 | // SAFETY: All of the decimals fit in buf due to MAX_DEC_N |
| 260 | // and the while condition ensures at least 4 more decimals. |
| 261 | unsafe { core::hint::assert_unchecked(offset >= 4) } |
| 262 | // SAFETY: The offset counts down from its initial buf.len() |
| 263 | // without underflow due to the previous precondition. |
| 264 | unsafe { core::hint::assert_unchecked(offset <= buf.len()) } |
| 265 | offset -= 4; |
| 266 | |
| 267 | // pull two pairs |
| 268 | let scale: Self = 1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)" ); |
| 269 | let quad = remain % scale; |
| 270 | remain /= scale; |
| 271 | let pair1 = (quad / 100) as usize; |
| 272 | let pair2 = (quad % 100) as usize; |
| 273 | buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]); |
| 274 | buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]); |
| 275 | buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]); |
| 276 | buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]); |
| 277 | } |
| 278 | |
| 279 | // Format per two digits from the lookup table. |
| 280 | if remain > 9 { |
| 281 | // SAFETY: All of the decimals fit in buf due to MAX_DEC_N |
| 282 | // and the while condition ensures at least 2 more decimals. |
| 283 | unsafe { core::hint::assert_unchecked(offset >= 2) } |
| 284 | // SAFETY: The offset counts down from its initial buf.len() |
| 285 | // without underflow due to the previous precondition. |
| 286 | unsafe { core::hint::assert_unchecked(offset <= buf.len()) } |
| 287 | offset -= 2; |
| 288 | |
| 289 | let pair = (remain % 100) as usize; |
| 290 | remain /= 100; |
| 291 | buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]); |
| 292 | buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]); |
| 293 | } |
| 294 | |
| 295 | // Format the last remaining digit, if any. |
| 296 | if remain != 0 || self == 0 { |
| 297 | // SAFETY: All of the decimals fit in buf due to MAX_DEC_N |
| 298 | // and the if condition ensures (at least) 1 more decimals. |
| 299 | unsafe { core::hint::assert_unchecked(offset >= 1) } |
| 300 | // SAFETY: The offset counts down from its initial buf.len() |
| 301 | // without underflow due to the previous precondition. |
| 302 | unsafe { core::hint::assert_unchecked(offset <= buf.len()) } |
| 303 | offset -= 1; |
| 304 | |
| 305 | // Either the compiler sees that remain < 10, or it prevents |
| 306 | // a boundary check up next. |
| 307 | let last = (remain & 15) as usize; |
| 308 | buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]); |
| 309 | // not used: remain = 0; |
| 310 | } |
| 311 | |
| 312 | // SAFETY: All buf content since offset is set. |
| 313 | let written = unsafe { buf.get_unchecked(offset..) }; |
| 314 | // SAFETY: Writes use ASCII from the lookup table exclusively. |
| 315 | unsafe { |
| 316 | str::from_utf8_unchecked(slice::from_raw_parts( |
| 317 | MaybeUninit::slice_as_ptr(written), |
| 318 | written.len(), |
| 319 | )) |
| 320 | } |
| 321 | } |
| 322 | })* |
| 323 | |
| 324 | #[cfg(feature = "optimize_for_size" )] |
| 325 | fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 326 | const MAX_DEC_N: usize = $u::MAX.ilog(10) as usize + 1; |
| 327 | let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N]; |
| 328 | let mut curr = MAX_DEC_N; |
| 329 | let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); |
| 330 | |
| 331 | // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning |
| 332 | // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at |
| 333 | // each step this is kept the same as `n` is divided. Since `n` is always |
| 334 | // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]` |
| 335 | // is safe to access. |
| 336 | unsafe { |
| 337 | loop { |
| 338 | curr -= 1; |
| 339 | buf_ptr.add(curr).write((n % 10) as u8 + b'0' ); |
| 340 | n /= 10; |
| 341 | |
| 342 | if n == 0 { |
| 343 | break; |
| 344 | } |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8 |
| 349 | let buf_slice = unsafe { |
| 350 | str::from_utf8_unchecked( |
| 351 | slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr)) |
| 352 | }; |
| 353 | f.pad_integral(is_nonnegative, "" , buf_slice) |
| 354 | } |
| 355 | }; |
| 356 | } |
| 357 | |
| 358 | macro_rules! impl_Exp { |
| 359 | ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => { |
| 360 | fn $name( |
| 361 | mut n: $u, |
| 362 | is_nonnegative: bool, |
| 363 | upper: bool, |
| 364 | f: &mut fmt::Formatter<'_> |
| 365 | ) -> fmt::Result { |
| 366 | let (mut n, mut exponent, trailing_zeros, added_precision) = { |
| 367 | let mut exponent = 0; |
| 368 | // count and remove trailing decimal zeroes |
| 369 | while n % 10 == 0 && n >= 10 { |
| 370 | n /= 10; |
| 371 | exponent += 1; |
| 372 | } |
| 373 | let (added_precision, subtracted_precision) = match f.precision() { |
| 374 | Some(fmt_prec) => { |
| 375 | // number of decimal digits minus 1 |
| 376 | let mut tmp = n; |
| 377 | let mut prec = 0; |
| 378 | while tmp >= 10 { |
| 379 | tmp /= 10; |
| 380 | prec += 1; |
| 381 | } |
| 382 | (fmt_prec.saturating_sub(prec), prec.saturating_sub(fmt_prec)) |
| 383 | } |
| 384 | None => (0, 0) |
| 385 | }; |
| 386 | for _ in 1..subtracted_precision { |
| 387 | n /= 10; |
| 388 | exponent += 1; |
| 389 | } |
| 390 | if subtracted_precision != 0 { |
| 391 | let rem = n % 10; |
| 392 | n /= 10; |
| 393 | exponent += 1; |
| 394 | // round up last digit, round to even on a tie |
| 395 | if rem > 5 || (rem == 5 && (n % 2 != 0 || subtracted_precision > 1 )) { |
| 396 | n += 1; |
| 397 | // if the digit is rounded to the next power |
| 398 | // instead adjust the exponent |
| 399 | if n.ilog10() > (n - 1).ilog10() { |
| 400 | n /= 10; |
| 401 | exponent += 1; |
| 402 | } |
| 403 | } |
| 404 | } |
| 405 | (n, exponent, exponent, added_precision) |
| 406 | }; |
| 407 | |
| 408 | // Since `curr` always decreases by the number of digits copied, this means |
| 409 | // that `curr >= 0`. |
| 410 | let mut buf = [MaybeUninit::<u8>::uninit(); 40]; |
| 411 | let mut curr = buf.len(); //index for buf |
| 412 | let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); |
| 413 | let lut_ptr = DEC_DIGITS_LUT.as_ptr(); |
| 414 | |
| 415 | // decode 2 chars at a time |
| 416 | while n >= 100 { |
| 417 | let d1 = ((n % 100) as usize) << 1; |
| 418 | curr -= 2; |
| 419 | // SAFETY: `d1 <= 198`, so we can copy from `lut_ptr[d1..d1 + 2]` since |
| 420 | // `DEC_DIGITS_LUT` has a length of 200. |
| 421 | unsafe { |
| 422 | ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); |
| 423 | } |
| 424 | n /= 100; |
| 425 | exponent += 2; |
| 426 | } |
| 427 | // n is <= 99, so at most 2 chars long |
| 428 | let mut n = n as isize; // possibly reduce 64bit math |
| 429 | // decode second-to-last character |
| 430 | if n >= 10 { |
| 431 | curr -= 1; |
| 432 | // SAFETY: Safe since `40 > curr >= 0` (see comment) |
| 433 | unsafe { |
| 434 | *buf_ptr.add(curr) = (n as u8 % 10_u8) + b'0' ; |
| 435 | } |
| 436 | n /= 10; |
| 437 | exponent += 1; |
| 438 | } |
| 439 | // add decimal point iff >1 mantissa digit will be printed |
| 440 | if exponent != trailing_zeros || added_precision != 0 { |
| 441 | curr -= 1; |
| 442 | // SAFETY: Safe since `40 > curr >= 0` |
| 443 | unsafe { |
| 444 | *buf_ptr.add(curr) = b'.' ; |
| 445 | } |
| 446 | } |
| 447 | |
| 448 | // SAFETY: Safe since `40 > curr >= 0` |
| 449 | let buf_slice = unsafe { |
| 450 | // decode last character |
| 451 | curr -= 1; |
| 452 | *buf_ptr.add(curr) = (n as u8) + b'0' ; |
| 453 | |
| 454 | let len = buf.len() - curr as usize; |
| 455 | slice::from_raw_parts(buf_ptr.add(curr), len) |
| 456 | }; |
| 457 | |
| 458 | // stores 'e' (or 'E') and the up to 2-digit exponent |
| 459 | let mut exp_buf = [MaybeUninit::<u8>::uninit(); 3]; |
| 460 | let exp_ptr = MaybeUninit::slice_as_mut_ptr(&mut exp_buf); |
| 461 | // SAFETY: In either case, `exp_buf` is written within bounds and `exp_ptr[..len]` |
| 462 | // is contained within `exp_buf` since `len <= 3`. |
| 463 | let exp_slice = unsafe { |
| 464 | *exp_ptr.add(0) = if upper { b'E' } else { b'e' }; |
| 465 | let len = if exponent < 10 { |
| 466 | *exp_ptr.add(1) = (exponent as u8) + b'0' ; |
| 467 | 2 |
| 468 | } else { |
| 469 | let off = exponent << 1; |
| 470 | ptr::copy_nonoverlapping(lut_ptr.add(off), exp_ptr.add(1), 2); |
| 471 | 3 |
| 472 | }; |
| 473 | slice::from_raw_parts(exp_ptr, len) |
| 474 | }; |
| 475 | |
| 476 | let parts = &[ |
| 477 | numfmt::Part::Copy(buf_slice), |
| 478 | numfmt::Part::Zero(added_precision), |
| 479 | numfmt::Part::Copy(exp_slice), |
| 480 | ]; |
| 481 | let sign = if !is_nonnegative { |
| 482 | "-" |
| 483 | } else if f.sign_plus() { |
| 484 | "+" |
| 485 | } else { |
| 486 | "" |
| 487 | }; |
| 488 | let formatted = numfmt::Formatted { sign, parts }; |
| 489 | // SAFETY: `buf_slice` and `exp_slice` contain only ASCII characters. |
| 490 | unsafe { f.pad_formatted_parts(&formatted) } |
| 491 | } |
| 492 | |
| 493 | $( |
| 494 | #[stable(feature = "integer_exp_format" , since = "1.42.0" )] |
| 495 | impl fmt::LowerExp for $t { |
| 496 | #[allow(unused_comparisons)] |
| 497 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 498 | let is_nonnegative = *self >= 0; |
| 499 | let n = if is_nonnegative { |
| 500 | self.$conv_fn() |
| 501 | } else { |
| 502 | // convert the negative num to positive by summing 1 to its 2s complement |
| 503 | (!self.$conv_fn()).wrapping_add(1) |
| 504 | }; |
| 505 | $name(n, is_nonnegative, false, f) |
| 506 | } |
| 507 | })* |
| 508 | $( |
| 509 | #[stable(feature = "integer_exp_format" , since = "1.42.0" )] |
| 510 | impl fmt::UpperExp for $t { |
| 511 | #[allow(unused_comparisons)] |
| 512 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 513 | let is_nonnegative = *self >= 0; |
| 514 | let n = if is_nonnegative { |
| 515 | self.$conv_fn() |
| 516 | } else { |
| 517 | // convert the negative num to positive by summing 1 to its 2s complement |
| 518 | (!self.$conv_fn()).wrapping_add(1) |
| 519 | }; |
| 520 | $name(n, is_nonnegative, true, f) |
| 521 | } |
| 522 | })* |
| 523 | }; |
| 524 | } |
| 525 | |
| 526 | impl_Debug! { |
| 527 | i8 i16 i32 i64 i128 isize |
| 528 | u8 u16 u32 u64 u128 usize |
| 529 | } |
| 530 | |
| 531 | // Include wasm32 in here since it doesn't reflect the native pointer size, and |
| 532 | // often cares strongly about getting a smaller code size. |
| 533 | #[cfg (any(target_pointer_width = "64" , target_arch = "wasm32" ))] |
| 534 | mod imp { |
| 535 | use super::*; |
| 536 | impl_Display!( |
| 537 | i8, u8, |
| 538 | i16, u16, |
| 539 | i32, u32, |
| 540 | i64, u64, |
| 541 | isize, usize, |
| 542 | ; as u64 via to_u64 named fmt_u64 |
| 543 | ); |
| 544 | impl_Exp!( |
| 545 | i8, u8, i16, u16, i32, u32, i64, u64, usize, isize |
| 546 | as u64 via to_u64 named exp_u64 |
| 547 | ); |
| 548 | } |
| 549 | |
| 550 | #[cfg (not(any(target_pointer_width = "64" , target_arch = "wasm32" )))] |
| 551 | mod imp { |
| 552 | use super::*; |
| 553 | impl_Display!( |
| 554 | i8, u8, |
| 555 | i16, u16, |
| 556 | i32, u32, |
| 557 | isize, usize, |
| 558 | ; as u32 via to_u32 named fmt_u32); |
| 559 | impl_Display!( |
| 560 | i64, u64, |
| 561 | ; as u64 via to_u64 named fmt_u64); |
| 562 | |
| 563 | impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32); |
| 564 | impl_Exp!(i64, u64 as u64 via to_u64 named exp_u64); |
| 565 | } |
| 566 | impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128); |
| 567 | |
| 568 | /// Helper function for writing a u64 into `buf` going from last to first, with `curr`. |
| 569 | fn parse_u64_into<const N: usize>(mut n: u64, buf: &mut [MaybeUninit<u8>; N], curr: &mut usize) { |
| 570 | let buf_ptr = MaybeUninit::slice_as_mut_ptr(buf); |
| 571 | let lut_ptr = DEC_DIGITS_LUT.as_ptr(); |
| 572 | assert!(*curr > 19); |
| 573 | |
| 574 | // SAFETY: |
| 575 | // Writes at most 19 characters into the buffer. Guaranteed that any ptr into LUT is at most |
| 576 | // 198, so will never OOB. There is a check above that there are at least 19 characters |
| 577 | // remaining. |
| 578 | unsafe { |
| 579 | if n >= 1e16 as u64 { |
| 580 | let to_parse = n % 1e16 as u64; |
| 581 | n /= 1e16 as u64; |
| 582 | |
| 583 | // Some of these are nops but it looks more elegant this way. |
| 584 | let d1 = ((to_parse / 1e14 as u64) % 100) << 1; |
| 585 | let d2 = ((to_parse / 1e12 as u64) % 100) << 1; |
| 586 | let d3 = ((to_parse / 1e10 as u64) % 100) << 1; |
| 587 | let d4 = ((to_parse / 1e8 as u64) % 100) << 1; |
| 588 | let d5 = ((to_parse / 1e6 as u64) % 100) << 1; |
| 589 | let d6 = ((to_parse / 1e4 as u64) % 100) << 1; |
| 590 | let d7 = ((to_parse / 1e2 as u64) % 100) << 1; |
| 591 | let d8 = ((to_parse / 1e0 as u64) % 100) << 1; |
| 592 | |
| 593 | *curr -= 16; |
| 594 | |
| 595 | ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(*curr + 0), 2); |
| 596 | ptr::copy_nonoverlapping(lut_ptr.add(d2 as usize), buf_ptr.add(*curr + 2), 2); |
| 597 | ptr::copy_nonoverlapping(lut_ptr.add(d3 as usize), buf_ptr.add(*curr + 4), 2); |
| 598 | ptr::copy_nonoverlapping(lut_ptr.add(d4 as usize), buf_ptr.add(*curr + 6), 2); |
| 599 | ptr::copy_nonoverlapping(lut_ptr.add(d5 as usize), buf_ptr.add(*curr + 8), 2); |
| 600 | ptr::copy_nonoverlapping(lut_ptr.add(d6 as usize), buf_ptr.add(*curr + 10), 2); |
| 601 | ptr::copy_nonoverlapping(lut_ptr.add(d7 as usize), buf_ptr.add(*curr + 12), 2); |
| 602 | ptr::copy_nonoverlapping(lut_ptr.add(d8 as usize), buf_ptr.add(*curr + 14), 2); |
| 603 | } |
| 604 | if n >= 1e8 as u64 { |
| 605 | let to_parse = n % 1e8 as u64; |
| 606 | n /= 1e8 as u64; |
| 607 | |
| 608 | // Some of these are nops but it looks more elegant this way. |
| 609 | let d1 = ((to_parse / 1e6 as u64) % 100) << 1; |
| 610 | let d2 = ((to_parse / 1e4 as u64) % 100) << 1; |
| 611 | let d3 = ((to_parse / 1e2 as u64) % 100) << 1; |
| 612 | let d4 = ((to_parse / 1e0 as u64) % 100) << 1; |
| 613 | *curr -= 8; |
| 614 | |
| 615 | ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(*curr + 0), 2); |
| 616 | ptr::copy_nonoverlapping(lut_ptr.add(d2 as usize), buf_ptr.add(*curr + 2), 2); |
| 617 | ptr::copy_nonoverlapping(lut_ptr.add(d3 as usize), buf_ptr.add(*curr + 4), 2); |
| 618 | ptr::copy_nonoverlapping(lut_ptr.add(d4 as usize), buf_ptr.add(*curr + 6), 2); |
| 619 | } |
| 620 | // `n` < 1e8 < (1 << 32) |
| 621 | let mut n = n as u32; |
| 622 | if n >= 1e4 as u32 { |
| 623 | let to_parse = n % 1e4 as u32; |
| 624 | n /= 1e4 as u32; |
| 625 | |
| 626 | let d1 = (to_parse / 100) << 1; |
| 627 | let d2 = (to_parse % 100) << 1; |
| 628 | *curr -= 4; |
| 629 | |
| 630 | ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(*curr + 0), 2); |
| 631 | ptr::copy_nonoverlapping(lut_ptr.add(d2 as usize), buf_ptr.add(*curr + 2), 2); |
| 632 | } |
| 633 | |
| 634 | // `n` < 1e4 < (1 << 16) |
| 635 | let mut n = n as u16; |
| 636 | if n >= 100 { |
| 637 | let d1 = (n % 100) << 1; |
| 638 | n /= 100; |
| 639 | *curr -= 2; |
| 640 | ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(*curr), 2); |
| 641 | } |
| 642 | |
| 643 | // decode last 1 or 2 chars |
| 644 | if n < 10 { |
| 645 | *curr -= 1; |
| 646 | *buf_ptr.add(*curr) = (n as u8) + b'0' ; |
| 647 | } else { |
| 648 | let d1 = n << 1; |
| 649 | *curr -= 2; |
| 650 | ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(*curr), 2); |
| 651 | } |
| 652 | } |
| 653 | } |
| 654 | |
| 655 | #[stable (feature = "rust1" , since = "1.0.0" )] |
| 656 | impl fmt::Display for u128 { |
| 657 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 658 | fmt_u128(*self, is_nonnegative:true, f) |
| 659 | } |
| 660 | } |
| 661 | |
| 662 | #[stable (feature = "rust1" , since = "1.0.0" )] |
| 663 | impl fmt::Display for i128 { |
| 664 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 665 | let is_nonnegative: bool = *self >= 0; |
| 666 | let n: u128 = if is_nonnegative { |
| 667 | self.to_u128() |
| 668 | } else { |
| 669 | // convert the negative num to positive by summing 1 to its 2s complement |
| 670 | (!self.to_u128()).wrapping_add(1) |
| 671 | }; |
| 672 | fmt_u128(n, is_nonnegative, f) |
| 673 | } |
| 674 | } |
| 675 | |
| 676 | /// Specialized optimization for u128. Instead of taking two items at a time, it splits |
| 677 | /// into at most 2 u64s, and then chunks by 10e16, 10e8, 10e4, 10e2, and then 10e1. |
| 678 | /// It also has to handle 1 last item, as 10^40 > 2^128 > 10^39, whereas |
| 679 | /// 10^20 > 2^64 > 10^19. |
| 680 | fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 681 | const MAX_DEC_N: usize = u128::MAX.ilog(10) as usize + 1; |
| 682 | let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N]; |
| 683 | let mut curr = buf.len(); |
| 684 | |
| 685 | let (n, rem) = udiv_1e19(n); |
| 686 | parse_u64_into(rem, &mut buf, &mut curr); |
| 687 | |
| 688 | if n != 0 { |
| 689 | // 0 pad up to point |
| 690 | let target = buf.len() - 19; |
| 691 | // SAFETY: Guaranteed that we wrote at most 19 bytes, and there must be space |
| 692 | // remaining since it has length 39 |
| 693 | unsafe { |
| 694 | ptr::write_bytes( |
| 695 | MaybeUninit::slice_as_mut_ptr(&mut buf).add(target), |
| 696 | b'0' , |
| 697 | curr - target, |
| 698 | ); |
| 699 | } |
| 700 | curr = target; |
| 701 | |
| 702 | let (n, rem) = udiv_1e19(n); |
| 703 | parse_u64_into(rem, &mut buf, &mut curr); |
| 704 | // Should this following branch be annotated with unlikely? |
| 705 | if n != 0 { |
| 706 | let target = buf.len() - 38; |
| 707 | // The raw `buf_ptr` pointer is only valid until `buf` is used the next time, |
| 708 | // buf `buf` is not used in this scope so we are good. |
| 709 | let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); |
| 710 | // SAFETY: At this point we wrote at most 38 bytes, pad up to that point, |
| 711 | // There can only be at most 1 digit remaining. |
| 712 | unsafe { |
| 713 | ptr::write_bytes(buf_ptr.add(target), b'0' , curr - target); |
| 714 | curr = target - 1; |
| 715 | *buf_ptr.add(curr) = (n as u8) + b'0' ; |
| 716 | } |
| 717 | } |
| 718 | } |
| 719 | |
| 720 | // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid |
| 721 | // UTF-8 since `DEC_DIGITS_LUT` is |
| 722 | let buf_slice = unsafe { |
| 723 | str::from_utf8_unchecked(slice::from_raw_parts( |
| 724 | MaybeUninit::slice_as_mut_ptr(&mut buf).add(curr), |
| 725 | buf.len() - curr, |
| 726 | )) |
| 727 | }; |
| 728 | f.pad_integral(is_nonnegative, "" , buf_slice) |
| 729 | } |
| 730 | |
| 731 | /// Partition of `n` into n > 1e19 and rem <= 1e19 |
| 732 | /// |
| 733 | /// Integer division algorithm is based on the following paper: |
| 734 | /// |
| 735 | /// T. Granlund and P. Montgomery, “Division by Invariant Integers Using Multiplication” |
| 736 | /// in Proc. of the SIGPLAN94 Conference on Programming Language Design and |
| 737 | /// Implementation, 1994, pp. 61–72 |
| 738 | /// |
| 739 | fn udiv_1e19(n: u128) -> (u128, u64) { |
| 740 | const DIV: u64 = 1e19 as u64; |
| 741 | const FACTOR: u128 = 156927543384667019095894735580191660403; |
| 742 | |
| 743 | let quot: u128 = if n < 1 << 83 { |
| 744 | ((n >> 19) as u64 / (DIV >> 19)) as u128 |
| 745 | } else { |
| 746 | n.widening_mul(FACTOR).1 >> 62 |
| 747 | }; |
| 748 | |
| 749 | let rem: u64 = (n - quot * DIV as u128) as u64; |
| 750 | (quot, rem) |
| 751 | } |
| 752 | |