@@ -9,9 +9,6 @@ use crate::{fmt, ptr, slice, str};
9
9
trait DisplayInt :
10
10
PartialEq + PartialOrd + Div < Output = Self > + Rem < Output = Self > + Sub < Output = Self > + Copy
11
11
{
12
- fn zero ( ) -> Self ;
13
- fn from_u8 ( u : u8 ) -> Self ;
14
- fn to_u8 ( & self ) -> u8 ;
15
12
#[ cfg( not( any( target_pointer_width = "64" , target_arch = "wasm32" ) ) ) ]
16
13
fn to_u32 ( & self ) -> u32 ;
17
14
fn to_u64 ( & self ) -> u64 ;
@@ -21,9 +18,6 @@ trait DisplayInt:
21
18
macro_rules! impl_int {
22
19
( $( $t: ident) * ) => (
23
20
$( 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 }
27
21
#[ cfg( not( any( target_pointer_width = "64" , target_arch = "wasm32" ) ) ) ]
28
22
fn to_u32( & self ) -> u32 { * self as u32 }
29
23
fn to_u64( & self ) -> u64 { * self as u64 }
@@ -37,147 +31,87 @@ impl_int! {
37
31
u8 u16 u32 u64 u128 usize
38
32
}
39
33
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( ) ;
77
44
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 ;
93
61
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
+ }
119
74
}
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)
120
86
}
121
87
}
122
- }
123
- }
124
88
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) => {
132
89
#[ 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.
134
92
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)
136
95
}
137
96
}
138
97
} ;
139
98
}
140
99
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" }
173
107
} ;
174
108
}
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 }
181
115
182
116
macro_rules! impl_Debug {
183
117
( $( $T: ident) * ) => {
0 commit comments