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,6 +21,109 @@ impl<const BUF_SIZE: usize> NumBuffer<BUF_SIZE> {
2121 }
2222}
2323
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_digits_into < T , const BUF_SIZE : usize > ( var : T , buf : & mut NumBuffer < BUF_SIZE > , & mut offset: usize ) {
35+ // Consume the least-significant decimals from a working copy.
36+ let mut remain = var;
37+
38+ // Format per four digits from the lookup table.
39+ // Four digits need a 16-bit $unsigned or wider.
40+ while size_of :: < T > ( ) > 1 && remain > 999 . try_into ( ) . expect ( "branch is not hit for types that cannot fit 999 (u8)" ) {
41+ // SAFETY: All of the decimals fit in buf, since it now is size-checked
42+ // and the while condition ensures at least 4 more decimals.
43+ unsafe { core:: hint:: assert_unchecked ( offset >= 4 ) }
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+ offset -= 4 ;
48+
49+ // pull two pairs
50+ let scale: T = 1_00_00 . try_into ( ) . expect ( "branch is not hit for types that cannot fit 1E4 (u8)" ) ;
51+ let quad = remain % scale;
52+ remain /= scale;
53+ let pair1 = ( quad / 100 ) as usize ;
54+ let pair2 = ( quad % 100 ) as usize ;
55+ buf. contents [ offset + 0 ] . write ( DEC_DIGITS_LUT [ pair1 * 2 + 0 ] ) ;
56+ buf. contents [ offset + 1 ] . write ( DEC_DIGITS_LUT [ pair1 * 2 + 1 ] ) ;
57+ buf. contents [ offset + 2 ] . write ( DEC_DIGITS_LUT [ pair2 * 2 + 0 ] ) ;
58+ buf. contents [ offset + 3 ] . write ( DEC_DIGITS_LUT [ pair2 * 2 + 1 ] ) ;
59+ }
60+
61+ // Format per two digits from the lookup table.
62+ if remain > 9 {
63+ // SAFETY: All of the decimals fit in buf, since it now is size-checked
64+ // and the while condition ensures at least 2 more decimals.
65+ unsafe { core:: hint:: assert_unchecked ( offset >= 2 ) }
66+ // SAFETY: The offset counts down from its initial value BUF_SIZE
67+ // without underflow due to the previous precondition.
68+ unsafe { core:: hint:: assert_unchecked ( offset <= BUF_SIZE ) }
69+ offset -= 2 ;
70+
71+ let pair = ( remain % 100 ) as usize ;
72+ remain /= 100 ;
73+ buf. contents [ offset + 0 ] . write ( DEC_DIGITS_LUT [ pair * 2 + 0 ] ) ;
74+ buf. contents [ offset + 1 ] . write ( DEC_DIGITS_LUT [ pair * 2 + 1 ] ) ;
75+ }
76+
77+ // Format the last remaining digit, if any.
78+ if remain != 0 || var == 0 {
79+ // SAFETY: All of the decimals fit in buf, since it now is size-checked
80+ // and the if condition ensures (at least) 1 more decimals.
81+ unsafe { core:: hint:: assert_unchecked ( offset >= 1 ) }
82+ // SAFETY: The offset counts down from its initial value BUF_SIZE
83+ // without underflow due to the previous precondition.
84+ unsafe { core:: hint:: assert_unchecked ( offset <= BUF_SIZE ) }
85+ offset -= 1 ;
86+
87+ // Either the compiler sees that remain < 10, or it prevents
88+ // a boundary check up next.
89+ let last = ( remain & 15 ) as usize ;
90+ buf. contents [ offset] . write ( DEC_DIGITS_LUT [ last * 2 + 1 ] ) ;
91+ // not used: remain = 0;
92+ }
93+ }
94+
95+ #[ inline]
96+ fn raw_write_sign_into < const BUF_SIZE : usize > ( buf : & mut NumBuffer < BUF_SIZE > , offset : & mut usize ) {
97+ // SAFETY: All of the decimals (with the sign) fit in buf, since it now is size-checked
98+ // and the if condition ensures (at least) that the sign can be added.
99+ unsafe { core:: hint:: assert_unchecked ( offset >= 1 ) }
100+
101+ // SAFETY: The offset counts down from its initial value BUF_SIZE
102+ // without underflow due to the previous precondition.
103+ unsafe { core:: hint:: assert_unchecked ( offset <= BUF_SIZE ) }
104+
105+ // Setting sign for the negative number
106+ offset -= 1 ;
107+ buf. contents [ offset] . write ( NEGATIVE_SIGN [ 0 ] ) ;
108+ }
109+
110+ #[ inline]
111+ fn raw_extract_as_str < const BUF_SIZE : usize > ( buf : & NumBuffer < BUF_SIZE > , offset : usize ) -> & str {
112+ // SAFETY: All contents of `buf` since `offset` is set.
113+ let written = unsafe { buf. contents . get_unchecked ( offset..) } ;
114+
115+ // SAFETY: Writes use ASCII from the lookup table
116+ // (and `NEGATIVE_SIGN` in case of negative numbers) exclusively.
117+ let as_str = unsafe {
118+ str:: from_utf8_unchecked ( slice:: from_raw_parts (
119+ MaybeUninit :: slice_as_ptr ( written) ,
120+ written. len ( ) ,
121+ ) )
122+ } ;
123+
124+ as_str
125+ }
126+
24127macro_rules! int_impl_format_into {
25128 ( $( $T: ident) * ) => {
26129 $(
@@ -44,19 +147,16 @@ macro_rules! int_impl_format_into {
44147 /// ```
45148 ///
46149 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-
57150 // 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;
151+ let decimal_string_size: usize = if self == $T:: MIN {
152+ $T:: MAX . ilog( 10 ) as usize + 1 + 1
153+ } else if self < 0 {
154+ self . abs( ) . ilog( 10 ) as usize + 1 + 1
155+ } else if self == 0 {
156+ 1
157+ } else {
158+ self . ilog( 10 ) as usize + 1
159+ } ;
60160
61161 // `buf` must have minimum size to store the decimal string version.
62162 // BUF_SIZE is the size of the buffer.
@@ -66,91 +166,14 @@ macro_rules! int_impl_format_into {
66166
67167 // Count the number of bytes in `buf` that are not initialized.
68168 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- }
94169
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- }
170+ raw_write_digits_into( self , & mut buf, & mut offset) ;
127171
128172 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 ] ) ;
173+ raw_write_sign_into( & mut buf, & mut offset) ;
140174 }
141175
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
176+ raw_extract_as_str( & buf, offset)
154177 }
155178 }
156179 ) *
@@ -180,16 +203,8 @@ macro_rules! uint_impl_format_into {
180203 /// ```
181204 ///
182205 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-
191206 // counting space for negative sign too, if `self` is negative
192- let decimal_string_size: usize = self . ilog( 10 ) as usize + 1 ;
207+ let decimal_string_size: usize = if self == 0 { 1 } else { self . ilog( 10 ) as usize + 1 } ;
193208
194209 // `buf` must have minimum size to store the decimal string version.
195210 // BUF_SIZE is the size of the buffer.
@@ -199,76 +214,10 @@ macro_rules! uint_impl_format_into {
199214
200215 // Count the number of bytes in `buf` that are not initialized.
201216 let mut offset = BUF_SIZE ;
202- // Consume the least-significant decimals from a working copy.
203- let mut remain = self ;
204217
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 ;
218+ raw_write_digits_into( self , & mut buf, & mut offset) ;
215219
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- }
227-
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- }
260-
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
220+ raw_extract_as_str( & buf, offset)
272221 }
273222 }
274223 ) *
0 commit comments