11use crate :: mem:: MaybeUninit ;
2+ use crate :: slice;
23
34/// A minimal buffer implementation containing elements of type
45/// `MaybeUninit<u8>`.
56#[ unstable( feature = "int_format_into" , issue = "138215" ) ]
67#[ derive( Debug ) ]
78pub struct NumBuffer < const BUF_SIZE : usize > {
8- /// A statically allocated array of elements of type
9- /// `MaybeUninit::<u8>`.
9+ /// An array of elements of type `MaybeUninit<u8>`.
1010 ///
1111 /// An alternative to `contents.len()` is `BUF_SIZE`.
1212 pub contents : [ MaybeUninit < u8 > ; BUF_SIZE ] ,
@@ -21,8 +21,131 @@ impl<const BUF_SIZE: usize> NumBuffer<BUF_SIZE> {
2121 }
2222}
2323
24- macro_rules! int_impl_format_into {
25- ( $( $T: ident) * ) => {
24+ const DEC_DIGITS_LUT : & [ u8 ; 200 ] = b"\
25+ 0001020304050607080910111213141516171819\
26+ 2021222324252627282930313233343536373839\
27+ 4041424344454647484950515253545556575859\
28+ 6061626364656667686970717273747576777879\
29+ 8081828384858687888990919293949596979899";
30+
31+ const NEGATIVE_SIGN : & [ u8 ; 1 ] = b"-" ;
32+
33+ #[ inline]
34+ fn raw_write_sign_into < const BUF_SIZE : usize > (
35+ buf : & mut NumBuffer < BUF_SIZE > ,
36+ start_offset : usize ,
37+ ) -> usize {
38+ let mut offset = start_offset;
39+
40+ // SAFETY: All of the decimals (with the sign) fit in buf, since it now is size-checked
41+ // and the if condition ensures (at least) that the sign can be added.
42+ unsafe { core:: hint:: assert_unchecked ( offset >= 1 ) }
43+
44+ // SAFETY: The offset counts down from its initial value BUF_SIZE
45+ // without underflow due to the previous precondition.
46+ unsafe { core:: hint:: assert_unchecked ( offset <= BUF_SIZE ) }
47+
48+ // Setting sign for the negative number
49+ offset -= 1 ;
50+ buf. contents [ offset] . write ( NEGATIVE_SIGN [ 0 ] ) ;
51+
52+ offset
53+ }
54+
55+ #[ inline]
56+ fn raw_extract_as_str < const BUF_SIZE : usize > ( buf : & NumBuffer < BUF_SIZE > , offset : usize ) -> & str {
57+ // SAFETY: All contents of `buf` since `offset` is set.
58+ let written = unsafe { buf. contents . get_unchecked ( offset..) } ;
59+
60+ // SAFETY: Writes use ASCII from the lookup table
61+ // (and `NEGATIVE_SIGN` in case of negative numbers) exclusively.
62+ let as_str = unsafe {
63+ str:: from_utf8_unchecked ( slice:: from_raw_parts (
64+ MaybeUninit :: slice_as_ptr ( written) ,
65+ written. len ( ) ,
66+ ) )
67+ } ;
68+
69+ as_str
70+ }
71+
72+ macro_rules! raw_write_digits_into {
73+ ( $T: ty, $BUF_SIZE: expr) => {
74+ |target_var: $T, buf: & mut NumBuffer <{ $BUF_SIZE } >, start_offset: usize | -> usize {
75+ let mut offset = start_offset;
76+
77+ // Consume the least-significant decimals from a working copy.
78+ let mut remain: $T = target_var;
79+
80+ // Format per four digits from the lookup table.
81+ // Four digits need a 16-bit $unsigned or wider.
82+ while size_of:: <$T>( ) > 1
83+ && remain
84+ > 999 . try_into( ) . expect( "branch is not hit for types that cannot fit 999 (u8)" )
85+ {
86+ // SAFETY: All of the decimals fit in buf, since it now is size-checked
87+ // and the while condition ensures at least 4 more decimals.
88+ unsafe { core:: hint:: assert_unchecked( offset >= 4 ) }
89+ // SAFETY: The offset counts down from its initial value $BUF_SIZE
90+ // without underflow due to the previous precondition.
91+ unsafe { core:: hint:: assert_unchecked( offset <= $BUF_SIZE) }
92+ offset -= 4 ;
93+
94+ // pull two pairs
95+ let scale: $T = 1_00_00
96+ . try_into( )
97+ . expect( "branch is not hit for types that cannot fit 1E4 (u8)" ) ;
98+
99+ let quad = remain % scale;
100+ remain /= scale;
101+ let pair1 = ( quad / 100 ) as usize ;
102+ let pair2 = ( quad % 100 ) as usize ;
103+ buf. contents[ offset + 0 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 0 ] ) ;
104+ buf. contents[ offset + 1 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 1 ] ) ;
105+ buf. contents[ offset + 2 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 0 ] ) ;
106+ buf. contents[ offset + 3 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 1 ] ) ;
107+ }
108+
109+ // Format per two digits from the lookup table.
110+ if remain > 9 {
111+ // SAFETY: All of the decimals fit in buf, since it now is size-checked
112+ // and the while condition ensures at least 2 more decimals.
113+ unsafe { core:: hint:: assert_unchecked( offset >= 2 ) }
114+ // SAFETY: The offset counts down from its initial value $BUF_SIZE
115+ // without underflow due to the previous precondition.
116+ unsafe { core:: hint:: assert_unchecked( offset <= $BUF_SIZE) }
117+ offset -= 2 ;
118+
119+ let pair = ( remain % 100 ) as usize ;
120+ remain /= 100 ;
121+ buf. contents[ offset + 0 ] . write( DEC_DIGITS_LUT [ pair * 2 + 0 ] ) ;
122+ buf. contents[ offset + 1 ] . write( DEC_DIGITS_LUT [ pair * 2 + 1 ] ) ;
123+ }
124+
125+ // Format the last remaining digit, if any.
126+ if remain != 0 || target_var == 0 {
127+ // SAFETY: All of the decimals fit in buf, since it now is size-checked
128+ // and the if condition ensures (at least) 1 more decimals.
129+ unsafe { core:: hint:: assert_unchecked( offset >= 1 ) }
130+ // SAFETY: The offset counts down from its initial value $BUF_SIZE
131+ // without underflow due to the previous precondition.
132+ unsafe { core:: hint:: assert_unchecked( offset <= $BUF_SIZE) }
133+ offset -= 1 ;
134+
135+ // Either the compiler sees that remain < 10, or it prevents
136+ // a boundary check up next.
137+ let last = ( remain & 15 ) as usize ;
138+ buf. contents[ offset] . write( DEC_DIGITS_LUT [ last * 2 + 1 ] ) ;
139+ // not used: remain = 0;
140+ }
141+
142+ offset
143+ }
144+ } ;
145+ }
146+
147+ macro_rules! macro_impl_format_into {
148+ ( signed; $( $T: ident) * ) => {
26149 $(
27150 #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
28151 impl $T {
@@ -44,19 +167,16 @@ macro_rules! int_impl_format_into {
44167 /// ```
45168 ///
46169 pub fn format_into<const BUF_SIZE : usize >( self , buf: & mut crate :: num:: NumBuffer <BUF_SIZE >) -> & str {
47- // 2 digit decimal look up table
48- const DEC_DIGITS_LUT : & [ u8 ; 200 ] = b"\
49- 0001020304050607080910111213141516171819\
50- 2021222324252627282930313233343536373839\
51- 4041424344454647484950515253545556575859\
52- 6061626364656667686970717273747576777879\
53- 8081828384858687888990919293949596979899";
54-
55- const NEGATIVE_SIGN : & [ u8 ; 1 ] = b"-" ;
56-
57170 // counting space for negative sign too, if `self` is negative
58- let sign_offset = if self < 0 { 1 } else { 0 } ;
59- let decimal_string_size: usize = self . ilog( 10 ) as usize + 1 + sign_offset;
171+ let decimal_string_size: usize = if self == $T:: MIN {
172+ $T:: MAX . ilog( 10 ) as usize + 1 + 1
173+ } else if self < 0 {
174+ self . abs( ) . ilog( 10 ) as usize + 1 + 1
175+ } else if self == 0 {
176+ 1
177+ } else {
178+ self . ilog( 10 ) as usize + 1
179+ } ;
60180
61181 // `buf` must have minimum size to store the decimal string version.
62182 // BUF_SIZE is the size of the buffer.
@@ -66,99 +186,20 @@ macro_rules! int_impl_format_into {
66186
67187 // Count the number of bytes in `buf` that are not initialized.
68188 let mut offset = BUF_SIZE ;
69- // Consume the least-significant decimals from a working copy.
70- let mut remain = self ;
71-
72- // Format per four digits from the lookup table.
73- // Four digits need a 16-bit $unsigned or wider.
74- while size_of:: <Self >( ) > 1 && remain > 999 . try_into( ) . expect( "branch is not hit for types that cannot fit 999 (u8)" ) {
75- // SAFETY: All of the decimals fit in buf, since it now is size-checked
76- // and the while condition ensures at least 4 more decimals.
77- unsafe { core:: hint:: assert_unchecked( offset >= 4 ) }
78- // SAFETY: The offset counts down from its initial value BUF_SIZE
79- // without underflow due to the previous precondition.
80- unsafe { core:: hint:: assert_unchecked( offset <= BUF_SIZE ) }
81- offset -= 4 ;
82-
83- // pull two pairs
84- let scale: Self = 1_00_00 . try_into( ) . expect( "branch is not hit for types that cannot fit 1E4 (u8)" ) ;
85- let quad = remain % scale;
86- remain /= scale;
87- let pair1 = ( quad / 100 ) as usize ;
88- let pair2 = ( quad % 100 ) as usize ;
89- buf. contents[ offset + 0 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 0 ] ) ;
90- buf. contents[ offset + 1 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 1 ] ) ;
91- buf. contents[ offset + 2 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 0 ] ) ;
92- buf. contents[ offset + 3 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 1 ] ) ;
93- }
94189
95- // Format per two digits from the lookup table.
96- if remain > 9 {
97- // SAFETY: All of the decimals fit in buf, since it now is size-checked
98- // and the while condition ensures at least 2 more decimals.
99- unsafe { core:: hint:: assert_unchecked( offset >= 2 ) }
100- // SAFETY: The offset counts down from its initial value BUF_SIZE
101- // without underflow due to the previous precondition.
102- unsafe { core:: hint:: assert_unchecked( offset <= BUF_SIZE ) }
103- offset -= 2 ;
104-
105- let pair = ( remain % 100 ) as usize ;
106- remain /= 100 ;
107- buf. contents[ offset + 0 ] . write( DEC_DIGITS_LUT [ pair * 2 + 0 ] ) ;
108- buf. contents[ offset + 1 ] . write( DEC_DIGITS_LUT [ pair * 2 + 1 ] ) ;
109- }
110-
111- // Format the last remaining digit, if any.
112- if remain != 0 || self == 0 {
113- // SAFETY: All of the decimals fit in buf, since it now is size-checked
114- // and the if condition ensures (at least) 1 more decimals.
115- unsafe { core:: hint:: assert_unchecked( offset >= 1 ) }
116- // SAFETY: The offset counts down from its initial value BUF_SIZE
117- // without underflow due to the previous precondition.
118- unsafe { core:: hint:: assert_unchecked( offset <= BUF_SIZE ) }
119- offset -= 1 ;
120-
121- // Either the compiler sees that remain < 10, or it prevents
122- // a boundary check up next.
123- let last = ( remain & 15 ) as usize ;
124- buf. contents[ offset] . write( DEC_DIGITS_LUT [ last * 2 + 1 ] ) ;
125- // not used: remain = 0;
126- }
190+ offset = raw_write_digits_into!( Self , BUF_SIZE ) ( self , buf, offset) ;
127191
128192 if self < 0 {
129- // SAFETY: All of the decimals (with the sign) fit in buf, since it now is size-checked
130- // and the if condition ensures (at least) that the sign can be added.
131- unsafe { core:: hint:: assert_unchecked( offset >= 1 ) }
132-
133- // SAFETY: The offset counts down from its initial value BUF_SIZE
134- // without underflow due to the previous precondition.
135- unsafe { core:: hint:: assert_unchecked( offset <= BUF_SIZE ) }
136-
137- // Setting sign for the negative number
138- offset -= 1 ;
139- buf. contents[ offset] . write( NEGATIVE_SIGN [ 0 ] ) ;
193+ offset = raw_write_sign_into( buf, offset) ;
140194 }
141195
142- // SAFETY: All buf content since offset is set.
143- let written = unsafe { buf. contents. get_unchecked( offset..) } ;
144-
145- // SAFETY: Writes use ASCII from the lookup table
146- // (and `NEGATIVE_SIGN` in case of negative numbers) exclusively.
147- let as_str = unsafe {
148- str :: from_utf8_unchecked( crate :: slice:: from_raw_parts(
149- crate :: mem:: MaybeUninit :: slice_as_ptr( written) ,
150- written. len( ) ,
151- ) )
152- } ;
153- as_str
196+ raw_extract_as_str( buf, offset)
154197 }
155198 }
156199 ) *
157200 } ;
158- }
159201
160- macro_rules! uint_impl_format_into {
161- ( $( $T: ident) * ) => {
202+ ( unsigned; $( $T: ident) * ) => {
162203 $(
163204 #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
164205 impl $T {
@@ -180,16 +221,8 @@ macro_rules! uint_impl_format_into {
180221 /// ```
181222 ///
182223 pub fn format_into<const BUF_SIZE : usize >( self , buf: & mut crate :: num:: NumBuffer <BUF_SIZE >) -> & str {
183- // 2 digit decimal look up table
184- const DEC_DIGITS_LUT : & [ u8 ; 200 ] = b"\
185- 0001020304050607080910111213141516171819\
186- 2021222324252627282930313233343536373839\
187- 4041424344454647484950515253545556575859\
188- 6061626364656667686970717273747576777879\
189- 8081828384858687888990919293949596979899";
190-
191224 // counting space for negative sign too, if `self` is negative
192- let decimal_string_size: usize = self . ilog( 10 ) as usize + 1 ;
225+ let decimal_string_size: usize = if self == 0 { 1 } else { self . ilog( 10 ) as usize + 1 } ;
193226
194227 // `buf` must have minimum size to store the decimal string version.
195228 // BUF_SIZE is the size of the buffer.
@@ -199,81 +232,15 @@ macro_rules! uint_impl_format_into {
199232
200233 // Count the number of bytes in `buf` that are not initialized.
201234 let mut offset = BUF_SIZE ;
202- // Consume the least-significant decimals from a working copy.
203- let mut remain = self ;
204-
205- // Format per four digits from the lookup table.
206- // Four digits need a 16-bit $unsigned or wider.
207- while size_of:: <Self >( ) > 1 && remain > 999 . try_into( ) . expect( "branch is not hit for types that cannot fit 999 (u8)" ) {
208- // SAFETY: All of the decimals fit in buf, since it now is size-checked
209- // and the while condition ensures at least 4 more decimals.
210- unsafe { core:: hint:: assert_unchecked( offset >= 4 ) }
211- // SAFETY: The offset counts down from its initial value BUF_SIZE
212- // without underflow due to the previous precondition.
213- unsafe { core:: hint:: assert_unchecked( offset <= BUF_SIZE ) }
214- offset -= 4 ;
215-
216- // pull two pairs
217- let scale: Self = 1_00_00 . try_into( ) . expect( "branch is not hit for types that cannot fit 1E4 (u8)" ) ;
218- let quad = remain % scale;
219- remain /= scale;
220- let pair1 = ( quad / 100 ) as usize ;
221- let pair2 = ( quad % 100 ) as usize ;
222- buf. contents[ offset + 0 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 0 ] ) ;
223- buf. contents[ offset + 1 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 1 ] ) ;
224- buf. contents[ offset + 2 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 0 ] ) ;
225- buf. contents[ offset + 3 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 1 ] ) ;
226- }
227235
228- // Format per two digits from the lookup table.
229- if remain > 9 {
230- // SAFETY: All of the decimals fit in buf, since it now is size-checked
231- // and the while condition ensures at least 2 more decimals.
232- unsafe { core:: hint:: assert_unchecked( offset >= 2 ) }
233- // SAFETY: The offset counts down from its initial value BUF_SIZE
234- // without underflow due to the previous precondition.
235- unsafe { core:: hint:: assert_unchecked( offset <= BUF_SIZE ) }
236- offset -= 2 ;
237-
238- let pair = ( remain % 100 ) as usize ;
239- remain /= 100 ;
240- buf. contents[ offset + 0 ] . write( DEC_DIGITS_LUT [ pair * 2 + 0 ] ) ;
241- buf. contents[ offset + 1 ] . write( DEC_DIGITS_LUT [ pair * 2 + 1 ] ) ;
242- }
243-
244- // Format the last remaining digit, if any.
245- if remain != 0 || self == 0 {
246- // SAFETY: All of the decimals fit in buf, since it now is size-checked
247- // and the if condition ensures (at least) 1 more decimals.
248- unsafe { core:: hint:: assert_unchecked( offset >= 1 ) }
249- // SAFETY: The offset counts down from its initial value BUF_SIZE
250- // without underflow due to the previous precondition.
251- unsafe { core:: hint:: assert_unchecked( offset <= BUF_SIZE ) }
252- offset -= 1 ;
253-
254- // Either the compiler sees that remain < 10, or it prevents
255- // a boundary check up next.
256- let last = ( remain & 15 ) as usize ;
257- buf. contents[ offset] . write( DEC_DIGITS_LUT [ last * 2 + 1 ] ) ;
258- // not used: remain = 0;
259- }
236+ offset = raw_write_digits_into!( Self , BUF_SIZE ) ( self , buf, offset) ;
260237
261- // SAFETY: All buf content since offset is set.
262- let written = unsafe { buf. contents. get_unchecked( offset..) } ;
263-
264- // SAFETY: Writes use ASCII from the lookup table exclusively.
265- let as_str = unsafe {
266- str :: from_utf8_unchecked( crate :: slice:: from_raw_parts(
267- crate :: mem:: MaybeUninit :: slice_as_ptr( written) ,
268- written. len( ) ,
269- ) )
270- } ;
271- as_str
238+ raw_extract_as_str( buf, offset)
272239 }
273240 }
274241 ) *
275242 } ;
276243}
277244
278- int_impl_format_into ! { i8 i16 i32 i64 i128 isize }
279- uint_impl_format_into ! { u8 u16 u32 u64 u128 usize }
245+ macro_impl_format_into ! { signed ; i8 i16 i32 i64 i128 isize }
246+ macro_impl_format_into ! { unsigned ; u8 u16 u32 u64 u128 usize }
0 commit comments