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,11 +21,134 @@ 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; $( $SignedT: ty, $UnsignedT: ty) * ) => {
26149 $(
27150 #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
28- impl $T {
151+ impl $SignedT {
29152 /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
30153 /// type [`NumBuffer`] that is passed by the caller by mutable reference.
31154 ///
@@ -37,26 +160,21 @@ macro_rules! int_impl_format_into {
37160 /// #![feature(int_format_into)]
38161 ///
39162 /// use core::num::NumBuffer;
40- #[ doc = concat!( "let n = -32" , stringify!( $T ) , ";" ) ]
163+ #[ doc = concat!( "let n = -32" , stringify!( $SignedT ) , ";" ) ]
41164 /// let mut buf = NumBuffer::<3>::new();
42165 ///
43166 /// assert_eq!(n.format_into(&mut buf), "-32");
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 < 0 {
172+ self . unsigned_abs( ) . ilog( 10 ) as usize + 1 + 1
173+ } else if self == 0 {
174+ 1
175+ } else {
176+ self . ilog( 10 ) as usize + 1
177+ } ;
60178
61179 // `buf` must have minimum size to store the decimal string version.
62180 // BUF_SIZE is the size of the buffer.
@@ -66,102 +184,23 @@ macro_rules! int_impl_format_into {
66184
67185 // Count the number of bytes in `buf` that are not initialized.
68186 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- }
94187
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- }
188+ offset = raw_write_digits_into!( $UnsignedT, BUF_SIZE ) ( self . unsigned_abs( ) , buf, offset) ;
127189
128190 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 ] ) ;
191+ offset = raw_write_sign_into( buf, offset) ;
140192 }
141193
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
194+ raw_extract_as_str( buf, offset)
154195 }
155196 }
156197 ) *
157198 } ;
158- }
159199
160- macro_rules! uint_impl_format_into {
161- ( $( $T: ident) * ) => {
200+ ( unsigned; $( $UnsignedT: ty) * ) => {
162201 $(
163202 #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
164- impl $T {
203+ impl $UnsignedT {
165204 /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
166205 /// type [`NumBuffer`] that is passed by the caller by mutable reference.
167206 ///
@@ -180,16 +219,8 @@ macro_rules! uint_impl_format_into {
180219 /// ```
181220 ///
182221 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-
191222 // counting space for negative sign too, if `self` is negative
192- let decimal_string_size: usize = self . ilog( 10 ) as usize + 1 ;
223+ let decimal_string_size: usize = if self == 0 { 1 } else { self . ilog( 10 ) as usize + 1 } ;
193224
194225 // `buf` must have minimum size to store the decimal string version.
195226 // BUF_SIZE is the size of the buffer.
@@ -199,81 +230,15 @@ macro_rules! uint_impl_format_into {
199230
200231 // Count the number of bytes in `buf` that are not initialized.
201232 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- }
227233
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- }
234+ offset = raw_write_digits_into!( Self , BUF_SIZE ) ( self , buf, offset) ;
260235
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
236+ raw_extract_as_str( buf, offset)
272237 }
273238 }
274239 ) *
275240 } ;
276241}
277242
278- int_impl_format_into ! { i8 i16 i32 i64 i128 isize }
279- uint_impl_format_into ! { u8 u16 u32 u64 u128 usize }
243+ macro_impl_format_into ! { unsigned ; u8 u16 u32 u64 u128 usize }
244+ macro_impl_format_into ! { signed ; ( i8 , u8 ) ( i16 , u16 ) ( i32 , u32 ) ( i64 , u64 ) ( i128 , u128 ) ( isize , usize ) }
0 commit comments