Skip to content

Commit 36a3735

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

File tree

1 file changed

+60
-122
lines changed

1 file changed

+60
-122
lines changed

library/core/src/fmt/num.rs

Lines changed: 60 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,81 @@ 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-
offset -= 1;
72-
buf[offset].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:literal, $dig_tab:literal) => {
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 macro arguments at compile time.
43+
const {
44+
assert!($Unsigned::MIN == 0, "need unsigned");
45+
assert!($dig_tab.is_ascii(), "need single-byte entries");
46+
}
7847

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(true, Self::PREFIX, digits)
82-
}
83-
}
48+
// ASCII digits in ascending order are used as a lookup table.
49+
const DIG_TAB: &[u8] = $dig_tab;
50+
const BASE: $Unsigned = DIG_TAB.len() as $Unsigned;
51+
const MAX_DIG_N: usize = $Unsigned::MAX.ilog(BASE) as usize + 1;
52+
53+
// Buffer digits of self with right alignment.
54+
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DIG_N];
55+
// Count the number of bytes in buf that are not initialized.
56+
let mut offset = buf.len();
57+
58+
// Accumulate each digit of the number from the least
59+
// significant to the most significant figure.
60+
let mut remain = *self;
61+
loop {
62+
let digit = remain % BASE;
63+
remain /= BASE;
8464

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),
65+
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
66+
// and the break condition below ensures at least 1 more
67+
// decimal.
68+
unsafe { core::hint::assert_unchecked(offset >= 1) }
69+
// SAFETY: The offset counts down from its initial buf.len()
70+
// without underflow due to the previous precondition.
71+
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
72+
offset -= 1;
73+
buf[offset].write(DIG_TAB[digit as usize]);
74+
if remain == 0 {
75+
break;
76+
}
11077
}
78+
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(true, $prefix, digits)
11182
}
11283
}
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) }
12084

121-
macro_rules! int_base {
122-
(fmt::$Trait:ident for $T:ident -> $Radix:ident) => {
12385
#[stable(feature = "rust1", since = "1.0.0")]
124-
impl fmt::$Trait for $T {
86+
impl fmt::$Trait for $Signed {
87+
/// Format signed integers in the two’s-complement form.
12588
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126-
$Radix.fmt_int(*self, f)
89+
fmt::$Trait::fmt(&self.cast_unsigned(), f)
12790
}
12891
}
12992
};
13093
}
13194

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.cast_unsigned(), 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.cast_unsigned(), 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.cast_unsigned(), 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.cast_unsigned(), f)
162-
}
163-
}
95+
/// Formatting of integers with a non-decimal radix.
96+
macro_rules! radix_integers {
97+
($Signed:ident, $Unsigned:ident) => {
98+
radix_integer! { fmt::Binary for $Signed and $Unsigned, "0b", b"01" }
99+
radix_integer! { fmt::Octal for $Signed and $Unsigned, "0o", b"01234567" }
100+
radix_integer! { fmt::LowerHex for $Signed and $Unsigned, "0x", b"0123456789abcdef" }
101+
radix_integer! { fmt::UpperHex for $Signed and $Unsigned, "0x", b"0123456789ABCDEF" }
164102
};
165103
}
166-
integer! { isize, usize }
167-
integer! { i8, u8 }
168-
integer! { i16, u16 }
169-
integer! { i32, u32 }
170-
integer! { i64, u64 }
171-
integer! { i128, u128 }
104+
radix_integers! { isize, usize }
105+
radix_integers! { i8, u8 }
106+
radix_integers! { i16, u16 }
107+
radix_integers! { i32, u32 }
108+
radix_integers! { i64, u64 }
109+
radix_integers! { i128, u128 }
172110

173111
macro_rules! impl_Debug {
174112
($($T:ident)*) => {

0 commit comments

Comments
 (0)