11//! Integer and floating-point number formatting
22
3+ use crate :: ascii;
34use crate :: fmt;
45use crate :: mem:: MaybeUninit ;
56use crate :: num:: fmt as numfmt;
@@ -200,17 +201,22 @@ debug! {
200201}
201202
202203// 2 digit decimal look up table
203- static DEC_DIGITS_LUT : & [ u8 ; 200 ] = b"0001020304050607080910111213141516171819\
204- 2021222324252627282930313233343536373839\
205- 4041424344454647484950515253545556575859\
206- 6061626364656667686970717273747576777879\
207- 8081828384858687888990919293949596979899";
204+ // FIXME: use `.as_ascii().unwrap()` once `slice::is_ascii` is `const fn`
205+ // SAFETY: They're all just digits, and CTFE will double-check validity too.
206+ static DEC_DIGITS_LUT : [ ascii:: Char ; 200 ] = unsafe {
207+ * b"0001020304050607080910111213141516171819\
208+ 2021222324252627282930313233343536373839\
209+ 4041424344454647484950515253545556575859\
210+ 6061626364656667686970717273747576777879\
211+ 8081828384858687888990919293949596979899"
212+ . as_ascii_unchecked ( )
213+ } ;
208214
209215macro_rules! impl_Display {
210216 ( $( $t: ident) ,* as $u: ident via $conv_fn: ident named $name: ident) => {
211217 fn $name( mut n: $u, is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
212218 // 2^128 is about 3*10^38, so 39 gives an extra byte of space
213- let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; 39 ] ;
219+ let mut buf = [ MaybeUninit :: <ascii :: Char >:: uninit( ) ; 39 ] ;
214220 let mut curr = buf. len( ) ;
215221 let buf_ptr = MaybeUninit :: slice_as_mut_ptr( & mut buf) ;
216222 let lut_ptr = DEC_DIGITS_LUT . as_ptr( ) ;
@@ -254,23 +260,21 @@ macro_rules! impl_Display {
254260 }
255261
256262 // decode last 1 or 2 chars
257- if n < 10 {
263+ if let Some ( d ) = ascii :: Char :: digit ( n as u8 ) {
258264 curr -= 1 ;
259- * buf_ptr. add( curr) = ( n as u8 ) + b'0' ;
265+ * buf_ptr. add( curr) = d ;
260266 } else {
261267 let d1 = n << 1 ;
262268 curr -= 2 ;
263269 ptr:: copy_nonoverlapping( lut_ptr. add( d1) , buf_ptr. add( curr) , 2 ) ;
264270 }
265271 }
266272
267- // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid
268- // UTF-8 since `DEC_DIGITS_LUT` is
269- let buf_slice = unsafe {
270- str :: from_utf8_unchecked(
271- slice:: from_raw_parts( buf_ptr. add( curr) , buf. len( ) - curr) )
273+ // SAFETY: `curr` > 0 (since we made `buf` large enough)
274+ let buf_slice: & [ ascii:: Char ] = unsafe {
275+ slice:: from_raw_parts( buf_ptr. add( curr) , buf. len( ) - curr)
272276 } ;
273- f. pad_integral( is_nonnegative, "" , buf_slice)
277+ f. pad_integral( is_nonnegative, "" , buf_slice. as_str ( ) )
274278 }
275279
276280 $( #[ stable( feature = "rust1" , since = "1.0.0" ) ]
@@ -299,7 +303,7 @@ macro_rules! impl_Exp {
299303 f: & mut fmt:: Formatter <' _>
300304 ) -> fmt:: Result {
301305 let ( mut n, mut exponent, trailing_zeros, added_precision) = {
302- let mut exponent = 0 ;
306+ let mut exponent: usize = 0 ;
303307 // count and remove trailing decimal zeroes
304308 while n % 10 == 0 && n >= 10 {
305309 n /= 10 ;
@@ -338,7 +342,7 @@ macro_rules! impl_Exp {
338342 // 39 digits (worst case u128) + . = 40
339343 // Since `curr` always decreases by the number of digits copied, this means
340344 // that `curr >= 0`.
341- let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; 40 ] ;
345+ let mut buf = [ MaybeUninit :: <ascii :: Char >:: uninit( ) ; 40 ] ;
342346 let mut curr = buf. len( ) ; //index for buf
343347 let buf_ptr = MaybeUninit :: slice_as_mut_ptr( & mut buf) ;
344348 let lut_ptr = DEC_DIGITS_LUT . as_ptr( ) ;
@@ -362,7 +366,7 @@ macro_rules! impl_Exp {
362366 curr -= 1 ;
363367 // SAFETY: Safe since `40 > curr >= 0` (see comment)
364368 unsafe {
365- * buf_ptr. add( curr) = ( n as u8 % 10_u8 ) + b'0' ;
369+ * buf_ptr. add( curr) = ascii :: Char :: digit ( n as u8 % 10_u8 ) . unwrap ( )
366370 }
367371 n /= 10 ;
368372 exponent += 1 ;
@@ -372,29 +376,31 @@ macro_rules! impl_Exp {
372376 curr -= 1 ;
373377 // SAFETY: Safe since `40 > curr >= 0`
374378 unsafe {
375- * buf_ptr. add( curr) = b'.' ;
379+ * buf_ptr. add( curr) = ascii :: Char :: FullStop ;
376380 }
377381 }
378382
379- // SAFETY: Safe since `40 > curr >= 0`
383+ // SAFETY: Safe since `40 > curr >= 0`.
384+ // n is <= 9, because after the loop it's <= 99, then it was
385+ // /= 10 again if it wasn't already below 10.
380386 let buf_slice = unsafe {
381387 // decode last character
382388 curr -= 1 ;
383- * buf_ptr. add( curr) = ( n as u8 ) + b'0' ;
389+ * buf_ptr. add( curr) = ascii :: Char :: digit_unchecked ( n as u8 ) ;
384390
385391 let len = buf. len( ) - curr as usize ;
386392 slice:: from_raw_parts( buf_ptr. add( curr) , len)
387393 } ;
388394
389395 // stores 'e' (or 'E') and the up to 2-digit exponent
390- let mut exp_buf = [ MaybeUninit :: <u8 >:: uninit( ) ; 3 ] ;
396+ let mut exp_buf = [ MaybeUninit :: <ascii :: Char >:: uninit( ) ; 3 ] ;
391397 let exp_ptr = MaybeUninit :: slice_as_mut_ptr( & mut exp_buf) ;
392398 // SAFETY: In either case, `exp_buf` is written within bounds and `exp_ptr[..len]`
393399 // is contained within `exp_buf` since `len <= 3`.
394400 let exp_slice = unsafe {
395- * exp_ptr. add( 0 ) = if upper { b'E' } else { b'e' } ;
396- let len = if exponent < 10 {
397- * exp_ptr. add( 1 ) = ( exponent as u8 ) + b'0' ;
401+ * exp_ptr. add( 0 ) = if upper { ascii :: Char :: CapitalE } else { ascii :: Char :: SmallE } ;
402+ let len = if let Some ( digit ) = ascii :: Char :: digit ( exponent as u8 ) {
403+ * exp_ptr. add( 1 ) = digit ;
398404 2
399405 } else {
400406 let off = exponent << 1 ;
@@ -405,9 +411,9 @@ macro_rules! impl_Exp {
405411 } ;
406412
407413 let parts = & [
408- numfmt:: Part :: Copy ( buf_slice) ,
414+ numfmt:: Part :: Copy ( buf_slice. as_bytes ( ) ) ,
409415 numfmt:: Part :: Zero ( added_precision) ,
410- numfmt:: Part :: Copy ( exp_slice)
416+ numfmt:: Part :: Copy ( exp_slice. as_bytes ( ) )
411417 ] ;
412418 let sign = if !is_nonnegative {
413419 "-"
@@ -479,7 +485,11 @@ mod imp {
479485impl_Exp ! ( i128 , u128 as u128 via to_u128 named exp_u128) ;
480486
481487/// Helper function for writing a u64 into `buf` going from last to first, with `curr`.
482- fn parse_u64_into < const N : usize > ( mut n : u64 , buf : & mut [ MaybeUninit < u8 > ; N ] , curr : & mut usize ) {
488+ fn parse_u64_into < const N : usize > (
489+ mut n : u64 ,
490+ buf : & mut [ MaybeUninit < ascii:: Char > ; N ] ,
491+ curr : & mut usize ,
492+ ) {
483493 let buf_ptr = MaybeUninit :: slice_as_mut_ptr ( buf) ;
484494 let lut_ptr = DEC_DIGITS_LUT . as_ptr ( ) ;
485495 assert ! ( * curr > 19 ) ;
@@ -553,10 +563,13 @@ fn parse_u64_into<const N: usize>(mut n: u64, buf: &mut [MaybeUninit<u8>; N], cu
553563 ptr:: copy_nonoverlapping ( lut_ptr. add ( d1 as usize ) , buf_ptr. add ( * curr) , 2 ) ;
554564 }
555565
566+ // `n` < 1e2 < (1 << 8)
567+ let n = n as u8 ;
568+
556569 // decode last 1 or 2 chars
557- if n < 10 {
570+ if let Some ( d ) = ascii :: Char :: digit ( n ) {
558571 * curr -= 1 ;
559- * buf_ptr. add ( * curr) = ( n as u8 ) + b'0' ;
572+ * buf_ptr. add ( * curr) = d ;
560573 } else {
561574 let d1 = n << 1 ;
562575 * curr -= 2 ;
@@ -592,7 +605,7 @@ impl fmt::Display for i128 {
592605/// 10^20 > 2^64 > 10^19.
593606fn fmt_u128 ( n : u128 , is_nonnegative : bool , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
594607 // 2^128 is about 3*10^38, so 39 gives an extra byte of space
595- let mut buf = [ MaybeUninit :: < u8 > :: uninit ( ) ; 39 ] ;
608+ let mut buf = [ MaybeUninit :: < ascii :: Char > :: uninit ( ) ; 39 ] ;
596609 let mut curr = buf. len ( ) ;
597610
598611 let ( n, rem) = udiv_1e19 ( n) ;
@@ -621,24 +634,20 @@ fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::R
621634 // buf `buf` is not used in this scope so we are good.
622635 let buf_ptr = MaybeUninit :: slice_as_mut_ptr ( & mut buf) ;
623636 // SAFETY: At this point we wrote at most 38 bytes, pad up to that point,
624- // There can only be at most 1 digit remaining.
637+ // There can only be at most 1 digit remaining. (2¹²⁸ ÷ 10³⁸ ≈ 3.4)
625638 unsafe {
626639 ptr:: write_bytes ( buf_ptr. add ( target) , b'0' , curr - target) ;
627640 curr = target - 1 ;
628- * buf_ptr. add ( curr) = ( n as u8 ) + b'0' ;
641+ * buf_ptr. add ( curr) = ascii :: Char :: digit_unchecked ( n as u8 ) ;
629642 }
630643 }
631644 }
632645
633- // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid
634- // UTF-8 since `DEC_DIGITS_LUT` is
635- let buf_slice = unsafe {
636- str:: from_utf8_unchecked ( slice:: from_raw_parts (
637- MaybeUninit :: slice_as_mut_ptr ( & mut buf) . add ( curr) ,
638- buf. len ( ) - curr,
639- ) )
646+ // SAFETY: `curr` > 0 (since we made `buf` large enough)
647+ let buf_slice: & [ ascii:: Char ] = unsafe {
648+ slice:: from_raw_parts ( MaybeUninit :: slice_as_mut_ptr ( & mut buf) . add ( curr) , buf. len ( ) - curr)
640649 } ;
641- f. pad_integral ( is_nonnegative, "" , buf_slice)
650+ f. pad_integral ( is_nonnegative, "" , buf_slice. as_str ( ) )
642651}
643652
644653/// Partition of `n` into n > 1e19 and rem <= 1e19
0 commit comments