@@ -474,20 +474,22 @@ mod imp {
474474 impl_Exp ! ( i8 , u8 , i16 , u16 , i32 , u32 , isize , usize as u32 via to_u32 named exp_u32) ;
475475 impl_Exp ! ( i64 , u64 as u64 via to_u64 named exp_u64) ;
476476}
477-
478- // impl_Display!(i128, u128 as u128 via to_u128 named fmt_u128);
479477impl_Exp ! ( i128 , u128 as u128 via to_u128 named exp_u128) ;
480478
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 ) {
482481 let buf_ptr = MaybeUninit :: first_ptr_mut ( buf) ;
483482 let lut_ptr = DEC_DIGITS_LUT . as_ptr ( ) ;
483+ assert ! ( * curr > 19 ) ;
484484
485485 // 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.
487489 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 ;
491493
492494 // Some of these are nops but it looks more elegant this way.
493495 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:
510512 ptr:: copy_nonoverlapping ( lut_ptr. offset ( d7 as isize ) , buf_ptr. offset ( * curr + 12 ) , 2 ) ;
511513 ptr:: copy_nonoverlapping ( lut_ptr. offset ( d8 as isize ) , buf_ptr. offset ( * curr + 14 ) , 2 ) ;
512514 }
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 ;
516518
517519 // Some of these are nops but it looks more elegant this way.
518520 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:
526528 ptr:: copy_nonoverlapping ( lut_ptr. offset ( d3 as isize ) , buf_ptr. offset ( * curr + 4 ) , 2 ) ;
527529 ptr:: copy_nonoverlapping ( lut_ptr. offset ( d4 as isize ) , buf_ptr. offset ( * curr + 6 ) , 2 ) ;
528530 }
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 ;
534536
535537 let d1 = ( to_parse / 100 ) << 1 ;
536538 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:
540542 ptr:: copy_nonoverlapping ( lut_ptr. offset ( d2 as isize ) , buf_ptr. offset ( * curr + 2 ) , 2 ) ;
541543 }
542544
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 ;
548550 * curr -= 2 ;
549551 ptr:: copy_nonoverlapping ( lut_ptr. offset ( d1 as isize ) , buf_ptr. offset ( * curr) , 2 ) ;
550552 }
551553
552554 // decode last 1 or 2 chars
553- if rem < 10 {
555+ if n < 10 {
554556 * curr -= 1 ;
555- * buf_ptr. offset ( * curr) = ( rem as u8 ) + b'0' ;
557+ * buf_ptr. offset ( * curr) = ( n as u8 ) + b'0' ;
556558 } else {
557- let d1 = rem << 1 ;
559+ let d1 = n << 1 ;
558560 * curr -= 2 ;
559561 ptr:: copy_nonoverlapping ( lut_ptr. offset ( d1 as isize ) , buf_ptr. offset ( * curr) , 2 ) ;
560562 }
@@ -564,7 +566,7 @@ fn fmt_u64_2<const N: usize>(mut rem: u64, buf: &mut [MaybeUninit<u8>; N], curr:
564566#[ stable( feature = "rust1" , since = "1.0.0" ) ]
565567impl fmt:: Display for u128 {
566568 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
567- fmt_u128_2 ( * self , true , f)
569+ fmt_u128 ( * self , true , f)
568570 }
569571}
570572
@@ -578,38 +580,41 @@ impl fmt::Display for i128 {
578580 // convert the negative num to positive by summing 1 to it's 2 complement
579581 ( !self . to_u128 ( ) ) . wrapping_add ( 1 )
580582 } ;
581- fmt_u128_2 ( n, is_nonnegative, f)
583+ fmt_u128 ( n, is_nonnegative, f)
582584 }
583585}
584586
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 {
587592 // 2^128 is about 3*10^38, so 39 gives an extra byte of space
588593 let mut buf = [ MaybeUninit :: < u8 > :: uninit ( ) ; 39 ] ;
589594 let mut curr = buf. len ( ) as isize ;
590595 let buf_ptr = MaybeUninit :: first_ptr_mut ( & mut buf) ;
591596
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) ;
602599
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 {
606606 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 {
613618 ptr:: write_bytes ( buf_ptr. offset ( target) , b'0' , ( curr - target) as usize ) ;
614619 curr = target - 1 ;
615620 * 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:
628633 f. pad_integral ( is_nonnegative, "" , buf_slice)
629634}
630635
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 ) {
632638 const DIV : u64 = 1e19 as u64 ;
633639 let high = ( n >> 64 ) as u64 ;
634640 if high == 0 {
0 commit comments