Skip to content

Commit cc992d0

Browse files
committed
fmt with table lookup for binary, octal and hex
* correct buffer size * no trait abstraction * similar to decimal
1 parent cd0ac7f commit cc992d0

File tree

1 file changed

+59
-122
lines changed

1 file changed

+59
-122
lines changed

library/core/src/fmt/num.rs

Lines changed: 59 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@ use crate::{fmt, ptr, slice, str};
1010
trait DisplayInt:
1111
PartialEq + PartialOrd + Div<Output = Self> + Rem<Output = Self> + Sub<Output = Self> + Copy
1212
{
13-
fn zero() -> Self;
14-
fn from_u8(u: u8) -> Self;
15-
fn to_u8(&self) -> u8;
1613
#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))]
1714
fn to_u32(&self) -> u32;
1815
fn to_u64(&self) -> u64;
@@ -22,9 +19,6 @@ trait DisplayInt:
2219
macro_rules! impl_int {
2320
($($t:ident)*) => (
2421
$(impl DisplayInt for $t {
25-
fn zero() -> Self { 0 }
26-
fn from_u8(u: u8) -> Self { u as Self }
27-
fn to_u8(&self) -> u8 { *self as u8 }
2822
#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))]
2923
fn to_u32(&self) -> u32 { *self as u32 }
3024
fn to_u64(&self) -> u64 { *self as u64 }
@@ -38,137 +32,80 @@ impl_int! {
3832
u8 u16 u32 u64 u128 usize
3933
}
4034

41-
/// A type that represents a specific radix
42-
///
43-
/// # Safety
44-
///
45-
/// `digit` must return an ASCII character.
46-
#[doc(hidden)]
47-
unsafe trait GenericRadix: Sized {
48-
/// The number of digits.
49-
const BASE: u8;
50-
51-
/// A radix-specific prefix string.
52-
const PREFIX: &'static str;
53-
54-
/// Converts an integer to corresponding radix digit.
55-
fn digit(x: u8) -> u8;
56-
57-
/// Format an unsigned integer using the radix using a formatter.
58-
fn fmt_int<T: DisplayInt>(&self, mut x: T, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59-
// The radix can be as low as 2, so we need a buffer of at least 128
60-
// characters for a base 2 number.
61-
let zero = T::zero();
62-
let mut buf = [MaybeUninit::<u8>::uninit(); 128];
63-
let mut offset = buf.len();
64-
let base = T::from_u8(Self::BASE);
65-
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-
}
35+
// Formatting of integers with a non-decimal radix.
36+
macro_rules! radix_integer {
37+
(fmt::$Trait:ident for $Signed:ident and $Unsigned:ident, $prefix:expr, $dig_tab:expr) => {
38+
#[stable(feature = "rust1", since = "1.0.0")]
39+
impl fmt::$Trait for $Unsigned {
40+
/// Format unsigned integers in the radix.
41+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42+
// Check arguments at compile time.
43+
assert!($Unsigned::MIN == 0);
44+
$dig_tab.as_ascii().unwrap();
7845

79-
// SAFETY: Starting from `offset`, all elements of the slice have been set.
80-
let digits = unsafe { slice_buffer_to_str(&buf, offset) };
81-
f.pad_integral(is_nonnegative, Self::PREFIX, digits)
82-
}
83-
}
46+
// ASCII digits in ascending order are used as a lookup table.
47+
const DIG_TAB: &[u8] = $dig_tab;
48+
const BASE: $Unsigned = DIG_TAB.len() as $Unsigned;
49+
const MAX_DIG_N: usize = $Unsigned::MAX.ilog(BASE) as usize + 1;
8450

85-
/// A binary (base 2) radix
86-
#[derive(Clone, PartialEq)]
87-
struct Binary;
88-
89-
/// An octal (base 8) radix
90-
#[derive(Clone, PartialEq)]
91-
struct Octal;
92-
93-
/// A hexadecimal (base 16) radix, formatted with lower-case characters
94-
#[derive(Clone, PartialEq)]
95-
struct LowerHex;
96-
97-
/// A hexadecimal (base 16) radix, formatted with upper-case characters
98-
#[derive(Clone, PartialEq)]
99-
struct UpperHex;
100-
101-
macro_rules! radix {
102-
($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => {
103-
unsafe impl GenericRadix for $T {
104-
const BASE: u8 = $base;
105-
const PREFIX: &'static str = $prefix;
106-
fn digit(x: u8) -> u8 {
107-
match x {
108-
$($x => $conv,)+
109-
x => panic!("number not in the range 0..={}: {}", Self::BASE - 1, x),
51+
// Buffer digits of self with right alignment.
52+
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DIG_N];
53+
// Count the number of bytes in buf that are not initialized.
54+
let mut offset = buf.len();
55+
56+
// Accumulate each digit of the number from the least
57+
// significant to the most significant figure.
58+
let mut remain = *self;
59+
loop {
60+
let digit = remain % BASE;
61+
remain /= BASE;
62+
63+
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
64+
// and the break condition below ensures at least 1 more
65+
// decimal.
66+
unsafe { core::hint::assert_unchecked(offset >= 1) }
67+
// SAFETY: The offset counts down from its initial buf.len()
68+
// without underflow due to the previous precondition.
69+
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
70+
offset -= 1;
71+
buf[offset].write(DIG_TAB[digit as usize]);
72+
if remain == 0 {
73+
break;
74+
}
11075
}
76+
77+
// SAFETY: Starting from `offset`, all elements of the slice have been set.
78+
let digits = unsafe { slice_buffer_to_str(&buf, offset) };
79+
f.pad_integral(true, $prefix, digits)
11180
}
11281
}
113-
}
114-
}
115-
116-
radix! { Binary, 2, "0b", x @ 0 ..= 1 => b'0' + x }
117-
radix! { Octal, 8, "0o", x @ 0 ..= 7 => b'0' + x }
118-
radix! { LowerHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'a' + (x - 10) }
119-
radix! { UpperHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'A' + (x - 10) }
12082

121-
macro_rules! int_base {
122-
(fmt::$Trait:ident for $T:ident -> $Radix:ident) => {
12383
#[stable(feature = "rust1", since = "1.0.0")]
124-
impl fmt::$Trait for $T {
84+
impl fmt::$Trait for $Signed {
85+
/// Format signed integers in the two’s-complement form.
12586
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126-
$Radix.fmt_int(*self, f)
87+
assert!($Signed::MIN < 0);
88+
fmt::$Trait::fmt(&(*self as $Unsigned), f)
12789
}
12890
}
12991
};
13092
}
13193

132-
macro_rules! integer {
133-
($Int:ident, $Uint:ident) => {
134-
int_base! { fmt::Binary for $Uint -> Binary }
135-
int_base! { fmt::Octal for $Uint -> Octal }
136-
int_base! { fmt::LowerHex for $Uint -> LowerHex }
137-
int_base! { fmt::UpperHex for $Uint -> UpperHex }
138-
139-
// Format signed integers as unsigned (two’s complement representation).
140-
#[stable(feature = "rust1", since = "1.0.0")]
141-
impl fmt::Binary for $Int {
142-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143-
fmt::Binary::fmt(&(*self as $Uint), f)
144-
}
145-
}
146-
#[stable(feature = "rust1", since = "1.0.0")]
147-
impl fmt::Octal for $Int {
148-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149-
fmt::Octal::fmt(&(*self as $Uint), f)
150-
}
151-
}
152-
#[stable(feature = "rust1", since = "1.0.0")]
153-
impl fmt::LowerHex for $Int {
154-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155-
fmt::LowerHex::fmt(&(*self as $Uint), f)
156-
}
157-
}
158-
#[stable(feature = "rust1", since = "1.0.0")]
159-
impl fmt::UpperHex for $Int {
160-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161-
fmt::UpperHex::fmt(&(*self as $Uint), f)
162-
}
163-
}
94+
// Formatting of integers with a non-decimal radix.
95+
macro_rules! radix_integers {
96+
($Signed:ident, $Unsigned:ident) => {
97+
radix_integer! { fmt::Binary for $Signed and $Unsigned, "0b", b"01" }
98+
radix_integer! { fmt::Octal for $Signed and $Unsigned, "0o", b"01234567" }
99+
radix_integer! { fmt::LowerHex for $Signed and $Unsigned, "0x", b"0123456789abcdef" }
100+
radix_integer! { fmt::UpperHex for $Signed and $Unsigned, "0x", b"0123456789ABCDEF" }
164101
};
165102
}
166-
integer! { isize, usize }
167-
integer! { i8, u8 }
168-
integer! { i16, u16 }
169-
integer! { i32, u32 }
170-
integer! { i64, u64 }
171-
integer! { i128, u128 }
103+
radix_integers! { isize, usize }
104+
radix_integers! { i8, u8 }
105+
radix_integers! { i16, u16 }
106+
radix_integers! { i32, u32 }
107+
radix_integers! { i64, u64 }
108+
radix_integers! { i128, u128 }
172109

173110
macro_rules! impl_Debug {
174111
($($T:ident)*) => {

0 commit comments

Comments
 (0)