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,20 @@ 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: (`assume_init_ref`) All buf content since offset is set.
213+ // SAFETY: (`from_utf8_unchecked`) Writes use ASCII from the lookup table exclusively.
214+ unsafe { str:: from_utf8_unchecked ( written. assume_init_ref ( ) ) }
215+ }
216+
202217macro_rules! impl_Display {
203218 ( $( $signed: ident, $unsigned: ident, ) * ; as $u: ident via $conv_fn: ident named $gen_name: ident) => {
204219
@@ -212,7 +227,8 @@ macro_rules! impl_Display {
212227 // Buffer decimals for $unsigned with right alignment.
213228 let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; MAX_DEC_N ] ;
214229
215- f. pad_integral( true , "" , self . _fmt( & mut buf) )
230+ // SAFETY: `buf` is always big enough to contain all the digits.
231+ unsafe { f. pad_integral( true , "" , self . _fmt( & mut buf) ) }
216232 }
217233 #[ cfg( feature = "optimize_for_size" ) ]
218234 {
@@ -230,7 +246,8 @@ macro_rules! impl_Display {
230246 // Buffer decimals for $unsigned with right alignment.
231247 let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; MAX_DEC_N ] ;
232248
233- f. pad_integral( * self >= 0 , "" , self . unsigned_abs( ) . _fmt( & mut buf) )
249+ // SAFETY: `buf` is always big enough to contain all the digits.
250+ unsafe { f. pad_integral( * self >= 0 , "" , self . unsigned_abs( ) . _fmt( & mut buf) ) }
234251 }
235252 #[ cfg( feature = "optimize_for_size" ) ]
236253 {
@@ -247,7 +264,14 @@ macro_rules! impl_Display {
247264 reason = "specialized method meant to only be used by `SpecToString` implementation" ,
248265 issue = "none"
249266 ) ]
250- pub fn _fmt<' a>( self , buf: & ' a mut [ MaybeUninit :: <u8 >] ) -> & ' a str {
267+ pub unsafe fn _fmt<' a>( self , buf: & ' a mut [ MaybeUninit :: <u8 >] ) -> & ' a str {
268+ // SAFETY: `buf` will always be big enough to contain all digits.
269+ let offset = unsafe { self . _fmt_inner( buf) } ;
270+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
271+ unsafe { slice_buffer_to_str( buf, offset) }
272+ }
273+
274+ unsafe fn _fmt_inner( self , buf: & mut [ MaybeUninit :: <u8 >] ) -> usize {
251275 // Count the number of bytes in buf that are not initialized.
252276 let mut offset = buf. len( ) ;
253277 // Consume the least-significant decimals from a working copy.
@@ -309,47 +333,123 @@ macro_rules! impl_Display {
309333 // not used: remain = 0;
310334 }
311335
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.
336+ offset
337+ }
338+ }
339+
340+ impl $signed {
341+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
342+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
343+ ///
344+ /// # Examples
345+ ///
346+ /// ```
347+ /// #![feature(int_format_into)]
348+ /// use core::fmt::NumBuffer;
349+ ///
350+ #[ doc = concat!( "let n = 0" , stringify!( $signed) , ";" ) ]
351+ /// let mut buf = NumBuffer::new();
352+ /// assert_eq!(n.format_into(&mut buf), "0");
353+ ///
354+ #[ doc = concat!( "let n1 = 32" , stringify!( $signed) , ";" ) ]
355+ /// assert_eq!(n1.format_into(&mut buf), "32");
356+ ///
357+ #[ doc = concat!( "let n2 = " , stringify!( $signed:: MAX ) , ";" ) ]
358+ #[ doc = concat!( "assert_eq!(n2.format_into(&mut buf), " , stringify!( $signed:: MAX ) , ".to_string());" ) ]
359+ /// ```
360+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
361+ pub fn format_into( self , buf: & mut NumBuffer <Self >) -> & str {
362+ let mut offset;
363+
364+ #[ cfg( not( feature = "optimize_for_size" ) ) ]
365+ // SAFETY: `buf` will always be big enough to contain all digits.
366+ unsafe {
367+ offset = self . unsigned_abs( ) . _fmt_inner( & mut buf. buf) ;
368+ }
369+ #[ cfg( feature = "optimize_for_size" ) ]
370+ {
371+ offset = _inner_slow_integer_to_str( self . unsigned_abs( ) . $conv_fn( ) , & mut buf. buf) ;
372+ }
373+ // Only difference between signed and unsigned are these 4 lines.
374+ if self < 0 {
375+ offset -= 1 ;
376+ buf. buf[ offset] . write( b'-' ) ;
377+ }
378+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
379+ unsafe { slice_buffer_to_str( & buf. buf, offset) }
380+ }
381+ }
382+
383+ impl $unsigned {
384+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
385+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
386+ ///
387+ /// # Examples
388+ ///
389+ /// ```
390+ /// #![feature(int_format_into)]
391+ /// use core::fmt::NumBuffer;
392+ ///
393+ #[ doc = concat!( "let n = 0" , stringify!( $unsigned) , ";" ) ]
394+ /// let mut buf = NumBuffer::new();
395+ /// assert_eq!(n.format_into(&mut buf), "0");
396+ ///
397+ #[ doc = concat!( "let n1 = 32" , stringify!( $unsigned) , ";" ) ]
398+ /// assert_eq!(n1.format_into(&mut buf), "32");
399+ ///
400+ #[ doc = concat!( "let n2 = " , stringify!( $unsigned:: MAX ) , ";" ) ]
401+ #[ doc = concat!( "assert_eq!(n2.format_into(&mut buf), " , stringify!( $unsigned:: MAX ) , ".to_string());" ) ]
402+ /// ```
403+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
404+ pub fn format_into( self , buf: & mut NumBuffer <Self >) -> & str {
405+ let offset;
406+
407+ #[ cfg( not( feature = "optimize_for_size" ) ) ]
408+ // SAFETY: `buf` will always be big enough to contain all digits.
315409 unsafe {
316- str :: from_utf8_unchecked( slice:: from_raw_parts(
317- MaybeUninit :: slice_as_ptr( written) ,
318- written. len( ) ,
319- ) )
410+ offset = self . _fmt_inner( & mut buf. buf) ;
320411 }
412+ #[ cfg( feature = "optimize_for_size" ) ]
413+ {
414+ offset = _inner_slow_integer_to_str( self . $conv_fn( ) , & mut buf. buf) ;
415+ }
416+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
417+ unsafe { slice_buffer_to_str( & buf. buf, offset) }
321418 }
322- } ) *
419+ }
420+
421+
422+ ) *
323423
324424 #[ 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) ;
425+ fn _inner_slow_integer_to_str( mut n: $u, buf: & mut [ MaybeUninit :: <u8 >] ) -> usize {
426+ let mut curr = buf. len( ) ;
330427
331428 // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning
332429 // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
333430 // each step this is kept the same as `n` is divided. Since `n` is always
334431 // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]`
335432 // is safe to access.
336- unsafe {
337- loop {
338- curr -= 1 ;
339- buf_ptr. add( curr) . write( ( n % 10 ) as u8 + b'0' ) ;
340- n /= 10 ;
433+ loop {
434+ curr -= 1 ;
435+ buf[ curr] . write( ( n % 10 ) as u8 + b'0' ) ;
436+ n /= 10 ;
341437
342- if n == 0 {
343- break ;
344- }
438+ if n == 0 {
439+ break ;
345440 }
346441 }
442+ curr
443+ }
347444
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- } ;
445+ #[ cfg( feature = "optimize_for_size" ) ]
446+ fn $gen_name( n: $u, is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
447+ const MAX_DEC_N : usize = $u:: MAX . ilog( 10 ) as usize + 1 ;
448+ let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; MAX_DEC_N ] ;
449+
450+ let offset = _inner_slow_integer_to_str( n, & mut buf) ;
451+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
452+ let buf_slice = unsafe { slice_buffer_to_str( & buf, offset) } ;
353453 f. pad_integral( is_nonnegative, "" , buf_slice)
354454 }
355455 } ;
@@ -572,7 +672,8 @@ impl fmt::Display for u128 {
572672 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
573673 let mut buf = [ MaybeUninit :: < u8 > :: uninit ( ) ; U128_MAX_DEC_N ] ;
574674
575- f. pad_integral ( true , "" , self . _fmt ( & mut buf) )
675+ // SAFETY: `buf` is always big enough to contain all the digits.
676+ unsafe { f. pad_integral ( true , "" , self . _fmt ( & mut buf) ) }
576677 }
577678}
578679
@@ -584,7 +685,8 @@ impl fmt::Display for i128 {
584685 let mut buf = [ MaybeUninit :: < u8 > :: uninit ( ) ; U128_MAX_DEC_N ] ;
585686
586687 let is_nonnegative = * self >= 0 ;
587- f. pad_integral ( is_nonnegative, "" , self . unsigned_abs ( ) . _fmt ( & mut buf) )
688+ // SAFETY: `buf` is always big enough to contain all the digits.
689+ unsafe { f. pad_integral ( is_nonnegative, "" , self . unsigned_abs ( ) . _fmt ( & mut buf) ) }
588690 }
589691}
590692
@@ -597,13 +699,21 @@ impl u128 {
597699 reason = "specialized method meant to only be used by `SpecToString` implementation" ,
598700 issue = "none"
599701 ) ]
600- pub fn _fmt < ' a > ( self , buf : & ' a mut [ MaybeUninit < u8 > ] ) -> & ' a str {
702+ pub unsafe 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+ buf[ offset] . write ( b'0' ) ;
715+ return offset;
605716 }
606-
607717 // Take the 16 least-significant decimals.
608718 let ( quot_1e16, mod_1e16) = div_rem_1e16 ( self ) ;
609719 let ( mut remain, mut offset) = if quot_1e16 == 0 {
@@ -677,16 +787,86 @@ impl u128 {
677787 buf[ offset] . write ( DEC_DIGITS_LUT [ last * 2 + 1 ] ) ;
678788 // not used: remain = 0;
679789 }
790+ offset
791+ }
680792
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- ) )
793+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
794+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
795+ ///
796+ /// # Examples
797+ ///
798+ /// ```
799+ /// #![feature(int_format_into)]
800+ /// use core::fmt::NumBuffer;
801+ ///
802+ /// let n = 0u128;
803+ /// let mut buf = NumBuffer::new();
804+ /// assert_eq!(n.format_into(&mut buf), "0");
805+ ///
806+ /// let n1 = 32u128;
807+ /// let mut buf1 = NumBuffer::new();
808+ /// assert_eq!(n1.format_into(&mut buf1), "32");
809+ ///
810+ /// let n2 = u128::MAX;
811+ /// let mut buf2 = NumBuffer::new();
812+ /// assert_eq!(n2.format_into(&mut buf2), u128::MAX.to_string());
813+ /// ```
814+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
815+ pub fn format_into ( self , buf : & mut NumBuffer < Self > ) -> & str {
816+ let diff = buf. capacity ( ) - U128_MAX_DEC_N ;
817+ // FIXME: Once const generics are better, use `NumberBufferTrait::BUF_SIZE` as generic const
818+ // for `fmt_u128_inner`.
819+ //
820+ // In the meantime, we have to use a slice starting at index 1 and add 1 to the returned
821+ // offset to ensure the number is correctly generated at the end of the buffer.
822+ // SAFETY: `diff` will always be between 0 and its initial value.
823+ unsafe { self . _fmt ( buf. buf . get_unchecked_mut ( diff..) ) }
824+ }
825+ }
826+
827+ impl i128 {
828+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
829+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
830+ ///
831+ /// # Examples
832+ ///
833+ /// ```
834+ /// #![feature(int_format_into)]
835+ /// use core::fmt::NumBuffer;
836+ ///
837+ /// let n = 0i128;
838+ /// let mut buf = NumBuffer::new();
839+ /// assert_eq!(n.format_into(&mut buf), "0");
840+ ///
841+ /// let n1 = i128::MIN;
842+ /// assert_eq!(n1.format_into(&mut buf), i128::MIN.to_string());
843+ ///
844+ /// let n2 = i128::MAX;
845+ /// assert_eq!(n2.format_into(&mut buf), i128::MAX.to_string());
846+ /// ```
847+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
848+ pub fn format_into ( self , buf : & mut NumBuffer < Self > ) -> & str {
849+ let diff = buf. capacity ( ) - U128_MAX_DEC_N ;
850+ // FIXME: Once const generics are better, use `NumberBufferTrait::BUF_SIZE` as generic const
851+ // for `fmt_u128_inner`.
852+ //
853+ // In the meantime, we have to use a slice starting at index 1 and add 1 to the returned
854+ // offset to ensure the number is correctly generated at the end of the buffer.
855+ let mut offset =
856+ // SAFETY: `buf` will always be big enough to contain all digits.
857+ unsafe { self . unsigned_abs ( ) . _fmt_inner ( buf. buf . get_unchecked_mut ( diff..) ) } ;
858+ // We put back the offset at the right position.
859+ offset += diff;
860+ // Only difference between signed and unsigned are these 4 lines.
861+ if self < 0 {
862+ offset -= 1 ;
863+ // SAFETY: `buf` will always be big enough to contain all digits plus the minus sign.
864+ unsafe {
865+ buf. buf . get_unchecked_mut ( offset) . write ( b'-' ) ;
866+ }
689867 }
868+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
869+ unsafe { slice_buffer_to_str ( & buf. buf , offset) }
690870 }
691871}
692872
0 commit comments