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,19 @@ 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
206+ ///
207+ /// `buf` content starting from `offset` index MUST BE initialized and MUST BE ascii
208+ /// characters.
209+ unsafe fn slice_buffer_to_str ( buf : & [ MaybeUninit < u8 > ] , offset : usize ) -> & str {
210+ // SAFETY: `offset` is always included between 0 and `buf`'s length.
211+ let written = unsafe { buf. get_unchecked ( offset..) } ;
212+ // SAFETY: Writes use ASCII from the lookup table exclusively.
213+ unsafe { str:: from_utf8_unchecked ( written. assume_init_ref ( ) ) }
214+ }
215+
202216macro_rules! impl_Display {
203217 ( $( $signed: ident, $unsigned: ident, ) * ; as $u: ident via $conv_fn: ident named $gen_name: ident) => {
204218
@@ -248,6 +262,13 @@ macro_rules! impl_Display {
248262 issue = "none"
249263 ) ]
250264 pub fn _fmt<' a>( self , buf: & ' a mut [ MaybeUninit :: <u8 >] ) -> & ' a str {
265+ // SAFETY: `buf` will always be big enough to contain all digits.
266+ let offset = unsafe { self . _fmt_inner( buf) } ;
267+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
268+ unsafe { slice_buffer_to_str( buf, offset) }
269+ }
270+
271+ unsafe fn _fmt_inner( self , buf: & mut [ MaybeUninit :: <u8 >] ) -> usize {
251272 // Count the number of bytes in buf that are not initialized.
252273 let mut offset = buf. len( ) ;
253274 // Consume the least-significant decimals from a working copy.
@@ -309,24 +330,99 @@ macro_rules! impl_Display {
309330 // not used: remain = 0;
310331 }
311332
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.
333+ offset
334+ }
335+ }
336+
337+ impl $signed {
338+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
339+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
340+ ///
341+ /// # Examples
342+ ///
343+ /// ```
344+ /// #![feature(int_format_into)]
345+ /// use core::fmt::NumBuffer;
346+ ///
347+ #[ doc = concat!( "let n = 0" , stringify!( $signed) , ";" ) ]
348+ /// let mut buf = NumBuffer::new();
349+ /// assert_eq!(n.format_into(&mut buf), "0");
350+ ///
351+ #[ doc = concat!( "let n1 = 32" , stringify!( $signed) , ";" ) ]
352+ /// assert_eq!(n1.format_into(&mut buf), "32");
353+ ///
354+ #[ doc = concat!( "let n2 = " , stringify!( $signed:: MAX ) , ";" ) ]
355+ #[ doc = concat!( "assert_eq!(n2.format_into(&mut buf), " , stringify!( $signed:: 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+ // SAFETY: `buf` will always be big enough to contain all digits.
315363 unsafe {
316- str :: from_utf8_unchecked( slice:: from_raw_parts(
317- MaybeUninit :: slice_as_ptr( written) ,
318- written. len( ) ,
319- ) )
364+ offset = self . unsigned_abs( ) . _fmt_inner( & mut buf. buf) ;
365+ }
366+ #[ cfg( feature = "optimize_for_size" ) ]
367+ {
368+ offset = _inner_slow_integer_to_str( self . unsigned_abs( ) . $conv_fn( ) , & mut buf. buf) ;
369+ }
370+ // Only difference between signed and unsigned are these 4 lines.
371+ if self < 0 {
372+ offset -= 1 ;
373+ // SAFETY: `buf` will always be big enough for negative numbers to contain all
374+ // digits plus the minus sign.
375+ unsafe { buf. buf. get_unchecked_mut( offset) . write( b'-' ) ; }
320376 }
377+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
378+ unsafe { slice_buffer_to_str( & buf. buf, offset) }
321379 }
322- } ) *
380+ }
381+
382+ impl $unsigned {
383+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
384+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
385+ ///
386+ /// # Examples
387+ ///
388+ /// ```
389+ /// #![feature(int_format_into)]
390+ /// use core::fmt::NumBuffer;
391+ ///
392+ #[ doc = concat!( "let n = 0" , stringify!( $unsigned) , ";" ) ]
393+ /// let mut buf = NumBuffer::new();
394+ /// assert_eq!(n.format_into(&mut buf), "0");
395+ ///
396+ #[ doc = concat!( "let n1 = 32" , stringify!( $unsigned) , ";" ) ]
397+ /// assert_eq!(n1.format_into(&mut buf), "32");
398+ ///
399+ #[ doc = concat!( "let n2 = " , stringify!( $unsigned:: MAX ) , ";" ) ]
400+ #[ doc = concat!( "assert_eq!(n2.format_into(&mut buf), " , stringify!( $unsigned:: MAX ) , ".to_string());" ) ]
401+ /// ```
402+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
403+ pub fn format_into( self , buf: & mut NumBuffer <Self >) -> & str {
404+ let offset;
405+
406+ #[ cfg( not( feature = "optimize_for_size" ) ) ]
407+ // SAFETY: `buf` will always be big enough to contain all digits.
408+ unsafe {
409+ offset = self . _fmt_inner( & mut buf. buf) ;
410+ }
411+ #[ cfg( feature = "optimize_for_size" ) ]
412+ {
413+ offset = _inner_slow_integer_to_str( self . $conv_fn( ) , & mut buf. buf) ;
414+ }
415+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
416+ unsafe { slice_buffer_to_str( & buf. buf, offset) }
417+ }
418+ }
419+
420+
421+ ) *
323422
324423 #[ 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) ;
424+ fn _inner_slow_integer_to_str( mut n: $u, buf: & mut [ MaybeUninit :: <u8 >] ) -> usize {
425+ let mut curr = buf. len( ) ;
330426
331427 // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning
332428 // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
@@ -336,20 +432,26 @@ macro_rules! impl_Display {
336432 unsafe {
337433 loop {
338434 curr -= 1 ;
339- buf_ptr. add( curr) . write( ( n % 10 ) as u8 + b'0' ) ;
435+ // SAFETY: `buf` will always be big enough to contain all digits.
436+ buf. get_unchecked( curr) . write( ( n % 10 ) as u8 + b'0' ) ;
340437 n /= 10 ;
341438
342439 if n == 0 {
343440 break ;
344441 }
345442 }
346443 }
444+ cur
445+ }
347446
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- } ;
447+ #[ cfg( feature = "optimize_for_size" ) ]
448+ fn $gen_name( n: $u, is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
449+ const MAX_DEC_N : usize = $u:: MAX . ilog( 10 ) as usize + 1 ;
450+ let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; MAX_DEC_N ] ;
451+
452+ let offset = _inner_slow_integer_to_str( n, & mut buf) ;
453+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
454+ let buf_slice = unsafe { slice_buffer_to_str( & buf, offset) } ;
353455 f. pad_integral( is_nonnegative, "" , buf_slice)
354456 }
355457 } ;
@@ -598,12 +700,23 @@ impl u128 {
598700 issue = "none"
599701 ) ]
600702 pub fn _fmt < ' a > ( self , buf : & ' a mut [ MaybeUninit < u8 > ] ) -> & ' a str {
703+ // SAFETY: `buf` will always be big enough to contain all digits.
704+ let offset = unsafe { self . _fmt_inner ( buf) } ;
705+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
706+ unsafe { slice_buffer_to_str ( buf, offset) }
707+ }
708+
709+ unsafe fn _fmt_inner ( self , buf : & mut [ MaybeUninit < u8 > ] ) -> usize {
601710 // Optimize common-case zero, which would also need special treatment due to
602711 // its "leading" zero.
603712 if self == 0 {
604- return "0" ;
713+ let offset = buf. len ( ) - 1 ;
714+ // SAFETY: `buf` will always be big enough to contain all digits.
715+ unsafe {
716+ buf. get_unchecked_mut ( offset) . write ( b'0' ) ;
717+ }
718+ return offset;
605719 }
606-
607720 // Take the 16 least-significant decimals.
608721 let ( quot_1e16, mod_1e16) = div_rem_1e16 ( self ) ;
609722 let ( mut remain, mut offset) = if quot_1e16 == 0 {
@@ -677,16 +790,86 @@ impl u128 {
677790 buf[ offset] . write ( DEC_DIGITS_LUT [ last * 2 + 1 ] ) ;
678791 // not used: remain = 0;
679792 }
793+ offset
794+ }
680795
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- ) )
796+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
797+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
798+ ///
799+ /// # Examples
800+ ///
801+ /// ```
802+ /// #![feature(int_format_into)]
803+ /// use core::fmt::NumBuffer;
804+ ///
805+ /// let n = 0u128;
806+ /// let mut buf = NumBuffer::new();
807+ /// assert_eq!(n.format_into(&mut buf), "0");
808+ ///
809+ /// let n1 = 32u128;
810+ /// let mut buf1 = NumBuffer::new();
811+ /// assert_eq!(n1.format_into(&mut buf1), "32");
812+ ///
813+ /// let n2 = u128::MAX;
814+ /// let mut buf2 = NumBuffer::new();
815+ /// assert_eq!(n2.format_into(&mut buf2), u128::MAX.to_string());
816+ /// ```
817+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
818+ pub fn format_into ( self , buf : & mut NumBuffer < Self > ) -> & str {
819+ let diff = buf. capacity ( ) - U128_MAX_DEC_N ;
820+ // FIXME: Once const generics are better, use `NumberBufferTrait::BUF_SIZE` as generic const
821+ // for `fmt_u128_inner`.
822+ //
823+ // In the meantime, we have to use a slice starting at index 1 and add 1 to the returned
824+ // offset to ensure the number is correctly generated at the end of the buffer.
825+ // SAFETY: `diff` will always be between 0 and its initial value.
826+ unsafe { self . _fmt ( buf. buf . get_unchecked_mut ( diff..) ) }
827+ }
828+ }
829+
830+ impl i128 {
831+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
832+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
833+ ///
834+ /// # Examples
835+ ///
836+ /// ```
837+ /// #![feature(int_format_into)]
838+ /// use core::fmt::NumBuffer;
839+ ///
840+ /// let n = 0i128;
841+ /// let mut buf = NumBuffer::new();
842+ /// assert_eq!(n.format_into(&mut buf), "0");
843+ ///
844+ /// let n1 = i128::MIN;
845+ /// assert_eq!(n1.format_into(&mut buf), i128::MIN.to_string());
846+ ///
847+ /// let n2 = i128::MAX;
848+ /// assert_eq!(n2.format_into(&mut buf), i128::MAX.to_string());
849+ /// ```
850+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
851+ pub fn format_into ( self , buf : & mut NumBuffer < Self > ) -> & str {
852+ let diff = buf. capacity ( ) - U128_MAX_DEC_N ;
853+ // FIXME: Once const generics are better, use `NumberBufferTrait::BUF_SIZE` as generic const
854+ // for `fmt_u128_inner`.
855+ //
856+ // In the meantime, we have to use a slice starting at index 1 and add 1 to the returned
857+ // offset to ensure the number is correctly generated at the end of the buffer.
858+ let mut offset =
859+ // SAFETY: `buf` will always be big enough to contain all digits.
860+ unsafe { self . unsigned_abs ( ) . _fmt_inner ( buf. buf . get_unchecked_mut ( diff..) ) } ;
861+ // We put back the offset at the right position.
862+ offset += diff;
863+ // Only difference between signed and unsigned are these 4 lines.
864+ if self < 0 {
865+ offset -= 1 ;
866+ // SAFETY: `buf` will always be big enough to contain all digits plus the minus sign.
867+ unsafe {
868+ buf. buf . get_unchecked_mut ( offset) . write ( b'-' ) ;
869+ }
689870 }
871+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
872+ unsafe { slice_buffer_to_str ( & buf. buf , offset) }
690873 }
691874}
692875
0 commit comments