core/fmt/num.rs
1//! Integer and floating-point number formatting
2
3use crate::fmt::NumBuffer;
4use crate::mem::MaybeUninit;
5use crate::num::imp::fmt as numfmt;
6use crate::{fmt, str};
7
8/// Formatting of integers with a non-decimal radix.
9macro_rules! radix_integer {
10 (fmt::$Trait:ident for $Signed:ident and $Unsigned:ident, $prefix:literal, $dig_tab:literal) => {
11 #[stable(feature = "rust1", since = "1.0.0")]
12 impl fmt::$Trait for $Unsigned {
13 /// Format unsigned integers in the radix.
14 #[ferrocene::prevalidated]
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 // Check macro arguments at compile time.
17 const {
18 assert!($Unsigned::MIN == 0, "need unsigned");
19 assert!($dig_tab.is_ascii(), "need single-byte entries");
20 }
21
22 // ASCII digits in ascending order are used as a lookup table.
23 const DIG_TAB: &[u8] = $dig_tab;
24 const BASE: $Unsigned = DIG_TAB.len() as $Unsigned;
25 const MAX_DIG_N: usize = $Unsigned::MAX.ilog(BASE) as usize + 1;
26
27 // Buffer digits of self with right alignment.
28 let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DIG_N];
29 // Count the number of bytes in buf that are not initialized.
30 let mut offset = buf.len();
31
32 // Accumulate each digit of the number from the least
33 // significant to the most significant figure.
34 let mut remain = *self;
35 loop {
36 let digit = remain % BASE;
37 remain /= BASE;
38
39 offset -= 1;
40 // SAFETY: `remain` will reach 0 and we will break before `offset` wraps
41 unsafe { core::hint::assert_unchecked(offset < buf.len()) }
42 buf[offset].write(DIG_TAB[digit as usize]);
43 if remain == 0 {
44 break;
45 }
46 }
47
48 // SAFETY: Starting from `offset`, all elements of the slice have been set.
49 let digits = unsafe { slice_buffer_to_str(&buf, offset) };
50 f.pad_integral(true, $prefix, digits)
51 }
52 }
53
54 #[stable(feature = "rust1", since = "1.0.0")]
55 impl fmt::$Trait for $Signed {
56 /// Format signed integers in the two’s-complement form.
57 #[ferrocene::prevalidated]
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 fmt::$Trait::fmt(&self.cast_unsigned(), f)
60 }
61 }
62 };
63}
64
65/// Formatting of integers with a non-decimal radix.
66macro_rules! radix_integers {
67 ($Signed:ident, $Unsigned:ident) => {
68 radix_integer! { fmt::Binary for $Signed and $Unsigned, "0b", b"01" }
69 radix_integer! { fmt::Octal for $Signed and $Unsigned, "0o", b"01234567" }
70 radix_integer! { fmt::LowerHex for $Signed and $Unsigned, "0x", b"0123456789abcdef" }
71 radix_integer! { fmt::UpperHex for $Signed and $Unsigned, "0x", b"0123456789ABCDEF" }
72 };
73}
74radix_integers! { isize, usize }
75radix_integers! { i8, u8 }
76radix_integers! { i16, u16 }
77radix_integers! { i32, u32 }
78radix_integers! { i64, u64 }
79radix_integers! { i128, u128 }
80
81macro_rules! impl_Debug {
82 ($($T:ident)*) => {
83 $(
84 #[stable(feature = "rust1", since = "1.0.0")]
85 impl fmt::Debug for $T {
86 #[inline]
87 #[ferrocene::prevalidated]
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 if f.debug_lower_hex() {
90 fmt::LowerHex::fmt(self, f)
91 } else if f.debug_upper_hex() {
92 fmt::UpperHex::fmt(self, f)
93 } else {
94 fmt::Display::fmt(self, f)
95 }
96 }
97 }
98 )*
99 };
100}
101
102// The string of all two-digit numbers in range 00..99 is used as a lookup table.
103static DECIMAL_PAIRS: &[u8; 200] = b"\
104 0001020304050607080910111213141516171819\
105 2021222324252627282930313233343536373839\
106 4041424344454647484950515253545556575859\
107 6061626364656667686970717273747576777879\
108 8081828384858687888990919293949596979899";
109
110/// This function converts a slice of ascii characters into a `&str` starting from `offset`.
111///
112/// # Safety
113///
114/// `buf` content starting from `offset` index MUST BE initialized and MUST BE ascii
115/// characters.
116#[ferrocene::prevalidated]
117unsafe fn slice_buffer_to_str(buf: &[MaybeUninit<u8>], offset: usize) -> &str {
118 // SAFETY: `offset` is always included between 0 and `buf`'s length.
119 let written = unsafe { buf.get_unchecked(offset..) };
120 // SAFETY: (`assume_init_ref`) All buf content since offset is set.
121 // SAFETY: (`from_utf8_unchecked`) Writes use ASCII from the lookup table exclusively.
122 unsafe { str::from_utf8_unchecked(written.assume_init_ref()) }
123}
124
125macro_rules! impl_Display {
126 ($($Signed:ident, $Unsigned:ident),* ; as $T:ident into $fmt_fn:ident) => {
127
128 $(
129 const _: () = {
130 assert!($Signed::MIN < 0, "need signed");
131 assert!($Unsigned::MIN == 0, "need unsigned");
132 assert!($Signed::BITS == $Unsigned::BITS, "need counterparts");
133 assert!($Signed::BITS <= $T::BITS, "need lossless conversion");
134 assert!($Unsigned::BITS <= $T::BITS, "need lossless conversion");
135 };
136
137 #[stable(feature = "rust1", since = "1.0.0")]
138 impl fmt::Display for $Unsigned {
139 #[ferrocene::prevalidated]
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 #[cfg(not(feature = "optimize_for_size"))]
142 {
143 const MAX_DEC_N: usize = $Unsigned::MAX.ilog10() as usize + 1;
144 // Buffer decimals for self with right alignment.
145 let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
146
147 // SAFETY: `buf` is always big enough to contain all the digits.
148 unsafe { f.pad_integral(true, "", self._fmt(&mut buf)) }
149 }
150 #[cfg(feature = "optimize_for_size")]
151 {
152 // Lossless conversion (with as) is asserted at the top of
153 // this macro.
154 ${concat($fmt_fn, _small)}(*self as $T, true, f)
155 }
156 }
157 }
158
159 #[stable(feature = "rust1", since = "1.0.0")]
160 impl fmt::Display for $Signed {
161 #[ferrocene::prevalidated]
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 #[cfg(not(feature = "optimize_for_size"))]
164 {
165 const MAX_DEC_N: usize = $Unsigned::MAX.ilog10() as usize + 1;
166 // Buffer decimals for self with right alignment.
167 let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
168
169 // SAFETY: `buf` is always big enough to contain all the digits.
170 unsafe { f.pad_integral(*self >= 0, "", self.unsigned_abs()._fmt(&mut buf)) }
171 }
172 #[cfg(feature = "optimize_for_size")]
173 {
174 // Lossless conversion (with as) is asserted at the top of
175 // this macro.
176 return ${concat($fmt_fn, _small)}(self.unsigned_abs() as $T, *self >= 0, f);
177 }
178 }
179 }
180
181 #[cfg(not(feature = "optimize_for_size"))]
182 impl $Unsigned {
183 #[doc(hidden)]
184 #[unstable(
185 feature = "fmt_internals",
186 reason = "specialized method meant to only be used by `SpecToString` implementation",
187 issue = "none"
188 )]
189 #[ferrocene::prevalidated]
190 pub unsafe fn _fmt<'a>(self, buf: &'a mut [MaybeUninit::<u8>]) -> &'a str {
191 // SAFETY: `buf` will always be big enough to contain all digits.
192 let offset = unsafe { self._fmt_inner(buf) };
193 // SAFETY: Starting from `offset`, all elements of the slice have been set.
194 unsafe { slice_buffer_to_str(buf, offset) }
195 }
196
197 #[ferrocene::prevalidated]
198 unsafe fn _fmt_inner(self, buf: &mut [MaybeUninit::<u8>]) -> usize {
199 // Count the number of bytes in buf that are not initialized.
200 let mut offset = buf.len();
201 // Consume the least-significant decimals from a working copy.
202 let mut remain = self;
203
204 // Format per four digits from the lookup table.
205 // Four digits need a 16-bit $Unsigned or wider.
206 while size_of::<Self>() > 1 && remain > 999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)") {
207 // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
208 // and the while condition ensures at least 4 more decimals.
209 unsafe { core::hint::assert_unchecked(offset >= 4) }
210 // SAFETY: The offset counts down from its initial buf.len()
211 // without underflow due to the previous precondition.
212 unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
213 offset -= 4;
214
215 // pull two pairs
216 let scale: Self = 1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)");
217 let quad = remain % scale;
218 remain /= scale;
219 let pair1 = (quad / 100) as usize;
220 let pair2 = (quad % 100) as usize;
221 buf[offset + 0].write(DECIMAL_PAIRS[pair1 * 2 + 0]);
222 buf[offset + 1].write(DECIMAL_PAIRS[pair1 * 2 + 1]);
223 buf[offset + 2].write(DECIMAL_PAIRS[pair2 * 2 + 0]);
224 buf[offset + 3].write(DECIMAL_PAIRS[pair2 * 2 + 1]);
225 }
226
227 // Format per two digits from the lookup table.
228 if remain > 9 {
229 // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
230 // and the if condition ensures at least 2 more decimals.
231 unsafe { core::hint::assert_unchecked(offset >= 2) }
232 // SAFETY: The offset counts down from its initial buf.len()
233 // without underflow due to the previous precondition.
234 unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
235 offset -= 2;
236
237 let pair = (remain % 100) as usize;
238 remain /= 100;
239 buf[offset + 0].write(DECIMAL_PAIRS[pair * 2 + 0]);
240 buf[offset + 1].write(DECIMAL_PAIRS[pair * 2 + 1]);
241 }
242
243 // Format the last remaining digit, if any.
244 if remain != 0 || self == 0 {
245 // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
246 // and the if condition ensures (at least) 1 more decimals.
247 unsafe { core::hint::assert_unchecked(offset >= 1) }
248 // SAFETY: The offset counts down from its initial buf.len()
249 // without underflow due to the previous precondition.
250 unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
251 offset -= 1;
252
253 // Either the compiler sees that remain < 10, or it prevents
254 // a boundary check up next.
255 let last = (remain & 15) as usize;
256 buf[offset].write(DECIMAL_PAIRS[last * 2 + 1]);
257 // not used: remain = 0;
258 }
259
260 offset
261 }
262 }
263
264 impl $Signed {
265 /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
266 /// type [`NumBuffer`] that is passed by the caller by mutable reference.
267 ///
268 /// # Examples
269 ///
270 /// ```
271 /// #![feature(int_format_into)]
272 /// use core::fmt::NumBuffer;
273 ///
274 #[doc = concat!("let n = 0", stringify!($Signed), ";")]
275 /// let mut buf = NumBuffer::new();
276 /// assert_eq!(n.format_into(&mut buf), "0");
277 ///
278 #[doc = concat!("let n1 = 32", stringify!($Signed), ";")]
279 /// assert_eq!(n1.format_into(&mut buf), "32");
280 ///
281 #[doc = concat!("let n2 = ", stringify!($Signed::MAX), ";")]
282 #[doc = concat!("assert_eq!(n2.format_into(&mut buf), ", stringify!($Signed::MAX), ".to_string());")]
283 /// ```
284 #[unstable(feature = "int_format_into", issue = "138215")]
285 #[ferrocene::prevalidated]
286 pub fn format_into(self, buf: &mut NumBuffer<Self>) -> &str {
287 let mut offset;
288
289 #[cfg(not(feature = "optimize_for_size"))]
290 // SAFETY: `buf` will always be big enough to contain all digits.
291 unsafe {
292 offset = self.unsigned_abs()._fmt_inner(&mut buf.buf);
293 }
294 #[cfg(feature = "optimize_for_size")]
295 {
296 // Lossless conversion (with as) is asserted at the top of
297 // this macro.
298 offset = ${concat($fmt_fn, _in_buf_small)}(self.unsigned_abs() as $T, &mut buf.buf);
299 }
300 // Only difference between signed and unsigned are these 4 lines.
301 if self < 0 {
302 offset -= 1;
303 buf.buf[offset].write(b'-');
304 }
305 // SAFETY: Starting from `offset`, all elements of the slice have been set.
306 unsafe { slice_buffer_to_str(&buf.buf, offset) }
307 }
308 }
309
310 impl $Unsigned {
311 /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
312 /// type [`NumBuffer`] that is passed by the caller by mutable reference.
313 ///
314 /// # Examples
315 ///
316 /// ```
317 /// #![feature(int_format_into)]
318 /// use core::fmt::NumBuffer;
319 ///
320 #[doc = concat!("let n = 0", stringify!($Unsigned), ";")]
321 /// let mut buf = NumBuffer::new();
322 /// assert_eq!(n.format_into(&mut buf), "0");
323 ///
324 #[doc = concat!("let n1 = 32", stringify!($Unsigned), ";")]
325 /// assert_eq!(n1.format_into(&mut buf), "32");
326 ///
327 #[doc = concat!("let n2 = ", stringify!($Unsigned::MAX), ";")]
328 #[doc = concat!("assert_eq!(n2.format_into(&mut buf), ", stringify!($Unsigned::MAX), ".to_string());")]
329 /// ```
330 #[unstable(feature = "int_format_into", issue = "138215")]
331 #[ferrocene::prevalidated]
332 pub fn format_into(self, buf: &mut NumBuffer<Self>) -> &str {
333 let offset;
334
335 #[cfg(not(feature = "optimize_for_size"))]
336 // SAFETY: `buf` will always be big enough to contain all digits.
337 unsafe {
338 offset = self._fmt_inner(&mut buf.buf);
339 }
340 #[cfg(feature = "optimize_for_size")]
341 {
342 // Lossless conversion (with as) is asserted at the top of
343 // this macro.
344 offset = ${concat($fmt_fn, _in_buf_small)}(self as $T, &mut buf.buf);
345 }
346 // SAFETY: Starting from `offset`, all elements of the slice have been set.
347 unsafe { slice_buffer_to_str(&buf.buf, offset) }
348 }
349 }
350
351 )*
352
353 #[cfg(feature = "optimize_for_size")]
354 fn ${concat($fmt_fn, _in_buf_small)}(mut n: $T, buf: &mut [MaybeUninit::<u8>]) -> usize {
355 let mut curr = buf.len();
356
357 // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning
358 // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
359 // each step this is kept the same as `n` is divided. Since `n` is always
360 // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]`
361 // is safe to access.
362 loop {
363 curr -= 1;
364 buf[curr].write((n % 10) as u8 + b'0');
365 n /= 10;
366
367 if n == 0 {
368 break;
369 }
370 }
371 curr
372 }
373
374 #[cfg(feature = "optimize_for_size")]
375 fn ${concat($fmt_fn, _small)}(n: $T, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376 const MAX_DEC_N: usize = $T::MAX.ilog(10) as usize + 1;
377 let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
378
379 let offset = ${concat($fmt_fn, _in_buf_small)}(n, &mut buf);
380 // SAFETY: Starting from `offset`, all elements of the slice have been set.
381 let buf_slice = unsafe { slice_buffer_to_str(&buf, offset) };
382 f.pad_integral(is_nonnegative, "", buf_slice)
383 }
384 };
385}
386
387macro_rules! impl_Exp {
388 ($($Signed:ident, $Unsigned:ident),* ; as $T:ident into $fmt_fn:ident) => {
389 const _: () = assert!($T::MIN == 0, "need unsigned");
390
391 #[ferrocene::prevalidated]
392 fn $fmt_fn(
393 f: &mut fmt::Formatter<'_>,
394 n: $T,
395 is_nonnegative: bool,
396 letter_e: u8
397 ) -> fmt::Result {
398 debug_assert!(letter_e.is_ascii_alphabetic(), "single-byte character");
399
400 // Print the integer as a coefficient in range (-10, 10).
401 let mut exp = n.checked_ilog10().unwrap_or(0) as usize;
402 debug_assert!(n / (10 as $T).pow(exp as u32) < 10);
403
404 // Precisison is counted as the number of digits in the fraction.
405 let mut coef_prec = exp;
406 // Keep the digits as an integer (paired with its coef_prec count).
407 let mut coef = n;
408
409 // A Formatter may set the precision to a fixed number of decimals.
410 let more_prec = match f.precision() {
411 None => {
412 // Omit any and all trailing zeroes.
413 while coef_prec != 0 && coef % 10 == 0 {
414 coef /= 10;
415 coef_prec -= 1;
416 }
417 0
418 },
419
420 Some(fmt_prec) if fmt_prec >= coef_prec => {
421 // Count the number of additional zeroes needed.
422 fmt_prec - coef_prec
423 },
424
425 Some(fmt_prec) => {
426 // Count the number of digits to drop.
427 let less_prec = coef_prec - fmt_prec;
428 assert!(less_prec > 0);
429 // Scale down the coefficient/precision pair. For example,
430 // coef 123456 gets coef_prec 5 (to make 1.23456). To format
431 // the number with 2 decimals, i.e., fmt_prec 2, coef should
432 // be scaled by 10⁵⁻²=1000 to get coef 123 with coef_prec 2.
433
434 // SAFETY: Any precision less than coef_prec will cause a
435 // power of ten below the coef value.
436 let scale = unsafe {
437 (10 as $T).checked_pow(less_prec as u32).unwrap_unchecked()
438 };
439 let floor = coef / scale;
440 // Round half to even conform documentation.
441 let over = coef % scale;
442 let half = scale / 2;
443 let round_up = if over < half {
444 0
445 } else if over > half {
446 1
447 } else {
448 floor & 1 // round odd up to even
449 };
450 // Adding one to a scale down of at least 10 won't overflow.
451 coef = floor + round_up;
452 coef_prec = fmt_prec;
453
454 // The round_up may have caused the coefficient to reach 10
455 // (which is not permitted). For example, anything in range
456 // [9.95, 10) becomes 10.0 when adjusted to precision 1.
457 if round_up != 0 && coef.checked_ilog10().unwrap_or(0) as usize > coef_prec {
458 debug_assert_eq!(coef, (10 as $T).pow(coef_prec as u32 + 1));
459 coef /= 10; // drop one trailing zero
460 exp += 1; // one power of ten higher
461 }
462 0
463 },
464 };
465
466 // Allocate a text buffer with lazy initialization.
467 const MAX_DEC_N: usize = $T::MAX.ilog10() as usize + 1;
468 const MAX_COEF_LEN: usize = MAX_DEC_N + ".".len();
469 const MAX_TEXT_LEN: usize = MAX_COEF_LEN + "e99".len();
470 let mut buf = [MaybeUninit::<u8>::uninit(); MAX_TEXT_LEN];
471
472 // Encode the coefficient in buf[..coef_len].
473 let (lead_dec, coef_len) = if coef_prec == 0 && more_prec == 0 {
474 (coef, 1_usize) // single digit; no fraction
475 } else {
476 buf[1].write(b'.');
477 let fraction_range = 2..(2 + coef_prec);
478
479 // Consume the least-significant decimals from a working copy.
480 let mut remain = coef;
481 #[cfg(feature = "optimize_for_size")] {
482 for i in fraction_range.clone().rev() {
483 let digit = (remain % 10) as usize;
484 remain /= 10;
485 buf[i].write(b'0' + digit as u8);
486 }
487 }
488 #[cfg(not(feature = "optimize_for_size"))] {
489 // Write digits per two at a time with a lookup table.
490 for i in fraction_range.clone().skip(1).rev().step_by(2) {
491 let pair = (remain % 100) as usize;
492 remain /= 100;
493 buf[i - 1].write(DECIMAL_PAIRS[pair * 2 + 0]);
494 buf[i - 0].write(DECIMAL_PAIRS[pair * 2 + 1]);
495 }
496 // An odd number of digits leave one digit remaining.
497 if coef_prec & 1 != 0 {
498 let digit = (remain % 10) as usize;
499 remain /= 10;
500 buf[fraction_range.start].write(b'0' + digit as u8);
501 }
502 }
503
504 (remain, fraction_range.end)
505 };
506 debug_assert!(lead_dec < 10);
507 debug_assert!(lead_dec != 0 || coef == 0, "significant digits only");
508 buf[0].write(b'0' + lead_dec as u8);
509
510 // SAFETY: The number of decimals is limited, captured by MAX.
511 unsafe { core::hint::assert_unchecked(coef_len <= MAX_COEF_LEN) }
512 // Encode the scale factor in buf[coef_len..text_len].
513 buf[coef_len].write(letter_e);
514 let text_len: usize = match exp {
515 ..10 => {
516 buf[coef_len + 1].write(b'0' + exp as u8);
517 coef_len + 2
518 },
519 10..100 => {
520 #[cfg(feature = "optimize_for_size")] {
521 buf[coef_len + 1].write(b'0' + (exp / 10) as u8);
522 buf[coef_len + 2].write(b'0' + (exp % 10) as u8);
523 }
524 #[cfg(not(feature = "optimize_for_size"))] {
525 buf[coef_len + 1].write(DECIMAL_PAIRS[exp * 2 + 0]);
526 buf[coef_len + 2].write(DECIMAL_PAIRS[exp * 2 + 1]);
527 }
528 coef_len + 3
529 },
530 #[ferrocene::annotation("Branch is unreachable. See SAFETY comment below.")]
531 _ => {
532 const { assert!($T::MAX.ilog10() < 100) };
533 // SAFETY: A `u256::MAX` would get exponent 77.
534 unsafe { core::hint::unreachable_unchecked() }
535 }
536 };
537 // SAFETY: All bytes up until text_len have been set.
538 let text = unsafe { buf[..text_len].assume_init_ref() };
539
540 if more_prec == 0 {
541 // SAFETY: Text is set with ASCII exclusively: either a decimal,
542 // or a LETTER_E, or a dot. ASCII implies valid UTF-8.
543 let as_str = unsafe { str::from_utf8_unchecked(text) };
544 f.pad_integral(is_nonnegative, "", as_str)
545 } else {
546 let parts = &[
547 numfmt::Part::Copy(&text[..coef_len]),
548 numfmt::Part::Zero(more_prec),
549 numfmt::Part::Copy(&text[coef_len..]),
550 ];
551 let sign = if !is_nonnegative {
552 "-"
553 } else if f.sign_plus() {
554 "+"
555 } else {
556 ""
557 };
558 // SAFETY: Text is set with ASCII exclusively: either a decimal,
559 // or a LETTER_E, or a dot. ASCII implies valid UTF-8.
560 unsafe { f.pad_formatted_parts(&numfmt::Formatted { sign, parts }) }
561 }
562 }
563
564 $(
565 const _: () = {
566 assert!($Signed::MIN < 0, "need signed");
567 assert!($Unsigned::MIN == 0, "need unsigned");
568 assert!($Signed::BITS == $Unsigned::BITS, "need counterparts");
569 assert!($Signed::BITS <= $T::BITS, "need lossless conversion");
570 assert!($Unsigned::BITS <= $T::BITS, "need lossless conversion");
571 };
572 #[stable(feature = "integer_exp_format", since = "1.42.0")]
573 impl fmt::LowerExp for $Signed {
574 #[ferrocene::prevalidated]
575 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
576 $fmt_fn(f, self.unsigned_abs() as $T, *self >= 0, b'e')
577 }
578 }
579 #[stable(feature = "integer_exp_format", since = "1.42.0")]
580 impl fmt::LowerExp for $Unsigned {
581 #[ferrocene::prevalidated]
582 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
583 $fmt_fn(f, *self as $T, true, b'e')
584 }
585 }
586 #[stable(feature = "integer_exp_format", since = "1.42.0")]
587 impl fmt::UpperExp for $Signed {
588 #[ferrocene::prevalidated]
589 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
590 $fmt_fn(f, self.unsigned_abs() as $T, *self >= 0, b'E')
591 }
592 }
593 #[stable(feature = "integer_exp_format", since = "1.42.0")]
594 impl fmt::UpperExp for $Unsigned {
595 #[ferrocene::prevalidated]
596 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
597 $fmt_fn(f, *self as $T, true, b'E')
598 }
599 }
600 )*
601
602 };
603}
604
605impl_Debug! {
606 i8 i16 i32 i64 i128 isize
607 u8 u16 u32 u64 u128 usize
608}
609
610// Include wasm32 in here since it doesn't reflect the native pointer size, and
611// often cares strongly about getting a smaller code size.
612#[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))]
613mod imp {
614 use super::*;
615 impl_Display!(i8, u8, i16, u16, i32, u32, i64, u64, isize, usize; as u64 into display_u64);
616 impl_Exp!(i8, u8, i16, u16, i32, u32, i64, u64, isize, usize; as u64 into exp_u64);
617}
618
619#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))]
620mod imp {
621 use super::*;
622 impl_Display!(i8, u8, i16, u16, i32, u32, isize, usize; as u32 into display_u32);
623 impl_Display!(i64, u64; as u64 into display_u64);
624
625 impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize; as u32 into exp_u32);
626 impl_Exp!(i64, u64; as u64 into exp_u64);
627}
628impl_Exp!(i128, u128; as u128 into exp_u128);
629
630const U128_MAX_DEC_N: usize = u128::MAX.ilog10() as usize + 1;
631
632#[stable(feature = "rust1", since = "1.0.0")]
633impl fmt::Display for u128 {
634 #[ferrocene::prevalidated]
635 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
636 let mut buf = [MaybeUninit::<u8>::uninit(); U128_MAX_DEC_N];
637
638 // SAFETY: `buf` is always big enough to contain all the digits.
639 unsafe { f.pad_integral(true, "", self._fmt(&mut buf)) }
640 }
641}
642
643#[stable(feature = "rust1", since = "1.0.0")]
644impl fmt::Display for i128 {
645 #[ferrocene::prevalidated]
646 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
647 // This is not a typo, we use the maximum number of digits of `u128`, hence why we use
648 // `U128_MAX_DEC_N`.
649 let mut buf = [MaybeUninit::<u8>::uninit(); U128_MAX_DEC_N];
650
651 let is_nonnegative = *self >= 0;
652 // SAFETY: `buf` is always big enough to contain all the digits.
653 unsafe { f.pad_integral(is_nonnegative, "", self.unsigned_abs()._fmt(&mut buf)) }
654 }
655}
656
657impl u128 {
658 /// Format optimized for u128. Computation of 128 bits is limited by processing
659 /// in batches of 16 decimals at a time.
660 #[doc(hidden)]
661 #[unstable(
662 feature = "fmt_internals",
663 reason = "specialized method meant to only be used by `SpecToString` implementation",
664 issue = "none"
665 )]
666 #[ferrocene::prevalidated]
667 pub unsafe fn _fmt<'a>(self, buf: &'a mut [MaybeUninit<u8>]) -> &'a str {
668 // SAFETY: `buf` will always be big enough to contain all digits.
669 let offset = unsafe { self._fmt_inner(buf) };
670 // SAFETY: Starting from `offset`, all elements of the slice have been set.
671 unsafe { slice_buffer_to_str(buf, offset) }
672 }
673
674 #[ferrocene::prevalidated]
675 unsafe fn _fmt_inner(self, buf: &mut [MaybeUninit<u8>]) -> usize {
676 // Optimize common-case zero, which would also need special treatment due to
677 // its "leading" zero.
678 if self == 0 {
679 let offset = buf.len() - 1;
680 buf[offset].write(b'0');
681 return offset;
682 }
683 // Take the 16 least-significant decimals.
684 let (quot_1e16, mod_1e16) = div_rem_1e16(self);
685 let (mut remain, mut offset) = if quot_1e16 == 0 {
686 (mod_1e16, U128_MAX_DEC_N)
687 } else {
688 // Write digits at buf[23..39].
689 enc_16lsd::<{ U128_MAX_DEC_N - 16 }>(buf, mod_1e16);
690
691 // Take another 16 decimals.
692 let (quot2, mod2) = div_rem_1e16(quot_1e16);
693 if quot2 == 0 {
694 (mod2, U128_MAX_DEC_N - 16)
695 } else {
696 // Write digits at buf[7..23].
697 enc_16lsd::<{ U128_MAX_DEC_N - 32 }>(buf, mod2);
698 // Quot2 has at most 7 decimals remaining after two 1e16 divisions.
699 (quot2 as u64, U128_MAX_DEC_N - 32)
700 }
701 };
702
703 // Format per four digits from the lookup table.
704 while remain > 999 {
705 // SAFETY: All of the decimals fit in buf due to U128_MAX_DEC_N
706 // and the while condition ensures at least 4 more decimals.
707 unsafe { core::hint::assert_unchecked(offset >= 4) }
708 // SAFETY: The offset counts down from its initial buf.len()
709 // without underflow due to the previous precondition.
710 unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
711 offset -= 4;
712
713 // pull two pairs
714 let quad = remain % 1_00_00;
715 remain /= 1_00_00;
716 let pair1 = (quad / 100) as usize;
717 let pair2 = (quad % 100) as usize;
718 buf[offset + 0].write(DECIMAL_PAIRS[pair1 * 2 + 0]);
719 buf[offset + 1].write(DECIMAL_PAIRS[pair1 * 2 + 1]);
720 buf[offset + 2].write(DECIMAL_PAIRS[pair2 * 2 + 0]);
721 buf[offset + 3].write(DECIMAL_PAIRS[pair2 * 2 + 1]);
722 }
723
724 // Format per two digits from the lookup table.
725 if remain > 9 {
726 // SAFETY: All of the decimals fit in buf due to U128_MAX_DEC_N
727 // and the if condition ensures at least 2 more decimals.
728 unsafe { core::hint::assert_unchecked(offset >= 2) }
729 // SAFETY: The offset counts down from its initial buf.len()
730 // without underflow due to the previous precondition.
731 unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
732 offset -= 2;
733
734 let pair = (remain % 100) as usize;
735 remain /= 100;
736 buf[offset + 0].write(DECIMAL_PAIRS[pair * 2 + 0]);
737 buf[offset + 1].write(DECIMAL_PAIRS[pair * 2 + 1]);
738 }
739
740 // Format the last remaining digit, if any.
741 if remain != 0 {
742 // SAFETY: All of the decimals fit in buf due to U128_MAX_DEC_N
743 // and the if condition ensures (at least) 1 more decimals.
744 unsafe { core::hint::assert_unchecked(offset >= 1) }
745 // SAFETY: The offset counts down from its initial buf.len()
746 // without underflow due to the previous precondition.
747 unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
748 offset -= 1;
749
750 // Either the compiler sees that remain < 10, or it prevents
751 // a boundary check up next.
752 let last = (remain & 15) as usize;
753 buf[offset].write(DECIMAL_PAIRS[last * 2 + 1]);
754 // not used: remain = 0;
755 }
756 offset
757 }
758
759 /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
760 /// type [`NumBuffer`] that is passed by the caller by mutable reference.
761 ///
762 /// # Examples
763 ///
764 /// ```
765 /// #![feature(int_format_into)]
766 /// use core::fmt::NumBuffer;
767 ///
768 /// let n = 0u128;
769 /// let mut buf = NumBuffer::new();
770 /// assert_eq!(n.format_into(&mut buf), "0");
771 ///
772 /// let n1 = 32u128;
773 /// let mut buf1 = NumBuffer::new();
774 /// assert_eq!(n1.format_into(&mut buf1), "32");
775 ///
776 /// let n2 = u128::MAX;
777 /// let mut buf2 = NumBuffer::new();
778 /// assert_eq!(n2.format_into(&mut buf2), u128::MAX.to_string());
779 /// ```
780 #[unstable(feature = "int_format_into", issue = "138215")]
781 #[ferrocene::prevalidated]
782 pub fn format_into(self, buf: &mut NumBuffer<Self>) -> &str {
783 let diff = buf.capacity() - U128_MAX_DEC_N;
784 // FIXME: Once const generics are better, use `NumberBufferTrait::BUF_SIZE` as generic const
785 // for `fmt_u128_inner`.
786 //
787 // In the meantime, we have to use a slice starting at index 1 and add 1 to the returned
788 // offset to ensure the number is correctly generated at the end of the buffer.
789 // SAFETY: `diff` will always be between 0 and its initial value.
790 unsafe { self._fmt(buf.buf.get_unchecked_mut(diff..)) }
791 }
792}
793
794impl i128 {
795 /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
796 /// type [`NumBuffer`] that is passed by the caller by mutable reference.
797 ///
798 /// # Examples
799 ///
800 /// ```
801 /// #![feature(int_format_into)]
802 /// use core::fmt::NumBuffer;
803 ///
804 /// let n = 0i128;
805 /// let mut buf = NumBuffer::new();
806 /// assert_eq!(n.format_into(&mut buf), "0");
807 ///
808 /// let n1 = i128::MIN;
809 /// assert_eq!(n1.format_into(&mut buf), i128::MIN.to_string());
810 ///
811 /// let n2 = i128::MAX;
812 /// assert_eq!(n2.format_into(&mut buf), i128::MAX.to_string());
813 /// ```
814 #[unstable(feature = "int_format_into", issue = "138215")]
815 #[ferrocene::prevalidated]
816 pub fn format_into(self, buf: &mut NumBuffer<Self>) -> &str {
817 let diff = buf.capacity() - U128_MAX_DEC_N;
818 // FIXME: Once const generics are better, use `NumberBufferTrait::BUF_SIZE` as generic const
819 // for `fmt_u128_inner`.
820 //
821 // In the meantime, we have to use a slice starting at index 1 and add 1 to the returned
822 // offset to ensure the number is correctly generated at the end of the buffer.
823 let mut offset =
824 // SAFETY: `buf` will always be big enough to contain all digits.
825 unsafe { self.unsigned_abs()._fmt_inner(buf.buf.get_unchecked_mut(diff..)) };
826 // We put back the offset at the right position.
827 offset += diff;
828 // Only difference between signed and unsigned are these 4 lines.
829 if self < 0 {
830 offset -= 1;
831 // SAFETY: `buf` will always be big enough to contain all digits plus the minus sign.
832 unsafe {
833 buf.buf.get_unchecked_mut(offset).write(b'-');
834 }
835 }
836 // SAFETY: Starting from `offset`, all elements of the slice have been set.
837 unsafe { slice_buffer_to_str(&buf.buf, offset) }
838 }
839}
840
841/// Encodes the 16 least-significant decimals of n into `buf[OFFSET .. OFFSET +
842/// 16 ]`.
843#[ferrocene::prevalidated]
844fn enc_16lsd<const OFFSET: usize>(buf: &mut [MaybeUninit<u8>], n: u64) {
845 // Consume the least-significant decimals from a working copy.
846 let mut remain = n;
847
848 // Format per four digits from the lookup table.
849 for quad_index in (0..4).rev() {
850 // pull two pairs
851 let quad = remain % 1_00_00;
852 remain /= 1_00_00;
853 let pair1 = (quad / 100) as usize;
854 let pair2 = (quad % 100) as usize;
855 buf[quad_index * 4 + OFFSET + 0].write(DECIMAL_PAIRS[pair1 * 2 + 0]);
856 buf[quad_index * 4 + OFFSET + 1].write(DECIMAL_PAIRS[pair1 * 2 + 1]);
857 buf[quad_index * 4 + OFFSET + 2].write(DECIMAL_PAIRS[pair2 * 2 + 0]);
858 buf[quad_index * 4 + OFFSET + 3].write(DECIMAL_PAIRS[pair2 * 2 + 1]);
859 }
860}
861
862/// Euclidean division plus remainder with constant 1E16 basically consumes 16
863/// decimals from n.
864///
865/// The integer division algorithm is based on the following paper:
866///
867/// T. Granlund and P. Montgomery, “Division by Invariant Integers Using Multiplication”
868/// in Proc. of the SIGPLAN94 Conference on Programming Language Design and
869/// Implementation, 1994, pp. 61–72
870///
871#[inline]
872#[ferrocene::prevalidated]
873fn div_rem_1e16(n: u128) -> (u128, u64) {
874 const D: u128 = 1_0000_0000_0000_0000;
875 // The check inlines well with the caller flow.
876 if n < D {
877 return (0, n as u64);
878 }
879
880 // These constant values are computed with the CHOOSE_MULTIPLIER procedure
881 // from the Granlund & Montgomery paper, using N=128, prec=128 and d=1E16.
882 const M_HIGH: u128 = 76624777043294442917917351357515459181;
883 const SH_POST: u8 = 51;
884
885 let quot = n.widening_mul(M_HIGH).1 >> SH_POST;
886 let rem = n - quot * D;
887 (quot, rem as u64)
888}