11//! Integer and floating-point number formatting
22
3+ use crate :: fmt:: NumBuffer ;
34use crate :: mem:: MaybeUninit ;
45use crate :: num:: fmt as numfmt;
56use crate :: ops:: { Div , Rem , Sub } ;
@@ -199,6 +200,42 @@ 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 ( buf : & NumBuffer , offset : usize ) -> & str {
209+ let written = unsafe { buf. contents . get_unchecked ( offset..) } ;
210+ let as_str = unsafe {
211+ str:: from_utf8_unchecked ( slice:: from_raw_parts (
212+ MaybeUninit :: slice_as_ptr ( written) ,
213+ written. len ( ) ,
214+ ) )
215+ } ;
216+
217+ as_str
218+ }
219+
220+ // Unsafe because
221+ // 1. Offset is not bound checked
222+ unsafe fn _add_negative_sign (
223+ is_nonnegative : bool ,
224+ buf : & mut NumBuffer ,
225+ start_offset : usize ,
226+ ) -> usize {
227+ if is_nonnegative {
228+ return start_offset;
229+ }
230+
231+ let offset = start_offset - 1 ;
232+
233+ // Setting sign for the negative number
234+ buf. contents [ offset] . write ( NEGATIVE_SIGN [ 0 ] ) ;
235+
236+ offset
237+ }
238+
202239macro_rules! impl_Display {
203240 ( $( $signed: ident, $unsigned: ident, ) * ; as $u: ident via $conv_fn: ident named $gen_name: ident) => {
204241
@@ -212,7 +249,9 @@ macro_rules! impl_Display {
212249 }
213250 #[ cfg( feature = "optimize_for_size" ) ]
214251 {
215- $gen_name( self . $conv_fn( ) , true , f)
252+ let mut buf = NumBuffer :: new( ) ;
253+ let as_str = $gen_name( self . $conv_fn( ) , true , & mut buf) ;
254+ f. pad_integral( true , "" , as_str)
216255 }
217256 }
218257 }
@@ -226,31 +265,122 @@ macro_rules! impl_Display {
226265 }
227266 #[ cfg( feature = "optimize_for_size" ) ]
228267 {
229- return $gen_name( self . unsigned_abs( ) . $conv_fn( ) , * self >= 0 , f) ;
268+ let mut buf = NumBuffer :: new( ) ;
269+
270+ // not setting the sign here, hence sending in `true`
271+ let as_str = $gen_name( self . unsigned_abs( ) . $conv_fn( ) , true , & mut buf) ;
272+ f. pad_integral( * self >= 0 , "" , as_str)
273+ }
274+ }
275+ }
276+
277+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
278+ impl $signed {
279+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
280+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
281+ ///
282+ /// # Examples
283+ /// ```
284+ /// #![feature(int_format_into)]
285+ /// use core::fmt::NumBuffer;
286+ ///
287+ #[ doc = concat!( "let n = -32" , stringify!( $signed) , ";" ) ]
288+ /// let mut buf = NumBuffer::new();
289+ /// assert_eq!(n.format_into(&mut buf), "-32");
290+ ///
291+ #[ doc = concat!( "let n2 = " , stringify!( $signed:: MIN ) , ";" ) ]
292+ /// let mut buf2 = NumBuffer::new();
293+ #[ doc = concat!( "assert_eq!(n2.format_into(&mut buf2), " , stringify!( $signed:: MIN ) , ".to_string());" ) ]
294+ ///
295+ #[ doc = concat!( "let n3 = " , stringify!( $signed:: MAX ) , ";" ) ]
296+ /// let mut buf3 = NumBuffer::new();
297+ #[ doc = concat!( "assert_eq!(n3.format_into(&mut buf3), " , stringify!( $signed:: MAX ) , ".to_string());" ) ]
298+ /// ```
299+ ///
300+ pub fn format_into( self , buf: & mut NumBuffer ) -> & str {
301+ #[ cfg( not( feature = "optimize_for_size" ) ) ]
302+ {
303+ let is_nonnegative = self > 0 ;
304+ let offset = self . unsigned_abs( ) . _write_into_buf( buf) ;
305+
306+ // SAFETY: `offset >= 2` (unchanged if `is_nonnegative` is true) and
307+ // `offset >= 1` (incase `is_nonnegative` is false) so
308+ // `buf_ptr[offset..offset + 1]` is safe to access.
309+ unsafe { _add_negative_sign( is_nonnegative, buf, offset) ; }
310+
311+ // SAFETY: All buf content since offset is set, and
312+ // writes use ASCII from the lookup table exclusively.
313+ let as_str = unsafe { _extract_str_from_buf( buf, offset) } ;
314+
315+ as_str
316+ }
317+
318+ #[ cfg( feature = "optimize_for_size" ) ]
319+ {
320+ let is_nonnegative = self > 0 ;
321+ $gen_name( self . unsigned_abs( ) , is_nonnegative, buf)
322+ }
323+
324+ }
325+ }
326+
327+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
328+ impl $unsigned {
329+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
330+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
331+ ///
332+ /// # Examples
333+ /// ```
334+ /// #![feature(int_format_into)]
335+ /// use core::fmt::NumBuffer;
336+ ///
337+ #[ doc = concat!( "let n = 32" , stringify!( $unsigned) , ";" ) ]
338+ /// let mut buf = NumBuffer::new();
339+ /// assert_eq!(n.format_into(&mut buf), "32");
340+ ///
341+ #[ doc = concat!( "let n2 = " , stringify!( $unsigned:: MAX ) , ";" ) ]
342+ /// let mut buf2 = NumBuffer::new();
343+ #[ doc = concat!( "assert_eq!(n2.format_into(&mut buf2), " , stringify!( $unsigned:: MAX ) , ".to_string());" ) ]
344+ /// ```
345+ ///
346+ pub fn format_into( self , buf: & mut NumBuffer ) -> & str {
347+ #[ cfg( not( feature = "optimize_for_size" ) ) ]
348+ {
349+ let offset = self . _write_into_buf( buf) ;
350+
351+ // SAFETY: All contents in `buf` since offset is set, and
352+ // writes use ASCII from the lookup table exclusively.
353+ let as_str = unsafe { _extract_str_from_buf( buf, offset) } ;
354+
355+ as_str
356+ }
357+
358+ #[ cfg( feature = "optimize_for_size" ) ]
359+ {
360+ $gen_name( self , true , buf)
230361 }
362+
231363 }
232364 }
233365
234366 #[ cfg( not( feature = "optimize_for_size" ) ) ]
235367 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 ] ;
368+ fn _write_into_buf( self , buf: & mut NumBuffer ) -> usize {
240369 // Count the number of bytes in buf that are not initialized.
241- let mut offset = buf. len( ) ;
370+ let mut offset = buf. contents . len( ) ;
242371 // Consume the least-significant decimals from a working copy.
243372 let mut remain = self ;
244373
245374 // Format per four digits from the lookup table.
246375 // Four digits need a 16-bit $unsigned or wider.
247376 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
377+ // SAFETY: All of the decimals fit in buf since buf is large enough to
378+ // accommodate the largest representation of a number possible (that of i128::MIN)
249379 // and the while condition ensures at least 4 more decimals.
250380 unsafe { core:: hint:: assert_unchecked( offset >= 4 ) }
251- // SAFETY: The offset counts down from its initial buf.len()
381+ // SAFETY: The offset counts down from its initial size
252382 // without underflow due to the previous precondition.
253- unsafe { core:: hint:: assert_unchecked( offset <= buf. len( ) ) }
383+ unsafe { core:: hint:: assert_unchecked( offset <= buf. contents . len( ) ) }
254384 offset -= 4 ;
255385
256386 // pull two pairs
@@ -259,26 +389,27 @@ macro_rules! impl_Display {
259389 remain /= scale;
260390 let pair1 = ( quad / 100 ) as usize ;
261391 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 ] ) ;
392+ buf. contents [ offset + 0 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 0 ] ) ;
393+ buf. contents [ offset + 1 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 1 ] ) ;
394+ buf. contents [ offset + 2 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 0 ] ) ;
395+ buf. contents [ offset + 3 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 1 ] ) ;
266396 }
267397
268398 // Format per two digits from the lookup table.
269399 if remain > 9 {
270- // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
400+ // SAFETY: All of the decimals fit in buf since buf is large enough to
401+ // accommodate the largest representation of a number possilble (that of i128::MIN)
271402 // and the while condition ensures at least 2 more decimals.
272403 unsafe { core:: hint:: assert_unchecked( offset >= 2 ) }
273- // SAFETY: The offset counts down from its initial buf.len()
404+ // SAFETY: The offset counts down from its initial size
274405 // without underflow due to the previous precondition.
275- unsafe { core:: hint:: assert_unchecked( offset <= buf. len( ) ) }
406+ unsafe { core:: hint:: assert_unchecked( offset <= buf. contents . len( ) ) }
276407 offset -= 2 ;
277408
278409 let pair = ( remain % 100 ) as usize ;
279410 remain /= 100 ;
280- buf[ offset + 0 ] . write( DEC_DIGITS_LUT [ pair * 2 + 0 ] ) ;
281- buf[ offset + 1 ] . write( DEC_DIGITS_LUT [ pair * 2 + 1 ] ) ;
411+ buf. contents [ offset + 0 ] . write( DEC_DIGITS_LUT [ pair * 2 + 0 ] ) ;
412+ buf. contents [ offset + 1 ] . write( DEC_DIGITS_LUT [ pair * 2 + 1 ] ) ;
282413 }
283414
284415 // Format the last remaining digit, if any.
@@ -288,40 +419,43 @@ macro_rules! impl_Display {
288419 unsafe { core:: hint:: assert_unchecked( offset >= 1 ) }
289420 // SAFETY: The offset counts down from its initial buf.len()
290421 // without underflow due to the previous precondition.
291- unsafe { core:: hint:: assert_unchecked( offset <= buf. len( ) ) }
422+ unsafe { core:: hint:: assert_unchecked( offset <= buf. contents . len( ) ) }
292423 offset -= 1 ;
293424
294425 // Either the compiler sees that remain < 10, or it prevents
295426 // a boundary check up next.
296427 let last = ( remain & 15 ) as usize ;
297- buf[ offset] . write( DEC_DIGITS_LUT [ last * 2 + 1 ] ) ;
428+ buf. contents [ offset] . write( DEC_DIGITS_LUT [ last * 2 + 1 ] ) ;
298429 // not used: remain = 0;
299430 }
300431
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.
432+ offset
433+ }
434+
435+ fn _fmt( self , is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
436+ // Buffer decimals for $unsigned with right alignment.
437+ let mut buf = NumBuffer :: new( ) ;
438+ let offset = self . _write_into_buf( & mut buf) ;
439+
440+ // SAFETY: All buf content since offset is set, and
441+ // writes use ASCII from the lookup table exclusively.
304442 let as_str = unsafe {
305- str :: from_utf8_unchecked( slice:: from_raw_parts(
306- MaybeUninit :: slice_as_ptr( written) ,
307- written. len( ) ,
308- ) )
443+ _extract_str_from_buf( & buf, offset)
309444 } ;
445+
310446 f. pad_integral( is_nonnegative, "" , as_str)
311447 }
312448 } ) *
313449
314450 #[ 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) ;
451+ fn $gen_name( mut n: $u, is_nonnegative: bool , buf: & mut NumBuffer ) -> & str {
452+ let mut curr = buf. contents. len( ) ;
453+ let buf_ptr = MaybeUninit :: slice_as_mut_ptr( & mut buf. contents) ;
320454
321455 // 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
456+ // `curr == buf.len() == 41 > log(n)` since `n < 2^128 < 10^39 < 10^41 `, and at
323457 // 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]`
458+ // non-negative, this means that `curr >= (41 - 39) == 2 > 0` so `buf_ptr[curr..curr + 1]`
325459 // is safe to access.
326460 unsafe {
327461 loop {
@@ -335,12 +469,18 @@ macro_rules! impl_Display {
335469 }
336470 }
337471
338- // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8
472+ // SAFETY: `curr >= 2` (unchanged if `is_nonnegative` is true) and
473+ // `curr >= 1` (incase `is_nonnegative` is false) so `buf_ptr[curr..curr + 1]`
474+ // is safe to access.
475+ unsafe { _add_negative_sign( is_nonnegative, buf, curr) ; }
476+
477+ // SAFETY: `curr >= 1` (since we made `buf` large enough), and all the chars are valid UTF-8
339478 let buf_slice = unsafe {
340479 str :: from_utf8_unchecked(
341480 slice:: from_raw_parts( buf_ptr. add( curr) , buf. len( ) - curr) )
342481 } ;
343- f. pad_integral( is_nonnegative, "" , buf_slice)
482+
483+ buf_slice
344484 }
345485 } ;
346486}
@@ -529,7 +669,7 @@ mod imp {
529669 i32 , u32 ,
530670 i64 , u64 ,
531671 isize , usize ,
532- ; as u64 via to_u64 named fmt_u64
672+ ; as u64 via to_u64 named stringify_u64
533673 ) ;
534674 impl_Exp ! (
535675 i8 , u8 , i16 , u16 , i32 , u32 , i64 , u64 , usize , isize
@@ -545,10 +685,10 @@ mod imp {
545685 i16 , u16 ,
546686 i32 , u32 ,
547687 isize , usize ,
548- ; as u32 via to_u32 named fmt_u32 ) ;
688+ ; as u32 via to_u32 named stringify_u32 ) ;
549689 impl_Display ! (
550690 i64 , u64 ,
551- ; as u64 via to_u64 named fmt_u64 ) ;
691+ ; as u64 via to_u64 named stringify_u64 ) ;
552692
553693 impl_Exp ! ( i8 , u8 , i16 , u16 , i32 , u32 , isize , usize as u32 via to_u32 named exp_u32) ;
554694 impl_Exp ! ( i64 , u64 as u64 via to_u64 named exp_u64) ;
0 commit comments