@@ -4,6 +4,7 @@ use crate::mem::MaybeUninit;
44use crate :: num:: fmt as numfmt;
55use crate :: ops:: { Div , Rem , Sub } ;
66use crate :: { fmt, ptr, slice, str} ;
7+ use crate :: fmt:: NumBuffer ;
78
89#[ doc( hidden) ]
910trait DisplayInt :
@@ -199,6 +200,45 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"\
199200 6061626364656667686970717273747576777879\
200201 8081828384858687888990919293949596979899";
201202
203+ static NEGATIVE_SIGN : & [ u8 ; 1 ] = b"-" ;
204+
205+ // Unsafe because
206+ // 1. The contents of `buf` is not validated in the function
207+ // 2. offset is not bound checked
208+ unsafe fn _extract_str_from_buf (
209+ buf : & NumBuffer ,
210+ offset : usize
211+ ) -> & str {
212+ let written = unsafe { buf. contents . get_unchecked ( offset..) } ;
213+ let as_str = unsafe {
214+ str:: from_utf8_unchecked ( slice:: from_raw_parts (
215+ MaybeUninit :: slice_as_ptr ( written) ,
216+ written. len ( ) ,
217+ ) )
218+ } ;
219+
220+ as_str
221+ }
222+
223+ // Unsafe because
224+ // 1. Offset is not bound checked
225+ unsafe fn _add_negative_sign (
226+ is_nonnegative : bool ,
227+ buf : & mut NumBuffer ,
228+ start_offset : usize
229+ ) -> usize {
230+ if is_nonnegative {
231+ return start_offset;
232+ }
233+
234+ let offset = start_offset - 1 ;
235+
236+ // Setting sign for the negative number
237+ buf. contents [ offset] . write ( NEGATIVE_SIGN [ 0 ] ) ;
238+
239+ offset
240+ }
241+
202242macro_rules! impl_Display {
203243 ( $( $signed: ident, $unsigned: ident, ) * ; as $u: ident via $conv_fn: ident named $gen_name: ident) => {
204244
@@ -212,7 +252,9 @@ macro_rules! impl_Display {
212252 }
213253 #[ cfg( feature = "optimize_for_size" ) ]
214254 {
215- $gen_name( self . $conv_fn( ) , true , f)
255+ let mut buf = NumBuffer :: new( ) ;
256+ let as_str = $gen_name( self . $conv_fn( ) , true , & mut buf) ;
257+ f. pad_integral( true , "" , as_str)
216258 }
217259 }
218260 }
@@ -226,31 +268,122 @@ macro_rules! impl_Display {
226268 }
227269 #[ cfg( feature = "optimize_for_size" ) ]
228270 {
229- return $gen_name( self . unsigned_abs( ) . $conv_fn( ) , * self >= 0 , f) ;
271+ let mut buf = NumBuffer :: new( ) ;
272+
273+ // not setting the sign here, hence sending in `true`
274+ let as_str = $gen_name( self . unsigned_abs( ) . $conv_fn( ) , true , & mut buf) ;
275+ f. pad_integral( * self >= 0 , "" , as_str)
230276 }
231277 }
232278 }
233279
280+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
281+ impl $signed {
282+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
283+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
284+ ///
285+ /// # Examples
286+ /// ```
287+ /// #![feature(int_format_into)]
288+ /// use core::fmt::NumBuffer;
289+ ///
290+ #[ doc = concat!( "let n = -32" , stringify!( $signed) , ";" ) ]
291+ /// let mut buf = NumBuffer::new();
292+ /// assert_eq!(n.format_into(&mut buf), "-32");
293+ ///
294+ #[ doc = concat!( "let n2 = " , stringify!( $signed:: MIN ) , ";" ) ]
295+ /// let mut buf2 = NumBuffer::new();
296+ #[ doc = concat!( "assert_eq!(n2.format_into(&mut buf2), " , stringify!( $signed:: MIN ) , ".to_string());" ) ]
297+ ///
298+ #[ doc = concat!( "let n3 = " , stringify!( $signed:: MAX ) , ";" ) ]
299+ /// let mut buf3 = NumBuffer::new();
300+ #[ doc = concat!( "assert_eq!(n3.format_into(&mut buf3), " , stringify!( $signed:: MAX ) , ".to_string());" ) ]
301+ /// ```
302+ ///
303+ pub fn format_into( self , buf: & mut NumBuffer ) -> & str {
304+ #[ cfg( not( feature = "optimize_for_size" ) ) ]
305+ {
306+ let is_nonnegative = self > 0 ;
307+ let offset = self . unsigned_abs( ) . _write_into_buf( buf) ;
308+
309+ // SAFETY: `offset >= 2` (unchanged if `is_nonnegative` is true) and
310+ // `offset >= 1` (incase `is_nonnegative` is false) so
311+ // `buf_ptr[offset..offset + 1]` is safe to access.
312+ unsafe { _add_negative_sign( is_nonnegative, buf, offset) ; }
313+
314+ // SAFETY: All buf content since offset is set, and
315+ // writes use ASCII from the lookup table exclusively.
316+ let as_str = unsafe { _extract_str_from_buf( buf, offset) } ;
317+
318+ as_str
319+ }
320+
321+ #[ cfg( feature = "optimize_for_size" ) ]
322+ {
323+ let is_nonnegative = self > 0 ;
324+ $gen_name( self . unsigned_abs( ) , is_nonnegative, buf)
325+ }
326+
327+ }
328+ }
329+
330+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
331+ impl $unsigned {
332+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
333+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
334+ ///
335+ /// # Examples
336+ /// ```
337+ /// #![feature(int_format_into)]
338+ /// use core::fmt::NumBuffer;
339+ ///
340+ #[ doc = concat!( "let n = 32" , stringify!( $unsigned) , ";" ) ]
341+ /// let mut buf = NumBuffer::new();
342+ /// assert_eq!(n.format_into(&mut buf), "32");
343+ ///
344+ #[ doc = concat!( "let n2 = " , stringify!( $unsigned:: MAX ) , ";" ) ]
345+ /// let mut buf2 = NumBuffer::new();
346+ #[ doc = concat!( "assert_eq!(n2.format_into(&mut buf2), " , stringify!( $unsigned:: MAX ) , ".to_string());" ) ]
347+ /// ```
348+ ///
349+ pub fn format_into( self , buf: & mut NumBuffer ) -> & str {
350+ #[ cfg( not( feature = "optimize_for_size" ) ) ]
351+ {
352+ let offset = self . _write_into_buf( buf) ;
353+
354+ // SAFETY: All contents in `buf` since offset is set, and
355+ // writes use ASCII from the lookup table exclusively.
356+ let as_str = unsafe { _extract_str_from_buf( buf, offset) } ;
357+
358+ as_str
359+ }
360+
361+ #[ cfg( feature = "optimize_for_size" ) ]
362+ {
363+ $gen_name( self , true , buf)
364+ }
365+
366+ }
367+ }
368+
234369 #[ cfg( not( feature = "optimize_for_size" ) ) ]
235370 impl $unsigned {
236- fn _fmt( self , is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
237- const MAX_DEC_N : usize = $unsigned:: MAX . ilog( 10 ) as usize + 1 ;
238- // Buffer decimals for $unsigned with right alignment.
239- let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; MAX_DEC_N ] ;
371+ fn _write_into_buf( self , buf: & mut NumBuffer ) -> usize {
240372 // Count the number of bytes in buf that are not initialized.
241- let mut offset = buf. len( ) ;
373+ let mut offset = buf. contents . len( ) ;
242374 // Consume the least-significant decimals from a working copy.
243375 let mut remain = self ;
244376
245377 // Format per four digits from the lookup table.
246378 // Four digits need a 16-bit $unsigned or wider.
247379 while size_of:: <Self >( ) > 1 && remain > 999 . try_into( ) . expect( "branch is not hit for types that cannot fit 999 (u8)" ) {
248- // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
380+ // SAFETY: All of the decimals fit in buf since buf is large enough to
381+ // accommodate the largest representation of a number possible (that of i128::MIN)
249382 // and the while condition ensures at least 4 more decimals.
250383 unsafe { core:: hint:: assert_unchecked( offset >= 4 ) }
251- // SAFETY: The offset counts down from its initial buf.len()
384+ // SAFETY: The offset counts down from its initial size
252385 // without underflow due to the previous precondition.
253- unsafe { core:: hint:: assert_unchecked( offset <= buf. len( ) ) }
386+ unsafe { core:: hint:: assert_unchecked( offset <= buf. contents . len( ) ) }
254387 offset -= 4 ;
255388
256389 // pull two pairs
@@ -259,26 +392,27 @@ macro_rules! impl_Display {
259392 remain /= scale;
260393 let pair1 = ( quad / 100 ) as usize ;
261394 let pair2 = ( quad % 100 ) as usize ;
262- buf[ offset + 0 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 0 ] ) ;
263- buf[ offset + 1 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 1 ] ) ;
264- buf[ offset + 2 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 0 ] ) ;
265- buf[ offset + 3 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 1 ] ) ;
395+ buf. contents [ offset + 0 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 0 ] ) ;
396+ buf. contents [ offset + 1 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 1 ] ) ;
397+ buf. contents [ offset + 2 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 0 ] ) ;
398+ buf. contents [ offset + 3 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 1 ] ) ;
266399 }
267400
268401 // Format per two digits from the lookup table.
269402 if remain > 9 {
270- // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
403+ // SAFETY: All of the decimals fit in buf since buf is large enough to
404+ // accommodate the largest representation of a number possilble (that of i128::MIN)
271405 // and the while condition ensures at least 2 more decimals.
272406 unsafe { core:: hint:: assert_unchecked( offset >= 2 ) }
273- // SAFETY: The offset counts down from its initial buf.len()
407+ // SAFETY: The offset counts down from its initial size
274408 // without underflow due to the previous precondition.
275- unsafe { core:: hint:: assert_unchecked( offset <= buf. len( ) ) }
409+ unsafe { core:: hint:: assert_unchecked( offset <= buf. contents . len( ) ) }
276410 offset -= 2 ;
277411
278412 let pair = ( remain % 100 ) as usize ;
279413 remain /= 100 ;
280- buf[ offset + 0 ] . write( DEC_DIGITS_LUT [ pair * 2 + 0 ] ) ;
281- buf[ offset + 1 ] . write( DEC_DIGITS_LUT [ pair * 2 + 1 ] ) ;
414+ buf. contents [ offset + 0 ] . write( DEC_DIGITS_LUT [ pair * 2 + 0 ] ) ;
415+ buf. contents [ offset + 1 ] . write( DEC_DIGITS_LUT [ pair * 2 + 1 ] ) ;
282416 }
283417
284418 // Format the last remaining digit, if any.
@@ -288,40 +422,43 @@ macro_rules! impl_Display {
288422 unsafe { core:: hint:: assert_unchecked( offset >= 1 ) }
289423 // SAFETY: The offset counts down from its initial buf.len()
290424 // without underflow due to the previous precondition.
291- unsafe { core:: hint:: assert_unchecked( offset <= buf. len( ) ) }
425+ unsafe { core:: hint:: assert_unchecked( offset <= buf. contents . len( ) ) }
292426 offset -= 1 ;
293427
294428 // Either the compiler sees that remain < 10, or it prevents
295429 // a boundary check up next.
296430 let last = ( remain & 15 ) as usize ;
297- buf[ offset] . write( DEC_DIGITS_LUT [ last * 2 + 1 ] ) ;
431+ buf. contents [ offset] . write( DEC_DIGITS_LUT [ last * 2 + 1 ] ) ;
298432 // not used: remain = 0;
299433 }
300434
301- // SAFETY: All buf content since offset is set.
302- let written = unsafe { buf. get_unchecked( offset..) } ;
303- // SAFETY: Writes use ASCII from the lookup table exclusively.
435+ offset
436+ }
437+
438+ fn _fmt( self , is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
439+ // Buffer decimals for $unsigned with right alignment.
440+ let mut buf = NumBuffer :: new( ) ;
441+ let offset = self . _write_into_buf( & mut buf) ;
442+
443+ // SAFETY: All buf content since offset is set, and
444+ // writes use ASCII from the lookup table exclusively.
304445 let as_str = unsafe {
305- str :: from_utf8_unchecked( slice:: from_raw_parts(
306- MaybeUninit :: slice_as_ptr( written) ,
307- written. len( ) ,
308- ) )
446+ _extract_str_from_buf( & buf, offset)
309447 } ;
448+
310449 f. pad_integral( is_nonnegative, "" , as_str)
311450 }
312451 } ) *
313452
314453 #[ cfg( feature = "optimize_for_size" ) ]
315- fn $gen_name( mut n: $u, is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
316- const MAX_DEC_N : usize = $u:: MAX . ilog( 10 ) as usize + 1 ;
317- let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; MAX_DEC_N ] ;
318- let mut curr = MAX_DEC_N ;
319- let buf_ptr = MaybeUninit :: slice_as_mut_ptr( & mut buf) ;
454+ fn $gen_name( mut n: $u, is_nonnegative: bool , buf: & mut NumBuffer ) -> & str {
455+ let mut curr = buf. contents. len( ) ;
456+ let buf_ptr = MaybeUninit :: slice_as_mut_ptr( & mut buf. contents) ;
320457
321458 // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning
322- // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
459+ // `curr == buf.len() == 41 > log(n)` since `n < 2^128 < 10^39 < 10^41 `, and at
323460 // each step this is kept the same as `n` is divided. Since `n` is always
324- // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]`
461+ // non-negative, this means that `curr >= (41 - 39) == 2 > 0` so `buf_ptr[curr..curr + 1]`
325462 // is safe to access.
326463 unsafe {
327464 loop {
@@ -335,12 +472,18 @@ macro_rules! impl_Display {
335472 }
336473 }
337474
338- // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8
475+ // SAFETY: `curr >= 2` (unchanged if `is_nonnegative` is true) and
476+ // `curr >= 1` (incase `is_nonnegative` is false) so `buf_ptr[curr..curr + 1]`
477+ // is safe to access.
478+ unsafe { _add_negative_sign( is_nonnegative, buf, curr) ; }
479+
480+ // SAFETY: `curr >= 1` (since we made `buf` large enough), and all the chars are valid UTF-8
339481 let buf_slice = unsafe {
340482 str :: from_utf8_unchecked(
341483 slice:: from_raw_parts( buf_ptr. add( curr) , buf. len( ) - curr) )
342484 } ;
343- f. pad_integral( is_nonnegative, "" , buf_slice)
485+
486+ buf_slice
344487 }
345488 } ;
346489}
@@ -529,7 +672,7 @@ mod imp {
529672 i32 , u32 ,
530673 i64 , u64 ,
531674 isize , usize ,
532- ; as u64 via to_u64 named fmt_u64
675+ ; as u64 via to_u64 named stringify_u64
533676 ) ;
534677 impl_Exp ! (
535678 i8 , u8 , i16 , u16 , i32 , u32 , i64 , u64 , usize , isize
@@ -545,10 +688,10 @@ mod imp {
545688 i16 , u16 ,
546689 i32 , u32 ,
547690 isize , usize ,
548- ; as u32 via to_u32 named fmt_u32 ) ;
691+ ; as u32 via to_u32 named stringify_u32 ) ;
549692 impl_Display ! (
550693 i64 , u64 ,
551- ; as u64 via to_u64 named fmt_u64 ) ;
694+ ; as u64 via to_u64 named stringify_u64 ) ;
552695
553696 impl_Exp ! ( i8 , u8 , i16 , u16 , i32 , u32 , isize , usize as u32 via to_u32 named exp_u32) ;
554697 impl_Exp ! ( i64 , u64 as u64 via to_u64 named exp_u64) ;
0 commit comments