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,17 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"\
199200 6061626364656667686970717273747576777879\
200201 8081828384858687888990919293949596979899";
201202
203+ /// This function converts a slice of ascii characters into a `&str` starting from `offset`.
204+ ///
205+ /// Safety notes: `buf` content starting from `offset` index MUST BE initialized and MUST BE ascii
206+ /// characters.
207+ unsafe fn slice_buffer_to_str ( buf : & [ MaybeUninit < u8 > ] , offset : usize ) -> & str {
208+ // SAFETY: All buf content since offset is set.
209+ let written = unsafe { buf. get_unchecked ( offset..) } ;
210+ // SAFETY: Writes use ASCII from the lookup table exclusively.
211+ unsafe { str:: from_utf8_unchecked ( written. assume_init_ref ( ) ) }
212+ }
213+
202214macro_rules! impl_Display {
203215 ( $( $signed: ident, $unsigned: ident, ) * ; as $u: ident via $conv_fn: ident named $gen_name: ident) => {
204216
@@ -248,6 +260,12 @@ macro_rules! impl_Display {
248260 issue = "none"
249261 ) ]
250262 pub fn _fmt<' a>( self , buf: & ' a mut [ MaybeUninit :: <u8 >] ) -> & ' a str {
263+ let offset = self . _fmt_inner( buf) ;
264+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
265+ unsafe { slice_buffer_to_str( buf, offset) }
266+ }
267+
268+ fn _fmt_inner( self , buf: & mut [ MaybeUninit :: <u8 >] ) -> usize {
251269 // Count the number of bytes in buf that are not initialized.
252270 let mut offset = buf. len( ) ;
253271 // Consume the least-significant decimals from a working copy.
@@ -309,24 +327,95 @@ macro_rules! impl_Display {
309327 // not used: remain = 0;
310328 }
311329
312- // SAFETY: All buf content since offset is set.
313- let written = unsafe { buf. get_unchecked( offset..) } ;
314- // SAFETY: Writes use ASCII from the lookup table exclusively.
315- unsafe {
316- str :: from_utf8_unchecked( slice:: from_raw_parts(
317- MaybeUninit :: slice_as_ptr( written) ,
318- written. len( ) ,
319- ) )
330+ offset
331+ }
332+ }
333+
334+ impl $signed {
335+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
336+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
337+ ///
338+ /// # Examples
339+ ///
340+ /// ```
341+ /// #![feature(int_format_into)]
342+ /// use core::fmt::NumBuffer;
343+ ///
344+ #[ doc = concat!( "let n = 0" , stringify!( $signed) , ";" ) ]
345+ /// let mut buf = NumBuffer::new();
346+ /// assert_eq!(n.format_into(&mut buf), "0");
347+ ///
348+ #[ doc = concat!( "let n1 = 32" , stringify!( $signed) , ";" ) ]
349+ /// assert_eq!(n1.format_into(&mut buf), "32");
350+ ///
351+ #[ doc = concat!( "let n2 = " , stringify!( $signed:: MAX ) , ";" ) ]
352+ #[ doc = concat!( "assert_eq!(n2.format_into(&mut buf), " , stringify!( $signed:: MAX ) , ".to_string());" ) ]
353+ /// ```
354+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
355+ pub fn format_into( self , buf: & mut NumBuffer <Self >) -> & str {
356+ let mut offset;
357+
358+ #[ cfg( not( feature = "optimize_for_size" ) ) ]
359+ {
360+ offset = self . unsigned_abs( ) . _fmt_inner( & mut buf. buf) ;
361+ }
362+ #[ cfg( feature = "optimize_for_size" ) ]
363+ {
364+ offset = _inner_slow_integer_to_str( self . unsigned_abs( ) . $conv_fn( ) , & mut buf. buf) ;
365+ }
366+ // Only difference between signed and unsigned are these 4 lines.
367+ if self < 0 {
368+ offset -= 1 ;
369+ buf. buf[ offset] . write( b'-' ) ;
320370 }
371+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
372+ unsafe { slice_buffer_to_str( & buf. buf, offset) }
321373 }
322- } ) *
374+ }
375+
376+ impl $unsigned {
377+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
378+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
379+ ///
380+ /// # Examples
381+ ///
382+ /// ```
383+ /// #![feature(int_format_into)]
384+ /// use core::fmt::NumBuffer;
385+ ///
386+ #[ doc = concat!( "let n = 0" , stringify!( $unsigned) , ";" ) ]
387+ /// let mut buf = NumBuffer::new();
388+ /// assert_eq!(n.format_into(&mut buf), "0");
389+ ///
390+ #[ doc = concat!( "let n1 = 32" , stringify!( $unsigned) , ";" ) ]
391+ /// assert_eq!(n1.format_into(&mut buf), "32");
392+ ///
393+ #[ doc = concat!( "let n2 = " , stringify!( $unsigned:: MAX ) , ";" ) ]
394+ #[ doc = concat!( "assert_eq!(n2.format_into(&mut buf), " , stringify!( $unsigned:: MAX ) , ".to_string());" ) ]
395+ /// ```
396+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
397+ pub fn format_into( self , buf: & mut NumBuffer <Self >) -> & str {
398+ let offset;
399+
400+ #[ cfg( not( feature = "optimize_for_size" ) ) ]
401+ {
402+ offset = self . _fmt_inner( & mut buf. buf) ;
403+ }
404+ #[ cfg( feature = "optimize_for_size" ) ]
405+ {
406+ offset = _inner_slow_integer_to_str( self . $conv_fn( ) , & mut buf. buf) ;
407+ }
408+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
409+ unsafe { slice_buffer_to_str( & buf. buf, offset) }
410+ }
411+ }
412+
413+
414+ ) *
323415
324416 #[ cfg( feature = "optimize_for_size" ) ]
325- fn $gen_name( mut n: $u, is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
326- const MAX_DEC_N : usize = $u:: MAX . ilog10( ) as usize + 1 ;
327- let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; MAX_DEC_N ] ;
328- let mut curr = MAX_DEC_N ;
329- let buf_ptr = MaybeUninit :: slice_as_mut_ptr( & mut buf) ;
417+ fn _inner_slow_integer_to_str( mut n: $u, buf: & mut [ MaybeUninit :: <u8 >] ) -> usize {
418+ let mut curr = buf. len( ) ;
330419
331420 // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning
332421 // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
@@ -336,20 +425,25 @@ macro_rules! impl_Display {
336425 unsafe {
337426 loop {
338427 curr -= 1 ;
339- buf_ptr . add ( curr) . write( ( n % 10 ) as u8 + b'0' ) ;
428+ buf [ curr] . write( ( n % 10 ) as u8 + b'0' ) ;
340429 n /= 10 ;
341430
342431 if n == 0 {
343432 break ;
344433 }
345434 }
346435 }
436+ cur
437+ }
347438
348- // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8
349- let buf_slice = unsafe {
350- str :: from_utf8_unchecked(
351- slice:: from_raw_parts( buf_ptr. add( curr) , buf. len( ) - curr) )
352- } ;
439+ #[ cfg( feature = "optimize_for_size" ) ]
440+ fn $gen_name( n: $u, is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
441+ const MAX_DEC_N : usize = $u:: MAX . ilog( 10 ) as usize + 1 ;
442+ let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; MAX_DEC_N ] ;
443+
444+ let offset = _inner_slow_integer_to_str( n, & mut buf) ;
445+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
446+ let buf_slice = unsafe { slice_buffer_to_str( & buf, offset) } ;
353447 f. pad_integral( is_nonnegative, "" , buf_slice)
354448 }
355449 } ;
@@ -598,12 +692,26 @@ impl u128 {
598692 issue = "none"
599693 ) ]
600694 pub fn _fmt < ' a > ( self , buf : & ' a mut [ MaybeUninit < u8 > ] ) -> & ' a str {
695+ let offset = self . _fmt_inner ( buf) ;
696+ // SAFETY: All buf content since offset is set.
697+ let written = unsafe { buf. get_unchecked ( offset..) } ;
698+ // SAFETY: Writes use ASCII from the lookup table exclusively.
699+ unsafe {
700+ str:: from_utf8_unchecked ( slice:: from_raw_parts (
701+ MaybeUninit :: slice_as_ptr ( written) ,
702+ written. len ( ) ,
703+ ) )
704+ }
705+ }
706+
707+ fn _fmt_inner ( self , buf : & mut [ MaybeUninit < u8 > ] ) -> usize {
601708 // Optimize common-case zero, which would also need special treatment due to
602709 // its "leading" zero.
603710 if self == 0 {
604- return "0" ;
711+ let offset = buf. len ( ) - 1 ;
712+ buf[ offset] . write ( b'0' ) ;
713+ return offset;
605714 }
606-
607715 // Take the 16 least-significant decimals.
608716 let ( quot_1e16, mod_1e16) = div_rem_1e16 ( self ) ;
609717 let ( mut remain, mut offset) = if quot_1e16 == 0 {
@@ -677,16 +785,80 @@ impl u128 {
677785 buf[ offset] . write ( DEC_DIGITS_LUT [ last * 2 + 1 ] ) ;
678786 // not used: remain = 0;
679787 }
788+ offset
789+ }
680790
681- // SAFETY: All buf content since offset is set.
682- let written = unsafe { buf. get_unchecked ( offset..) } ;
683- // SAFETY: Writes use ASCII from the lookup table exclusively.
684- unsafe {
685- str:: from_utf8_unchecked ( slice:: from_raw_parts (
686- MaybeUninit :: slice_as_ptr ( written) ,
687- written. len ( ) ,
688- ) )
791+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
792+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
793+ ///
794+ /// # Examples
795+ ///
796+ /// ```
797+ /// #![feature(int_format_into)]
798+ /// use core::fmt::NumBuffer;
799+ ///
800+ /// let n = 0u128;
801+ /// let mut buf = NumBuffer::new();
802+ /// assert_eq!(n.format_into(&mut buf), "0");
803+ ///
804+ /// let n1 = 32u128;
805+ /// let mut buf1 = NumBuffer::new();
806+ /// assert_eq!(n1.format_into(&mut buf1), "32");
807+ ///
808+ /// let n2 = u128::MAX;
809+ /// let mut buf2 = NumBuffer::new();
810+ /// assert_eq!(n2.format_into(&mut buf2), u128::MAX.to_string());
811+ /// ```
812+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
813+ pub fn format_into ( self , buf : & mut NumBuffer < Self > ) -> & str {
814+ let diff = buf. len ( ) - U128_MAX_DEC_N ;
815+ // FIXME: Once const generics are better, use `NumberBufferTrait::BUF_SIZE` as generic const
816+ // for `fmt_u128_inner`.
817+ //
818+ // In the meantime, we have to use a slice starting at index 1 and add 1 to the returned
819+ // offset to ensure the number is correctly generated at the end of the buffer.
820+ self . _fmt ( & mut buf. buf [ diff..] )
821+ }
822+ }
823+
824+ impl i128 {
825+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
826+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
827+ ///
828+ /// # Examples
829+ ///
830+ /// ```
831+ /// #![feature(int_format_into)]
832+ /// use core::fmt::NumBuffer;
833+ ///
834+ /// let n = 0i128;
835+ /// let mut buf = NumBuffer::new();
836+ /// assert_eq!(n.format_into(&mut buf), "0");
837+ ///
838+ /// let n1 = i128::MIN;
839+ /// assert_eq!(n1.format_into(&mut buf), i128::MIN.to_string());
840+ ///
841+ /// let n2 = i128::MAX;
842+ /// assert_eq!(n2.format_into(&mut buf), i128::MAX.to_string());
843+ /// ```
844+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
845+ pub fn format_into ( self , buf : & mut NumBuffer < Self > ) -> & str {
846+ let diff = buf. len ( ) - U128_MAX_DEC_N ;
847+ // FIXME: Once const generics are better, use `NumberBufferTrait::BUF_SIZE` as generic const
848+ // for `fmt_u128_inner`.
849+ //
850+ // In the meantime, we have to use a slice starting at index 1 and add 1 to the returned
851+ // offset to ensure the number is correctly generated at the end of the buffer.
852+ let mut offset = self . unsigned_abs ( ) . _fmt_inner ( & mut buf. buf [ diff..] ) ;
853+ // We put back the offset at the right position.
854+ offset += diff;
855+ // Only difference between signed and unsigned are these 4 lines.
856+ if self < 0 {
857+ offset -= 1 ;
858+ buf. buf [ offset] . write ( b'-' ) ;
689859 }
860+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
861+ unsafe { slice_buffer_to_str ( & buf. buf , offset) }
690862 }
691863}
692864
0 commit comments