@@ -474,20 +474,22 @@ mod imp {
474
474
impl_Exp ! ( i8 , u8 , i16 , u16 , i32 , u32 , isize , usize as u32 via to_u32 named exp_u32) ;
475
475
impl_Exp ! ( i64 , u64 as u64 via to_u64 named exp_u64) ;
476
476
}
477
-
478
- // impl_Display!(i128, u128 as u128 via to_u128 named fmt_u128);
479
477
impl_Exp ! ( i128 , u128 as u128 via to_u128 named exp_u128) ;
480
478
481
- fn fmt_u64_2 < const N : usize > ( mut rem : u64 , buf : & mut [ MaybeUninit < u8 > ; N ] , curr : & mut isize ) {
479
+ /// Helper function for writing a u64 into `buf` going from last to first, with `curr`.
480
+ fn parse_u64_into < const N : usize > ( mut n : u64 , buf : & mut [ MaybeUninit < u8 > ; N ] , curr : & mut isize ) {
482
481
let buf_ptr = MaybeUninit :: first_ptr_mut ( buf) ;
483
482
let lut_ptr = DEC_DIGITS_LUT . as_ptr ( ) ;
483
+ assert ! ( * curr > 19 ) ;
484
484
485
485
// SAFETY:
486
- // FIXME(fill this in after reviews)
486
+ // Writes at most 19 characters into the buffer. Guaranteed that any ptr into LUT is at most
487
+ // 198, so will never OOB. There is a check above that there are at least 19 characters
488
+ // remaining.
487
489
unsafe {
488
- if rem >= 1e16 as u64 {
489
- let to_parse = rem % 1e16 as u64 ;
490
- rem /= 1e16 as u64 ;
490
+ if n >= 1e16 as u64 {
491
+ let to_parse = n % 1e16 as u64 ;
492
+ n /= 1e16 as u64 ;
491
493
492
494
// Some of these are nops but it looks more elegant this way.
493
495
let d1 = ( ( to_parse / 1e14 as u64 ) % 100 ) << 1 ;
@@ -510,9 +512,9 @@ fn fmt_u64_2<const N: usize>(mut rem: u64, buf: &mut [MaybeUninit<u8>; N], curr:
510
512
ptr:: copy_nonoverlapping ( lut_ptr. offset ( d7 as isize ) , buf_ptr. offset ( * curr + 12 ) , 2 ) ;
511
513
ptr:: copy_nonoverlapping ( lut_ptr. offset ( d8 as isize ) , buf_ptr. offset ( * curr + 14 ) , 2 ) ;
512
514
}
513
- if rem >= 1e8 as u64 {
514
- let to_parse = rem % 1e8 as u64 ;
515
- rem /= 1e8 as u64 ;
515
+ if n >= 1e8 as u64 {
516
+ let to_parse = n % 1e8 as u64 ;
517
+ n /= 1e8 as u64 ;
516
518
517
519
// Some of these are nops but it looks more elegant this way.
518
520
let d1 = ( ( to_parse / 1e6 as u64 ) % 100 ) << 1 ;
@@ -526,11 +528,11 @@ fn fmt_u64_2<const N: usize>(mut rem: u64, buf: &mut [MaybeUninit<u8>; N], curr:
526
528
ptr:: copy_nonoverlapping ( lut_ptr. offset ( d3 as isize ) , buf_ptr. offset ( * curr + 4 ) , 2 ) ;
527
529
ptr:: copy_nonoverlapping ( lut_ptr. offset ( d4 as isize ) , buf_ptr. offset ( * curr + 6 ) , 2 ) ;
528
530
}
529
- // `rem ` < 1e8 < (1 << 32)
530
- let mut rem = rem as u32 ;
531
- if rem >= 1e4 as u32 {
532
- let to_parse = rem % 1e4 as u32 ;
533
- rem /= 1e4 as u32 ;
531
+ // `n ` < 1e8 < (1 << 32)
532
+ let mut n = n as u32 ;
533
+ if n >= 1e4 as u32 {
534
+ let to_parse = n % 1e4 as u32 ;
535
+ n /= 1e4 as u32 ;
534
536
535
537
let d1 = ( to_parse / 100 ) << 1 ;
536
538
let d2 = ( to_parse % 100 ) << 1 ;
@@ -540,21 +542,21 @@ fn fmt_u64_2<const N: usize>(mut rem: u64, buf: &mut [MaybeUninit<u8>; N], curr:
540
542
ptr:: copy_nonoverlapping ( lut_ptr. offset ( d2 as isize ) , buf_ptr. offset ( * curr + 2 ) , 2 ) ;
541
543
}
542
544
543
- // `rem ` < 1e4 < (1 << 16)
544
- let mut rem = rem as u16 ;
545
- if rem >= 100 {
546
- let d1 = ( rem % 100 ) << 1 ;
547
- rem /= 100 ;
545
+ // `n ` < 1e4 < (1 << 16)
546
+ let mut n = n as u16 ;
547
+ if n >= 100 {
548
+ let d1 = ( n % 100 ) << 1 ;
549
+ n /= 100 ;
548
550
* curr -= 2 ;
549
551
ptr:: copy_nonoverlapping ( lut_ptr. offset ( d1 as isize ) , buf_ptr. offset ( * curr) , 2 ) ;
550
552
}
551
553
552
554
// decode last 1 or 2 chars
553
- if rem < 10 {
555
+ if n < 10 {
554
556
* curr -= 1 ;
555
- * buf_ptr. offset ( * curr) = ( rem as u8 ) + b'0' ;
557
+ * buf_ptr. offset ( * curr) = ( n as u8 ) + b'0' ;
556
558
} else {
557
- let d1 = rem << 1 ;
559
+ let d1 = n << 1 ;
558
560
* curr -= 2 ;
559
561
ptr:: copy_nonoverlapping ( lut_ptr. offset ( d1 as isize ) , buf_ptr. offset ( * curr) , 2 ) ;
560
562
}
@@ -564,7 +566,7 @@ fn fmt_u64_2<const N: usize>(mut rem: u64, buf: &mut [MaybeUninit<u8>; N], curr:
564
566
#[ stable( feature = "rust1" , since = "1.0.0" ) ]
565
567
impl fmt:: Display for u128 {
566
568
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
567
- fmt_u128_2 ( * self , true , f)
569
+ fmt_u128 ( * self , true , f)
568
570
}
569
571
}
570
572
@@ -578,38 +580,41 @@ impl fmt::Display for i128 {
578
580
// convert the negative num to positive by summing 1 to it's 2 complement
579
581
( !self . to_u128 ( ) ) . wrapping_add ( 1 )
580
582
} ;
581
- fmt_u128_2 ( n, is_nonnegative, f)
583
+ fmt_u128 ( n, is_nonnegative, f)
582
584
}
583
585
}
584
586
585
- #[ allow( dead_code) ]
586
- fn fmt_u128_2 ( n : u128 , is_nonnegative : bool , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
587
+ /// Specialized optimization for u128. Instead of taking two items at a time, it splits
588
+ /// into at most 2 u64s, and then chunks by 10e16, 10e8, 10e4, 10e2, and then 10e1.
589
+ /// It also has to handle 1 last item, as 10^40 > 2^128 > 10^39, whereas
590
+ /// 10^20 > 2^64 > 10^19.
591
+ fn fmt_u128 ( n : u128 , is_nonnegative : bool , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
587
592
// 2^128 is about 3*10^38, so 39 gives an extra byte of space
588
593
let mut buf = [ MaybeUninit :: < u8 > :: uninit ( ) ; 39 ] ;
589
594
let mut curr = buf. len ( ) as isize ;
590
595
let buf_ptr = MaybeUninit :: first_ptr_mut ( & mut buf) ;
591
596
592
- // SAFETY: Since `d1` and `d2` are always less than or equal to `198`, we
593
- // can copy from `lut_ptr[d1..d1 + 1]` and `lut_ptr[d2..d2 + 1]`. To show
594
- // that it's OK to copy into `buf_ptr`, notice that at the beginning
595
- // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
596
- // each step this is kept the same as `n` is divided. Since `n` is always
597
- // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]`
598
- // is safe to access.
599
- unsafe {
600
- let ( n, rem) = udiv_1e9 ( n) ;
601
- fmt_u64_2 ( rem, & mut buf, & mut curr) ;
597
+ let ( n, rem) = udiv_1e19 ( n) ;
598
+ parse_u64_into ( rem, & mut buf, & mut curr) ;
602
599
603
- if n != 0 {
604
- // 0 pad up to point
605
- let target = ( buf. len ( ) - 19 ) as isize ;
600
+ if n != 0 {
601
+ // 0 pad up to point
602
+ let target = ( buf. len ( ) - 19 ) as isize ;
603
+ // SAFETY: Guaranteed that we wrote at most 19 bytes, and there must be space
604
+ // remaining since it has length 39
605
+ unsafe {
606
606
ptr:: write_bytes ( buf_ptr. offset ( target) , b'0' , ( curr - target) as usize ) ;
607
- curr = target;
608
- let ( n, rem) = udiv_1e9 ( n) ;
609
- fmt_u64_2 ( rem, & mut buf, & mut curr) ;
610
- // Should this following branch be annotated with unlikely?
611
- if n != 0 {
612
- let target = ( buf. len ( ) - 38 ) as isize ;
607
+ }
608
+ curr = target;
609
+
610
+ let ( n, rem) = udiv_1e19 ( n) ;
611
+ parse_u64_into ( rem, & mut buf, & mut curr) ;
612
+ // Should this following branch be annotated with unlikely?
613
+ if n != 0 {
614
+ let target = ( buf. len ( ) - 38 ) as isize ;
615
+ // SAFETY: At this point we wrote at most 38 bytes, pad up to that point,
616
+ // There can only be at most 1 digit remaining.
617
+ unsafe {
613
618
ptr:: write_bytes ( buf_ptr. offset ( target) , b'0' , ( curr - target) as usize ) ;
614
619
curr = target - 1 ;
615
620
* buf_ptr. offset ( curr) = ( n as u8 ) + b'0' ;
@@ -628,7 +633,8 @@ fn fmt_u128_2(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt:
628
633
f. pad_integral ( is_nonnegative, "" , buf_slice)
629
634
}
630
635
631
- fn udiv_1e9 ( n : u128 ) -> ( u128 , u64 ) {
636
+ /// Partition of `n` into n > 1e19 and rem <= 1e19
637
+ fn udiv_1e19 ( n : u128 ) -> ( u128 , u64 ) {
632
638
const DIV : u64 = 1e19 as u64 ;
633
639
let high = ( n >> 64 ) as u64 ;
634
640
if high == 0 {
0 commit comments