Skip to main content

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            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15                // Check macro arguments at compile time.
16                const {
17                    assert!($Unsigned::MIN == 0, "need unsigned");
18                    assert!($dig_tab.is_ascii(), "need single-byte entries");
19                }
20
21                // ASCII digits in ascending order are used as a lookup table.
22                const DIG_TAB: &[u8] = $dig_tab;
23                const BASE: $Unsigned = DIG_TAB.len() as $Unsigned;
24                const MAX_DIG_N: usize = $Unsigned::MAX.ilog(BASE) as usize + 1;
25
26                // Buffer digits of self with right alignment.
27                let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DIG_N];
28                // Count the number of bytes in buf that are not initialized.
29                let mut offset = buf.len();
30
31                // Accumulate each digit of the number from the least
32                // significant to the most significant figure.
33                let mut remain = *self;
34                loop {
35                    let digit = remain % BASE;
36                    remain /= BASE;
37
38                    offset -= 1;
39                    // SAFETY: `remain` will reach 0 and we will break before `offset` wraps
40                    unsafe { core::hint::assert_unchecked(offset < buf.len()) }
41                    buf[offset].write(DIG_TAB[digit as usize]);
42                    if remain == 0 {
43                        break;
44                    }
45                }
46
47                // SAFETY: Starting from `offset`, all elements of the slice have been set.
48                let digits = unsafe { slice_buffer_to_str(&buf, offset) };
49                f.pad_integral(true, $prefix, digits)
50            }
51        }
52
53        #[stable(feature = "rust1", since = "1.0.0")]
54        impl fmt::$Trait for $Signed {
55            /// Format signed integers in the two’s-complement form.
56            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57                fmt::$Trait::fmt(&self.cast_unsigned(), f)
58            }
59        }
60    };
61}
62
63/// Formatting of integers with a non-decimal radix.
64macro_rules! radix_integers {
65    ($Signed:ident, $Unsigned:ident) => {
66        radix_integer! { fmt::Binary   for $Signed and $Unsigned, "0b", b"01" }
67        radix_integer! { fmt::Octal    for $Signed and $Unsigned, "0o", b"01234567" }
68        radix_integer! { fmt::LowerHex for $Signed and $Unsigned, "0x", b"0123456789abcdef" }
69        radix_integer! { fmt::UpperHex for $Signed and $Unsigned, "0x", b"0123456789ABCDEF" }
70    };
71}
72radix_integers! { isize, usize }
73radix_integers! { i8, u8 }
74radix_integers! { i16, u16 }
75radix_integers! { i32, u32 }
76radix_integers! { i64, u64 }
77radix_integers! { i128, u128 }
78
79macro_rules! impl_Debug {
80    ($($T:ident)*) => {
81        $(
82            #[stable(feature = "rust1", since = "1.0.0")]
83            impl fmt::Debug for $T {
84                #[inline]
85                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86                    if f.debug_lower_hex() {
87                        fmt::LowerHex::fmt(self, f)
88                    } else if f.debug_upper_hex() {
89                        fmt::UpperHex::fmt(self, f)
90                    } else {
91                        fmt::Display::fmt(self, f)
92                    }
93                }
94            }
95        )*
96    };
97}
98
99// The string of all two-digit numbers in range 00..99 is used as a lookup table.
100static DECIMAL_PAIRS: &[u8; 200] = b"\
101      0001020304050607080910111213141516171819\
102      2021222324252627282930313233343536373839\
103      4041424344454647484950515253545556575859\
104      6061626364656667686970717273747576777879\
105      8081828384858687888990919293949596979899";
106
107/// This function converts a slice of ascii characters into a `&str` starting from `offset`.
108///
109/// # Safety
110///
111/// `buf` content starting from `offset` index MUST BE initialized and MUST BE ascii
112/// characters.
113unsafe fn slice_buffer_to_str(buf: &[MaybeUninit<u8>], offset: usize) -> &str {
114    // SAFETY: `offset` is always included between 0 and `buf`'s length.
115    let written = unsafe { buf.get_unchecked(offset..) };
116    // SAFETY: (`assume_init_ref`) All buf content since offset is set.
117    // SAFETY: (`from_utf8_unchecked`) Writes use ASCII from the lookup table exclusively.
118    unsafe { str::from_utf8_unchecked(written.assume_init_ref()) }
119}
120
121macro_rules! impl_Display {
122    ($($Signed:ident, $Unsigned:ident),* ; as $T:ident into $fmt_fn:ident) => {
123
124        $(
125        const _: () = {
126            assert!($Signed::MIN < 0, "need signed");
127            ass