@@ -9,9 +9,6 @@ use crate::{fmt, ptr, slice, str};
99trait 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:
2118macro_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
182116macro_rules! impl_Debug {
183117 ( $( $T: ident) * ) => {
0 commit comments