1
1
//! Integer and floating-point number formatting
2
2
3
+ use crate :: ascii;
3
4
use crate :: fmt;
4
5
use crate :: mem:: MaybeUninit ;
5
6
use crate :: num:: fmt as numfmt;
@@ -200,17 +201,22 @@ debug! {
200
201
}
201
202
202
203
// 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
+ } ;
208
214
209
215
macro_rules! impl_Display {
210
216
( $( $t: ident) ,* as $u: ident via $conv_fn: ident named $name: ident) => {
211
217
fn $name( mut n: $u, is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
212
218
// 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 ] ;
214
220
let mut curr = buf. len( ) ;
215
221
let buf_ptr = MaybeUninit :: slice_as_mut_ptr( & mut buf) ;
216
222
let lut_ptr = DEC_DIGITS_LUT . as_ptr( ) ;
@@ -254,23 +260,21 @@ macro_rules! impl_Display {
254
260
}
255
261
256
262
// decode last 1 or 2 chars
257
- if n < 10 {
263
+ if let Some ( d ) = ascii :: Char :: digit ( n as u8 ) {
258
264
curr -= 1 ;
259
- * buf_ptr. add( curr) = ( n as u8 ) + b'0' ;
265
+ * buf_ptr. add( curr) = d ;
260
266
} else {
261
267
let d1 = n << 1 ;
262
268
curr -= 2 ;
263
269
ptr:: copy_nonoverlapping( lut_ptr. add( d1) , buf_ptr. add( curr) , 2 ) ;
264
270
}
265
271
}
266
272
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)
272
276
} ;
273
- f. pad_integral( is_nonnegative, "" , buf_slice)
277
+ f. pad_integral( is_nonnegative, "" , buf_slice. as_str ( ) )
274
278
}
275
279
276
280
$( #[ stable( feature = "rust1" , since = "1.0.0" ) ]
@@ -299,7 +303,7 @@ macro_rules! impl_Exp {
299
303
f: & mut fmt:: Formatter <' _>
300
304
) -> fmt:: Result {
301
305
let ( mut n, mut exponent, trailing_zeros, added_precision) = {
302
- let mut exponent = 0 ;
306
+ let mut exponent: usize = 0 ;
303
307
// count and remove trailing decimal zeroes
304
308
while n % 10 == 0 && n >= 10 {
305
309
n /= 10 ;
@@ -338,7 +342,7 @@ macro_rules! impl_Exp {
338
342
// 39 digits (worst case u128) + . = 40
339
343
// Since `curr` always decreases by the number of digits copied, this means
340
344
// that `curr >= 0`.
341
- let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; 40 ] ;
345
+ let mut buf = [ MaybeUninit :: <ascii :: Char >:: uninit( ) ; 40 ] ;
342
346
let mut curr = buf. len( ) ; //index for buf
343
347
let buf_ptr = MaybeUninit :: slice_as_mut_ptr( & mut buf) ;
344
348
let lut_ptr = DEC_DIGITS_LUT . as_ptr( ) ;
@@ -362,7 +366,7 @@ macro_rules! impl_Exp {
362
366
curr -= 1 ;
363
367
// SAFETY: Safe since `40 > curr >= 0` (see comment)
364
368
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 ( )
366
370
}
367
371
n /= 10 ;
368
372
exponent += 1 ;
@@ -372,29 +376,31 @@ macro_rules! impl_Exp {
372
376
curr -= 1 ;
373
377
// SAFETY: Safe since `40 > curr >= 0`
374
378
unsafe {
375
- * buf_ptr. add( curr) = b'.' ;
379
+ * buf_ptr. add( curr) = ascii :: Char :: FullStop ;
376
380
}
377
381
}
378
382
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.
380
386
let buf_slice = unsafe {
381
387
// decode last character
382
388
curr -= 1 ;
383
- * buf_ptr. add( curr) = ( n as u8 ) + b'0' ;
389
+ * buf_ptr. add( curr) = ascii :: Char :: digit_unchecked ( n as u8 ) ;
384
390
385
391
let len = buf. len( ) - curr as usize ;
386
392
slice:: from_raw_parts( buf_ptr. add( curr) , len)
387
393
} ;
388
394
389
395
// 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 ] ;
391
397
let exp_ptr = MaybeUninit :: slice_as_mut_ptr( & mut exp_buf) ;
392
398
// SAFETY: In either case, `exp_buf` is written within bounds and `exp_ptr[..len]`
393
399
// is contained within `exp_buf` since `len <= 3`.
394
400
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 ;
398
404
2
399
405
} else {
400
406
let off = exponent << 1 ;
@@ -405,9 +411,9 @@ macro_rules! impl_Exp {
405
411
} ;
406
412
407
413
let parts = & [
408
- numfmt:: Part :: Copy ( buf_slice) ,
414
+ numfmt:: Part :: Copy ( buf_slice. as_bytes ( ) ) ,
409
415
numfmt:: Part :: Zero ( added_precision) ,
410
- numfmt:: Part :: Copy ( exp_slice)
416
+ numfmt:: Part :: Copy ( exp_slice. as_bytes ( ) )
411
417
] ;
412
418
let sign = if !is_nonnegative {
413
419
"-"
@@ -479,7 +485,11 @@ mod imp {
479
485
impl_Exp ! ( i128 , u128 as u128 via to_u128 named exp_u128) ;
480
486
481
487
/// 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
+ ) {
483
493
let buf_ptr = MaybeUninit :: slice_as_mut_ptr ( buf) ;
484
494
let lut_ptr = DEC_DIGITS_LUT . as_ptr ( ) ;
485
495
assert ! ( * curr > 19 ) ;
@@ -553,10 +563,13 @@ fn parse_u64_into<const N: usize>(mut n: u64, buf: &mut [MaybeUninit<u8>; N], cu
553
563
ptr:: copy_nonoverlapping ( lut_ptr. add ( d1 as usize ) , buf_ptr. add ( * curr) , 2 ) ;
554
564
}
555
565
566
+ // `n` < 1e2 < (1 << 8)
567
+ let n = n as u8 ;
568
+
556
569
// decode last 1 or 2 chars
557
- if n < 10 {
570
+ if let Some ( d ) = ascii :: Char :: digit ( n ) {
558
571
* curr -= 1 ;
559
- * buf_ptr. add ( * curr) = ( n as u8 ) + b'0' ;
572
+ * buf_ptr. add ( * curr) = d ;
560
573
} else {
561
574
let d1 = n << 1 ;
562
575
* curr -= 2 ;
@@ -592,7 +605,7 @@ impl fmt::Display for i128 {
592
605
/// 10^20 > 2^64 > 10^19.
593
606
fn fmt_u128 ( n : u128 , is_nonnegative : bool , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
594
607
// 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 ] ;
596
609
let mut curr = buf. len ( ) ;
597
610
598
611
let ( n, rem) = udiv_1e19 ( n) ;
@@ -621,24 +634,20 @@ fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::R
621
634
// buf `buf` is not used in this scope so we are good.
622
635
let buf_ptr = MaybeUninit :: slice_as_mut_ptr ( & mut buf) ;
623
636
// 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)
625
638
unsafe {
626
639
ptr:: write_bytes ( buf_ptr. add ( target) , b'0' , curr - target) ;
627
640
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 ) ;
629
642
}
630
643
}
631
644
}
632
645
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)
640
649
} ;
641
- f. pad_integral ( is_nonnegative, "" , buf_slice)
650
+ f. pad_integral ( is_nonnegative, "" , buf_slice. as_str ( ) )
642
651
}
643
652
644
653
/// Partition of `n` into n > 1e19 and rem <= 1e19
0 commit comments