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,18 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"\
199200 6061626364656667686970717273747576777879\
200201 8081828384858687888990919293949596979899";
201202
203+ unsafe fn slice_buffer_to_str ( buf : & [ MaybeUninit < u8 > ] , offset : usize ) -> & str {
204+ // SAFETY: All buf content since offset is set.
205+ let written = unsafe { buf. get_unchecked ( offset..) } ;
206+ // SAFETY: Writes use ASCII from the lookup table exclusively.
207+ unsafe {
208+ str:: from_utf8_unchecked ( slice:: from_raw_parts (
209+ MaybeUninit :: slice_as_ptr ( written) ,
210+ written. len ( ) ,
211+ ) )
212+ }
213+ }
214+
202215macro_rules! impl_Display {
203216 ( $( $signed: ident, $unsigned: ident, ) * ; as $u: ident via $conv_fn: ident named $gen_name: ident) => {
204217
@@ -248,6 +261,12 @@ macro_rules! impl_Display {
248261 issue = "none"
249262 ) ]
250263 pub fn _fmt<' a>( self , buf: & ' a mut [ MaybeUninit :: <u8 >] ) -> & ' a str {
264+ let offset = self . _fmt_inner( buf) ;
265+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
266+ unsafe { slice_buffer_to_str( buf, offset) }
267+ }
268+
269+ fn _fmt_inner( self , buf: & mut [ MaybeUninit :: <u8 >] ) -> usize {
251270 // Count the number of bytes in buf that are not initialized.
252271 let mut offset = buf. len( ) ;
253272 // Consume the least-significant decimals from a working copy.
@@ -309,24 +328,99 @@ macro_rules! impl_Display {
309328 // not used: remain = 0;
310329 }
311330
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- ) )
331+ offset
332+ }
333+ }
334+
335+ impl $signed {
336+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
337+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
338+ ///
339+ /// # Examples
340+ ///
341+ /// ```
342+ /// #![feature(int_format_into)]
343+ /// use core::fmt::NumBuffer;
344+ ///
345+ #[ doc = concat!( "let n = 0" , stringify!( $signed) , ";" ) ]
346+ /// let mut buf = NumBuffer::new();
347+ /// assert_eq!(n.format_into(&mut buf), "0");
348+ ///
349+ #[ doc = concat!( "let n1 = 32" , stringify!( $unsigned) , ";" ) ]
350+ /// let mut buf1 = NumBuffer::new();
351+ /// assert_eq!(n1.format_into(&mut buf1), "32");
352+ ///
353+ #[ doc = concat!( "let n2 = " , stringify!( $unsigned:: MAX ) , ";" ) ]
354+ /// let mut buf2 = NumBuffer::new();
355+ #[ doc = concat!( "assert_eq!(n2.format_into(&mut buf2), " , stringify!( $unsigned:: MAX ) , ".to_string());" ) ]
356+ /// ```
357+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
358+ pub fn format_into( self , buf: & mut NumBuffer <Self >) -> & str {
359+ let mut offset;
360+
361+ #[ cfg( not( feature = "optimize_for_size" ) ) ]
362+ {
363+ offset = self . unsigned_abs( ) . _fmt_inner( & mut buf. buf) ;
364+ }
365+ #[ cfg( feature = "optimize_for_size" ) ]
366+ {
367+ offset = _inner_slow_integer_to_str( self . unsigned_abs( ) . $conv_fn( ) , & mut buf. buf) ;
320368 }
369+ // Only difference between signed and unsigned are these 4 lines.
370+ if self < 0 {
371+ offset -= 1 ;
372+ buf. buf[ offset] . write( b'-' ) ;
373+ }
374+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
375+ unsafe { slice_buffer_to_str( & buf. buf, offset) }
321376 }
322- } ) *
377+ }
378+
379+ impl $unsigned {
380+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
381+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
382+ ///
383+ /// # Examples
384+ ///
385+ /// ```
386+ /// #![feature(int_format_into)]
387+ /// use core::fmt::NumBuffer;
388+ ///
389+ #[ doc = concat!( "let n = 0" , stringify!( $signed) , ";" ) ]
390+ /// let mut buf = NumBuffer::new();
391+ /// assert_eq!(n.format_into(&mut buf), "0");
392+ ///
393+ #[ doc = concat!( "let n1 = 32" , stringify!( $unsigned) , ";" ) ]
394+ /// let mut buf1 = NumBuffer::new();
395+ /// assert_eq!(n1.format_into(&mut buf1), "32");
396+ ///
397+ #[ doc = concat!( "let n2 = " , stringify!( $unsigned:: MAX ) , ";" ) ]
398+ /// let mut buf2 = NumBuffer::new();
399+ #[ doc = concat!( "assert_eq!(n2.format_into(&mut buf2), " , stringify!( $unsigned:: MAX ) , ".to_string());" ) ]
400+ /// ```
401+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
402+ pub fn format_into( self , buf: & mut NumBuffer <Self >) -> & str {
403+ let offset;
404+
405+ #[ cfg( not( feature = "optimize_for_size" ) ) ]
406+ {
407+ offset = self . _fmt_inner( & mut buf. buf) ;
408+ }
409+ #[ cfg( feature = "optimize_for_size" ) ]
410+ {
411+ offset = _inner_slow_integer_to_str( self . $conv_fn( ) , & mut buf. buf) ;
412+ }
413+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
414+ unsafe { slice_buffer_to_str( & buf. buf, offset) }
415+ }
416+ }
417+
418+
419+ ) *
323420
324421 #[ 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 . ilog( 10 ) 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) ;
422+ fn _inner_slow_integer_to_str( mut n: $u, buf: & mut [ MaybeUninit :: <u8 >] ) -> usize {
423+ let mut curr = buf. len( ) ;
330424
331425 // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning
332426 // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
@@ -336,21 +430,28 @@ macro_rules! impl_Display {
336430 unsafe {
337431 loop {
338432 curr -= 1 ;
339- buf_ptr . add ( curr) . write( ( n % 10 ) as u8 + b'0' ) ;
433+ buf [ curr] . write( ( n % 10 ) as u8 + b'0' ) ;
340434 n /= 10 ;
341435
342436 if n == 0 {
343437 break ;
344438 }
345439 }
346440 }
441+ cur
442+ }
347443
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- } ;
353- f. pad_integral( is_nonnegative, "" , buf_slice)
444+ #[ cfg( feature = "optimize_for_size" ) ]
445+ fn $gen_name( n: $u, is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
446+ const MAX_DEC_N : usize = $u:: MAX . ilog( 10 ) as usize + 1 ;
447+ let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; MAX_DEC_N ] ;
448+
449+ let offset = _inner_slow_integer_to_str( n, & mut buf) ;
450+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
451+ unsafe {
452+ let buf_slice = slice_buffer_to_str( & buf, offset) ;
453+ f. pad_integral( is_nonnegative, "" , buf_slice)
454+ }
354455 }
355456 } ;
356457}
@@ -566,7 +667,7 @@ mod imp {
566667impl_Exp ! ( i128 , u128 as u128 via to_u128 named exp_u128) ;
567668
568669/// Helper function for writing a u64 into `buf` going from last to first, with `curr`.
569- fn parse_u64_into < const N : usize > ( mut n : u64 , buf : & mut [ MaybeUninit < u8 > ; N ] , curr : & mut usize ) {
670+ fn parse_u64_into ( mut n : u64 , buf : & mut [ MaybeUninit < u8 > ] , curr : & mut usize ) {
570671 let buf_ptr = MaybeUninit :: slice_as_mut_ptr ( buf) ;
571672 let lut_ptr = DEC_DIGITS_LUT . as_ptr ( ) ;
572673 assert ! ( * curr > 19 ) ;
@@ -673,40 +774,98 @@ impl fmt::Display for i128 {
673774 }
674775}
675776
777+ impl u128 {
778+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
779+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
780+ ///
781+ /// # Examples
782+ ///
783+ /// ```
784+ /// #![feature(int_format_into)]
785+ /// use core::fmt::NumBuffer;
786+ ///
787+ /// let n = 0u128;
788+ /// let mut buf = NumBuffer::new();
789+ /// assert_eq!(n.format_into(&mut buf), "0");
790+ ///
791+ /// let n1 = 32u128;
792+ /// let mut buf1 = NumBuffer::new();
793+ /// assert_eq!(n1.format_into(&mut buf1), "32");
794+ ///
795+ /// let n2 = u128::MAX;
796+ /// let mut buf2 = NumBuffer::new();
797+ /// assert_eq!(n2.format_into(&mut buf2), u128::MAX.to_string());
798+ /// ```
799+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
800+ pub fn format_into ( self , buf : & mut NumBuffer < Self > ) -> & str {
801+ let offset = fmt_u128_inner ( self , & mut buf. buf ) ;
802+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
803+ unsafe { slice_buffer_to_str ( & buf. buf , offset) }
804+ }
805+ }
806+
807+ impl i128 {
808+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
809+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
810+ ///
811+ /// # Examples
812+ ///
813+ /// ```
814+ /// #![feature(int_format_into)]
815+ /// use core::fmt::NumBuffer;
816+ ///
817+ /// let n = 0i128;
818+ /// let mut buf = NumBuffer::new();
819+ /// assert_eq!(n.format_into(&mut buf), "0");
820+ ///
821+ /// let n1 = 32i128;
822+ /// let mut buf1 = NumBuffer::new();
823+ /// assert_eq!(n1.format_into(&mut buf1), "32");
824+ ///
825+ /// let n2 = i128::MAX;
826+ /// let mut buf2 = NumBuffer::new();
827+ /// assert_eq!(n2.format_into(&mut buf2), i128::MAX.to_string());
828+ /// ```
829+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
830+ pub fn format_into ( self , buf : & mut NumBuffer < Self > ) -> & str {
831+ let mut offset = fmt_u128_inner ( self . unsigned_abs ( ) , & mut buf. buf ) ;
832+ // Only difference between signed and unsigned are these 4 lines.
833+ if self < 0 {
834+ offset -= 1 ;
835+ buf. buf [ offset] . write ( b'-' ) ;
836+ }
837+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
838+ unsafe { slice_buffer_to_str ( & buf. buf , offset) }
839+ }
840+ }
841+
676842/// Specialized optimization for u128. Instead of taking two items at a time, it splits
677843/// into at most 2 u64s, and then chunks by 10e16, 10e8, 10e4, 10e2, and then 10e1.
678844/// It also has to handle 1 last item, as 10^40 > 2^128 > 10^39, whereas
679845/// 10^20 > 2^64 > 10^19.
680- fn fmt_u128 ( n : u128 , is_nonnegative : bool , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
681- // 2^128 is about 3*10^38, so 39 gives an extra byte of space
682- let mut buf = [ MaybeUninit :: < u8 > :: uninit ( ) ; 39 ] ;
846+ fn fmt_u128_inner ( n : u128 , buf : & mut [ MaybeUninit < u8 > ] ) -> usize {
683847 let mut curr = buf. len ( ) ;
684-
685848 let ( n, rem) = udiv_1e19 ( n) ;
686- parse_u64_into ( rem, & mut buf, & mut curr) ;
849+ parse_u64_into ( rem, buf, & mut curr) ;
687850
688851 if n != 0 {
689852 // 0 pad up to point
690853 let target = buf. len ( ) - 19 ;
691854 // SAFETY: Guaranteed that we wrote at most 19 bytes, and there must be space
692855 // remaining since it has length 39
693856 unsafe {
694- ptr:: write_bytes (
695- MaybeUninit :: slice_as_mut_ptr ( & mut buf) . add ( target) ,
696- b'0' ,
697- curr - target,
698- ) ;
857+ ptr:: write_bytes ( MaybeUninit :: slice_as_mut_ptr ( buf) . add ( target) , b'0' , curr - target) ;
699858 }
700859 curr = target;
701860
702861 let ( n, rem) = udiv_1e19 ( n) ;
703- parse_u64_into ( rem, & mut buf, & mut curr) ;
862+ parse_u64_into ( rem, buf, & mut curr) ;
704863 // Should this following branch be annotated with unlikely?
705864 if n != 0 {
706865 let target = buf. len ( ) - 38 ;
707866 // The raw `buf_ptr` pointer is only valid until `buf` is used the next time,
708867 // buf `buf` is not used in this scope so we are good.
709- let buf_ptr = MaybeUninit :: slice_as_mut_ptr ( & mut buf) ;
868+ let buf_ptr = MaybeUninit :: slice_as_mut_ptr ( buf) ;
710869 // SAFETY: At this point we wrote at most 38 bytes, pad up to that point,
711870 // There can only be at most 1 digit remaining.
712871 unsafe {
@@ -716,16 +875,19 @@ fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::R
716875 }
717876 }
718877 }
878+ curr
879+ }
719880
720- // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid
721- // UTF-8 since `DEC_DIGITS_LUT` is
722- let buf_slice = unsafe {
723- str:: from_utf8_unchecked ( slice:: from_raw_parts (
724- MaybeUninit :: slice_as_mut_ptr ( & mut buf) . add ( curr) ,
725- buf. len ( ) - curr,
726- ) )
727- } ;
728- f. pad_integral ( is_nonnegative, "" , buf_slice)
881+ fn fmt_u128 ( n : u128 , is_nonnegative : bool , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
882+ // 2^128 is about 3*10^38, so 39 gives an extra byte of space
883+ let mut buf = [ MaybeUninit :: < u8 > :: uninit ( ) ; 39 ] ;
884+
885+ let offset = fmt_u128_inner ( n, & mut buf) ;
886+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
887+ unsafe {
888+ let buf_slice = slice_buffer_to_str ( & buf, offset) ;
889+ f. pad_integral ( is_nonnegative, "" , buf_slice)
890+ }
729891}
730892
731893/// Partition of `n` into n > 1e19 and rem <= 1e19
0 commit comments