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
@@ -248,6 +263,13 @@ macro_rules! impl_Display {
248263 issue = "none"
249264 ) ]
250265 pub fn _fmt<' a>( self , buf: & ' a mut [ MaybeUninit :: <u8 >] ) -> & ' a str {
266+ // SAFETY: `buf` will always be big enough to contain all digits.
267+ let offset = unsafe { self . _fmt_inner( buf) } ;
268+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
269+ unsafe { slice_buffer_to_str( buf, offset) }
270+ }
271+
272+ unsafe fn _fmt_inner( self , buf: & mut [ MaybeUninit :: <u8 >] ) -> usize {
251273 // Count the number of bytes in buf that are not initialized.
252274 let mut offset = buf. len( ) ;
253275 // Consume the least-significant decimals from a working copy.
@@ -309,24 +331,99 @@ macro_rules! impl_Display {
309331 // not used: remain = 0;
310332 }
311333
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.
334+ offset
335+ }
336+ }
337+
338+ impl $signed {
339+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
340+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
341+ ///
342+ /// # Examples
343+ ///
344+ /// ```
345+ /// #![feature(int_format_into)]
346+ /// use core::fmt::NumBuffer;
347+ ///
348+ #[ doc = concat!( "let n = 0" , stringify!( $signed) , ";" ) ]
349+ /// let mut buf = NumBuffer::new();
350+ /// assert_eq!(n.format_into(&mut buf), "0");
351+ ///
352+ #[ doc = concat!( "let n1 = 32" , stringify!( $signed) , ";" ) ]
353+ /// assert_eq!(n1.format_into(&mut buf), "32");
354+ ///
355+ #[ doc = concat!( "let n2 = " , stringify!( $signed:: MAX ) , ";" ) ]
356+ #[ doc = concat!( "assert_eq!(n2.format_into(&mut buf), " , stringify!( $signed:: MAX ) , ".to_string());" ) ]
357+ /// ```
358+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
359+ pub fn format_into( self , buf: & mut NumBuffer <Self >) -> & str {
360+ let mut offset;
361+
362+ #[ cfg( not( feature = "optimize_for_size" ) ) ]
363+ // SAFETY: `buf` will always be big enough to contain all digits.
315364 unsafe {
316- str :: from_utf8_unchecked( slice:: from_raw_parts(
317- MaybeUninit :: slice_as_ptr( written) ,
318- written. len( ) ,
319- ) )
365+ offset = self . unsigned_abs( ) . _fmt_inner( & mut buf. buf) ;
366+ }
367+ #[ cfg( feature = "optimize_for_size" ) ]
368+ {
369+ offset = _inner_slow_integer_to_str( self . unsigned_abs( ) . $conv_fn( ) , & mut buf. buf) ;
370+ }
371+ // Only difference between signed and unsigned are these 4 lines.
372+ if self < 0 {
373+ offset -= 1 ;
374+ // SAFETY: `buf` will always be big enough for negative numbers to contain all
375+ // digits plus the minus sign.
376+ unsafe { buf. buf. get_unchecked_mut( offset) . write( b'-' ) ; }
320377 }
378+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
379+ unsafe { slice_buffer_to_str( & buf. buf, offset) }
321380 }
322- } ) *
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.
409+ unsafe {
410+ offset = self . _fmt_inner( & mut buf. buf) ;
411+ }
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) }
418+ }
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
@@ -336,20 +433,26 @@ macro_rules! impl_Display {
336433 unsafe {
337434 loop {
338435 curr -= 1 ;
339- buf_ptr. add( curr) . write( ( n % 10 ) as u8 + b'0' ) ;
436+ // SAFETY: `buf` will always be big enough to contain all digits.
437+ buf. get_unchecked( curr) . write( ( n % 10 ) as u8 + b'0' ) ;
340438 n /= 10 ;
341439
342440 if n == 0 {
343441 break ;
344442 }
345443 }
346444 }
445+ cur
446+ }
347447
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- } ;
448+ #[ cfg( feature = "optimize_for_size" ) ]
449+ fn $gen_name( n: $u, is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
450+ const MAX_DEC_N : usize = $u:: MAX . ilog( 10 ) as usize + 1 ;
451+ let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; MAX_DEC_N ] ;
452+
453+ let offset = _inner_slow_integer_to_str( n, & mut buf) ;
454+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
455+ let buf_slice = unsafe { slice_buffer_to_str( & buf, offset) } ;
353456 f. pad_integral( is_nonnegative, "" , buf_slice)
354457 }
355458 } ;
@@ -598,12 +701,23 @@ impl u128 {
598701 issue = "none"
599702 ) ]
600703 pub fn _fmt < ' a > ( self , buf : & ' a mut [ MaybeUninit < u8 > ] ) -> & ' a str {
704+ // SAFETY: `buf` will always be big enough to contain all digits.
705+ let offset = unsafe { self . _fmt_inner ( buf) } ;
706+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
707+ unsafe { slice_buffer_to_str ( buf, offset) }
708+ }
709+
710+ unsafe fn _fmt_inner ( self , buf : & mut [ MaybeUninit < u8 > ] ) -> usize {
601711 // Optimize common-case zero, which would also need special treatment due to
602712 // its "leading" zero.
603713 if self == 0 {
604- return "0" ;
714+ let offset = buf. len ( ) - 1 ;
715+ // SAFETY: `buf` will always be big enough to contain all digits.
716+ unsafe {
717+ buf. get_unchecked_mut ( offset) . write ( b'0' ) ;
718+ }
719+ return offset;
605720 }
606-
607721 // Take the 16 least-significant decimals.
608722 let ( quot_1e16, mod_1e16) = div_rem_1e16 ( self ) ;
609723 let ( mut remain, mut offset) = if quot_1e16 == 0 {
@@ -677,16 +791,86 @@ impl u128 {
677791 buf[ offset] . write ( DEC_DIGITS_LUT [ last * 2 + 1 ] ) ;
678792 // not used: remain = 0;
679793 }
794+ offset
795+ }
680796
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- ) )
797+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
798+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
799+ ///
800+ /// # Examples
801+ ///
802+ /// ```
803+ /// #![feature(int_format_into)]
804+ /// use core::fmt::NumBuffer;
805+ ///
806+ /// let n = 0u128;
807+ /// let mut buf = NumBuffer::new();
808+ /// assert_eq!(n.format_into(&mut buf), "0");
809+ ///
810+ /// let n1 = 32u128;
811+ /// let mut buf1 = NumBuffer::new();
812+ /// assert_eq!(n1.format_into(&mut buf1), "32");
813+ ///
814+ /// let n2 = u128::MAX;
815+ /// let mut buf2 = NumBuffer::new();
816+ /// assert_eq!(n2.format_into(&mut buf2), u128::MAX.to_string());
817+ /// ```
818+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
819+ pub fn format_into ( self , buf : & mut NumBuffer < Self > ) -> & str {
820+ let diff = buf. capacity ( ) - U128_MAX_DEC_N ;
821+ // FIXME: Once const generics are better, use `NumberBufferTrait::BUF_SIZE` as generic const
822+ // for `fmt_u128_inner`.
823+ //
824+ // In the meantime, we have to use a slice starting at index 1 and add 1 to the returned
825+ // offset to ensure the number is correctly generated at the end of the buffer.
826+ // SAFETY: `diff` will always be between 0 and its initial value.
827+ unsafe { self . _fmt ( buf. buf . get_unchecked_mut ( diff..) ) }
828+ }
829+ }
830+
831+ impl i128 {
832+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
833+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
834+ ///
835+ /// # Examples
836+ ///
837+ /// ```
838+ /// #![feature(int_format_into)]
839+ /// use core::fmt::NumBuffer;
840+ ///
841+ /// let n = 0i128;
842+ /// let mut buf = NumBuffer::new();
843+ /// assert_eq!(n.format_into(&mut buf), "0");
844+ ///
845+ /// let n1 = i128::MIN;
846+ /// assert_eq!(n1.format_into(&mut buf), i128::MIN.to_string());
847+ ///
848+ /// let n2 = i128::MAX;
849+ /// assert_eq!(n2.format_into(&mut buf), i128::MAX.to_string());
850+ /// ```
851+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
852+ pub fn format_into ( self , buf : & mut NumBuffer < Self > ) -> & str {
853+ let diff = buf. capacity ( ) - U128_MAX_DEC_N ;
854+ // FIXME: Once const generics are better, use `NumberBufferTrait::BUF_SIZE` as generic const
855+ // for `fmt_u128_inner`.
856+ //
857+ // In the meantime, we have to use a slice starting at index 1 and add 1 to the returned
858+ // offset to ensure the number is correctly generated at the end of the buffer.
859+ let mut offset =
860+ // SAFETY: `buf` will always be big enough to contain all digits.
861+ unsafe { self . unsigned_abs ( ) . _fmt_inner ( buf. buf . get_unchecked_mut ( diff..) ) } ;
862+ // We put back the offset at the right position.
863+ offset += diff;
864+ // Only difference between signed and unsigned are these 4 lines.
865+ if self < 0 {
866+ offset -= 1 ;
867+ // SAFETY: `buf` will always be big enough to contain all digits plus the minus sign.
868+ unsafe {
869+ buf. buf . get_unchecked_mut ( offset) . write ( b'-' ) ;
870+ }
689871 }
872+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
873+ unsafe { slice_buffer_to_str ( & buf. buf , offset) }
690874 }
691875}
692876
0 commit comments