Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit c8e628c

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

File tree

1 file changed

+66
-132
lines changed

1 file changed

+66
-132
lines changed

‎library/core/src/fmt/num.rs‎

Lines changed: 66 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ use crate::{fmt, ptr, slice, str};
99
trait DisplayInt:
1010
PartialEq + PartialOrd + Div<Output = Self> + Rem<Output = Self> + Sub<Output = Self> + Copy
1111
{
12-
fn zero() -> Self;
13-
fn from_u8(u: u8) -> Self;
14-
fn to_u8(&self) -> u8;
1512
#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))]
1613
fn to_u32(&self) -> u32;
1714
fn to_u64(&self) -> u64;
@@ -21,9 +18,6 @@ trait DisplayInt:
2118
macro_rules! impl_int {
2219
($($t:ident)*) => (
2320
$(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 }
2721
#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))]
2822
fn to_u32(&self) -> u32 { *self as u32 }
2923
fn to_u64(&self) -> u64 { *self as u64 }
@@ -37,147 +31,87 @@ impl_int! {
3731
u8 u16 u32 u64 u128 usize
3832
}
3933

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 unsigned 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 mut buf = [MaybeUninit::<u8>::uninit(); 128];
62-
let mut curr = buf.len();
63-
let base = T::from_u8(Self::BASE);
64-
65-
// Accumulate each digit of the number from the least significant
66-
// to the most significant figure.
67-
loop {
68-
let n = x % base; // Get the current place value.
69-
x = x / base; // Deaccumulate the number.
70-
curr -= 1;
71-
buf[curr].write(Self::digit(n.to_u8())); // Store the digit in the buffer.
72-
if x == zero {
73-
// No more digits left to accumulate.
74-
break;
75-
};
76-
}
34+
// Formatting of integers with a non-decimal radix.
35+
macro_rules! radix_integer {
36+
(fmt::$Trait:ident for $Signed:ident and $Unsigned:ident, $prefix:expr, $dig_tab:expr) => {
37+
#[stable(feature = "rust1", since = "1.0.0")]
38+
impl fmt::$Trait for $Unsigned {
39+
/// Format unsigned integers in the radix.
40+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41+
// Check arguments at compile time.
42+
assert!($Unsigned::MIN == 0);
43+
$dig_tab.as_ascii().unwrap();
7744

78-
// SAFETY: `curr` is initialized to `buf.len()` and is only decremented, so it can't overflow. It is
79-
// decremented exactly once for each digit. Since u128 is the widest fixed width integer format supported,
80-
// the maximum number of digits (bits) is 128 for base-2, so `curr` won't underflow as well.
81-
let buf = unsafe { buf.get_unchecked(curr..) };
82-
// SAFETY: The only chars in `buf` are created by `Self::digit` which are assumed to be
83-
// valid UTF-8
84-
let buf = unsafe {
85-
str::from_utf8_unchecked(slice::from_raw_parts(
86-
MaybeUninit::slice_as_ptr(buf),
87-
buf.len(),
88-
))
89-
};
90-
f.pad_integral(true, Self::PREFIX, buf)
91-
}
92-
}
45+
// ASCII digits in ascending order are used as a lookup table.
46+
const DIG_TAB: &[u8] = $dig_tab;
47+
const BASE: $Unsigned = DIG_TAB.len() as $Unsigned;
48+
const MAX_DIG_N: usize = $Unsigned::MAX.ilog(BASE) as usize + 1;
49+
50+
// Buffer digits of self with right alignment.
51+
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DIG_N];
52+
// Count the number of bytes in buf that are not initialized.
53+
let mut offset = buf.len();
54+
55+
// Accumulate each digit of the number from the least
56+
// significant to the most significant figure.
57+
let mut remain = *self;
58+
loop {
59+
let digit = remain % BASE;
60+
remain /= BASE;
9361

94-
/// A binary (base 2) radix
95-
#[derive(Clone, PartialEq)]
96-
struct Binary;
97-
98-
/// An octal (base 8) radix
99-
#[derive(Clone, PartialEq)]
100-
struct Octal;
101-
102-
/// A hexadecimal (base 16) radix, formatted with lower-case characters
103-
#[derive(Clone, PartialEq)]
104-
struct LowerHex;
105-
106-
/// A hexadecimal (base 16) radix, formatted with upper-case characters
107-
#[derive(Clone, PartialEq)]
108-
struct UpperHex;
109-
110-
macro_rules! radix {
111-
($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => {
112-
unsafe impl GenericRadix for $T {
113-
const BASE: u8 = $base;
114-
const PREFIX: &'static str = $prefix;
115-
fn digit(x: u8) -> u8 {
116-
match x {
117-
$($x => $conv,)+
118-
x => panic!("number not in the range 0..={}: {}", Self::BASE - 1, x),
62+
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
63+
// and the break condition below ensures at least 1 more
64+
// decimal.
65+
unsafe { core::hint::assert_unchecked(offset >= 1) }
66+
// SAFETY: The offset counts down from its initial buf.len()
67+
// without underflow due to the previous precondition.
68+
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
69+
offset -= 1;
70+
buf[offset].write(DIG_TAB[digit as usize]);
71+
if remain == 0 {
72+
break;
73+
}
11974
}
75+
76+
// SAFETY: All buf content since offset is set.
77+
let written = unsafe { buf.get_unchecked(offset..) };
78+
// SAFETY: Writes are ASCII numbers exclusively.
79+
let as_str = unsafe {
80+
str::from_utf8_unchecked(slice::from_raw_parts(
81+
MaybeUninit::slice_as_ptr(written),
82+
written.len(),
83+
))
84+
};
85+
f.pad_integral(true, $prefix, as_str)
12086
}
12187
}
122-
}
123-
}
12488

125-
radix! { Binary, 2, "0b", x @ 0 ..= 1 => b'0' + x }
126-
radix! { Octal, 8, "0o", x @ 0 ..= 7 => b'0' + x }
127-
radix! { LowerHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'a' + (x - 10) }
128-
radix! { UpperHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'A' + (x - 10) }
129-
130-
macro_rules! int_base {
131-
(fmt::$Trait:ident for $T:ident -> $Radix:ident) => {
13289
#[stable(feature = "rust1", since = "1.0.0")]
133-
impl fmt::$Trait for $T {
90+
impl fmt::$Trait for $Signed {
91+
/// Format signed integers in the two’s-complement form.
13492
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135-
$Radix.fmt_int(*self, f)
93+
assert!($Signed::MIN < 0);
94+
fmt::$Trait::fmt(&(*self as $Unsigned), f)
13695
}
13796
}
13897
};
13998
}
14099

141-
macro_rules! integer {
142-
($Int:ident, $Uint:ident) => {
143-
int_base! { fmt::Binary for $Uint -> Binary }
144-
int_base! { fmt::Octal for $Uint -> Octal }
145-
int_base! { fmt::LowerHex for $Uint -> LowerHex }
146-
int_base! { fmt::UpperHex for $Uint -> UpperHex }
147-
148-
// Format signed integers as unsigned (two’s complement representation).
149-
#[stable(feature = "rust1", since = "1.0.0")]
150-
impl fmt::Binary for $Int {
151-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152-
fmt::Binary::fmt(&(*self as $Uint), f)
153-
}
154-
}
155-
#[stable(feature = "rust1", since = "1.0.0")]
156-
impl fmt::Octal for $Int {
157-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158-
fmt::Octal::fmt(&(*self as $Uint), f)
159-
}
160-
}
161-
#[stable(feature = "rust1", since = "1.0.0")]
162-
impl fmt::LowerHex for $Int {
163-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164-
fmt::LowerHex::fmt(&(*self as $Uint), f)
165-
}
166-
}
167-
#[stable(feature = "rust1", since = "1.0.0")]
168-
impl fmt::UpperHex for $Int {
169-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170-
fmt::UpperHex::fmt(&(*self as $Uint), f)
171-
}
172-
}
100+
// Formatting of integers with a non-decimal radix.
101+
macro_rules! radix_integers {
102+
($Signed:ident, $Unsigned:ident) => {
103+
radix_integer! { fmt::Binary for $Signed and $Unsigned, "0b", b"01" }
104+
radix_integer! { fmt::Octal for $Signed and $Unsigned, "0o", b"01234567" }
105+
radix_integer! { fmt::LowerHex for $Signed and $Unsigned, "0x", b"0123456789abcdef" }
106+
radix_integer! { fmt::UpperHex for $Signed and $Unsigned, "0x", b"0123456789ABCDEF" }
173107
};
174108
}
175-
integer! { isize, usize }
176-
integer! { i8, u8 }
177-
integer! { i16, u16 }
178-
integer! { i32, u32 }
179-
integer! { i64, u64 }
180-
integer! { i128, u128 }
109+
radix_integers! { isize, usize }
110+
radix_integers! { i8, u8 }
111+
radix_integers! { i16, u16 }
112+
radix_integers! { i32, u32 }
113+
radix_integers! { i64, u64 }
114+
radix_integers! { i128, u128 }
181115

182116
macro_rules! impl_Debug {
183117
($($T:ident)*) => {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /