1
1
//! Integer and floating-point number formatting
2
2
3
+ use crate :: fmt:: NumBuffer ;
3
4
use crate :: mem:: MaybeUninit ;
4
5
use crate :: num:: fmt as numfmt;
5
6
use crate :: ops:: { Div , Rem , Sub } ;
@@ -199,6 +200,20 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"\
199
200
6061626364656667686970717273747576777879\
200
201
8081828384858687888990919293949596979899";
201
202
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
+
202
217
macro_rules! impl_Display {
203
218
( $( $signed: ident, $unsigned: ident, ) * ; as $u: ident via $conv_fn: ident named $gen_name: ident) => {
204
219
@@ -212,7 +227,8 @@ macro_rules! impl_Display {
212
227
// Buffer decimals for $unsigned with right alignment.
213
228
let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; MAX_DEC_N ] ;
214
229
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) ) }
216
232
}
217
233
#[ cfg( feature = "optimize_for_size" ) ]
218
234
{
@@ -230,7 +246,8 @@ macro_rules! impl_Display {
230
246
// Buffer decimals for $unsigned with right alignment.
231
247
let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; MAX_DEC_N ] ;
232
248
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) ) }
234
251
}
235
252
#[ cfg( feature = "optimize_for_size" ) ]
236
253
{
@@ -247,7 +264,14 @@ macro_rules! impl_Display {
247
264
reason = "specialized method meant to only be used by `SpecToString` implementation" ,
248
265
issue = "none"
249
266
) ]
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 {
251
275
// Count the number of bytes in buf that are not initialized.
252
276
let mut offset = buf. len( ) ;
253
277
// Consume the least-significant decimals from a working copy.
@@ -309,47 +333,123 @@ macro_rules! impl_Display {
309
333
// not used: remain = 0;
310
334
}
311
335
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.
315
409
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) ;
320
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) }
321
418
}
322
- } ) *
419
+ }
420
+
421
+
422
+ ) *
323
423
324
424
#[ 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( ) ;
330
427
331
428
// SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning
332
429
// `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
333
430
// each step this is kept the same as `n` is divided. Since `n` is always
334
431
// non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]`
335
432
// 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 ;
341
437
342
- if n == 0 {
343
- break ;
344
- }
438
+ if n == 0 {
439
+ break ;
345
440
}
346
441
}
442
+ curr
443
+ }
347
444
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) } ;
353
453
f. pad_integral( is_nonnegative, "" , buf_slice)
354
454
}
355
455
} ;
@@ -572,7 +672,8 @@ impl fmt::Display for u128 {
572
672
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
573
673
let mut buf = [ MaybeUninit :: < u8 > :: uninit ( ) ; U128_MAX_DEC_N ] ;
574
674
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) ) }
576
677
}
577
678
}
578
679
@@ -584,7 +685,8 @@ impl fmt::Display for i128 {
584
685
let mut buf = [ MaybeUninit :: < u8 > :: uninit ( ) ; U128_MAX_DEC_N ] ;
585
686
586
687
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) ) }
588
690
}
589
691
}
590
692
@@ -597,13 +699,21 @@ impl u128 {
597
699
reason = "specialized method meant to only be used by `SpecToString` implementation" ,
598
700
issue = "none"
599
701
) ]
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 {
601
710
// Optimize common-case zero, which would also need special treatment due to
602
711
// its "leading" zero.
603
712
if self == 0 {
604
- return "0" ;
713
+ let offset = buf. len ( ) - 1 ;
714
+ buf[ offset] . write ( b'0' ) ;
715
+ return offset;
605
716
}
606
-
607
717
// Take the 16 least-significant decimals.
608
718
let ( quot_1e16, mod_1e16) = div_rem_1e16 ( self ) ;
609
719
let ( mut remain, mut offset) = if quot_1e16 == 0 {
@@ -677,16 +787,86 @@ impl u128 {
677
787
buf[ offset] . write ( DEC_DIGITS_LUT [ last * 2 + 1 ] ) ;
678
788
// not used: remain = 0;
679
789
}
790
+ offset
791
+ }
680
792
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
+ }
689
867
}
868
+ // SAFETY: Starting from `offset`, all elements of the slice have been set.
869
+ unsafe { slice_buffer_to_str ( & buf. buf , offset) }
690
870
}
691
871
}
692
872
0 commit comments