Skip to content

Commit 0e141a2

Browse files
Implement _fmt on u128
1 parent 0cbc076 commit 0e141a2

File tree

1 file changed

+107
-94
lines changed

1 file changed

+107
-94
lines changed

library/core/src/fmt/num.rs

Lines changed: 107 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -568,120 +568,133 @@ impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128);
568568
#[stable(feature = "rust1", since = "1.0.0")]
569569
impl fmt::Display for u128 {
570570
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
571-
fmt_u128(*self, true, f)
571+
const MAX_DEC_N: usize = u128::MAX.ilog(10) as usize + 1;
572+
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
573+
574+
f.pad_integral(true, "", self._fmt(&mut buf))
572575
}
573576
}
574577

575578
#[stable(feature = "rust1", since = "1.0.0")]
576579
impl fmt::Display for i128 {
577580
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
578-
fmt_u128(self.unsigned_abs(), *self >= 0, f)
581+
// This is not a typo, we use the maximum number of digits of `u128`, hence why we use
582+
// `u128::MAX`.
583+
const MAX_DEC_N: usize = u128::MAX.ilog(10) as usize + 1;
584+
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
585+
586+
let is_nonnegative = *self >= 0;
587+
f.pad_integral(is_nonnegative, "", self.unsigned_abs()._fmt(&mut buf))
579588
}
580589
}
581590

582-
/// Format optimized for u128. Computation of 128 bits is limited by proccessing
583-
/// in batches of 16 decimals at a time.
584-
fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
585-
// Optimize common-case zero, which would also need special treatment due to
586-
// its "leading" zero.
587-
if n == 0 {
588-
return f.pad_integral(true, "", "0");
589-
}
591+
impl u128 {
592+
/// Format optimized for u128. Computation of 128 bits is limited by proccessing
593+
/// in batches of 16 decimals at a time.
594+
#[doc(hidden)]
595+
#[unstable(
596+
feature = "fmt_internals",
597+
reason = "specialized method meant to only be used by `SpecToString` implementation",
598+
issue = "none"
599+
)]
600+
pub fn _fmt<'a>(self, buf: &'a mut [MaybeUninit<u8>]) -> &'a str {
601+
const MAX_DEC_N: usize = u128::MAX.ilog(10) as usize + 1;
602+
603+
// Optimize common-case zero, which would also need special treatment due to
604+
// its "leading" zero.
605+
if self == 0 {
606+
return "0";
607+
}
590608

591-
// U128::MAX has 39 significant-decimals.
592-
const MAX_DEC_N: usize = u128::MAX.ilog(10) as usize + 1;
593-
// Buffer decimals with right alignment.
594-
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
595-
596-
// Take the 16 least-significant decimals.
597-
let (quot_1e16, mod_1e16) = div_rem_1e16(n);
598-
let (mut remain, mut offset) = if quot_1e16 == 0 {
599-
(mod_1e16, MAX_DEC_N)
600-
} else {
601-
// Write digits at buf[23..39].
602-
enc_16lsd::<{ MAX_DEC_N - 16 }>(&mut buf, mod_1e16);
603-
604-
// Take another 16 decimals.
605-
let (quot2, mod2) = div_rem_1e16(quot_1e16);
606-
if quot2 == 0 {
607-
(mod2, MAX_DEC_N - 16)
609+
// Take the 16 least-significant decimals.
610+
let (quot_1e16, mod_1e16) = div_rem_1e16(self);
611+
let (mut remain, mut offset) = if quot_1e16 == 0 {
612+
(mod_1e16, MAX_DEC_N)
608613
} else {
609-
// Write digits at buf[7..23].
610-
enc_16lsd::<{ MAX_DEC_N - 32 }>(&mut buf, mod2);
611-
// Quot2 has at most 7 decimals remaining after two 1e16 divisions.
612-
(quot2 as u64, MAX_DEC_N - 32)
613-
}
614-
};
614+
// Write digits at buf[23..39].
615+
enc_16lsd::<{ MAX_DEC_N - 16 }>(buf, mod_1e16);
615616

616-
// Format per four digits from the lookup table.
617-
while remain > 999 {
618-
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
619-
// and the while condition ensures at least 4 more decimals.
620-
unsafe { core::hint::assert_unchecked(offset >= 4) }
621-
// SAFETY: The offset counts down from its initial buf.len()
622-
// without underflow due to the previous precondition.
623-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
624-
offset -= 4;
617+
// Take another 16 decimals.
618+
let (quot2, mod2) = div_rem_1e16(quot_1e16);
619+
if quot2 == 0 {
620+
(mod2, MAX_DEC_N - 16)
621+
} else {
622+
// Write digits at buf[7..23].
623+
enc_16lsd::<{ MAX_DEC_N - 32 }>(buf, mod2);
624+
// Quot2 has at most 7 decimals remaining after two 1e16 divisions.
625+
(quot2 as u64, MAX_DEC_N - 32)
626+
}
627+
};
625628

626-
// pull two pairs
627-
let quad = remain % 1_00_00;
628-
remain /= 1_00_00;
629-
let pair1 = (quad / 100) as usize;
630-
let pair2 = (quad % 100) as usize;
631-
buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
632-
buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
633-
buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
634-
buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
635-
}
629+
// Format per four digits from the lookup table.
630+
while remain > 999 {
631+
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
632+
// and the while condition ensures at least 4 more decimals.
633+
unsafe { core::hint::assert_unchecked(offset >= 4) }
634+
// SAFETY: The offset counts down from its initial buf.len()
635+
// without underflow due to the previous precondition.
636+
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
637+
offset -= 4;
638+
639+
// pull two pairs
640+
let quad = remain % 1_00_00;
641+
remain /= 1_00_00;
642+
let pair1 = (quad / 100) as usize;
643+
let pair2 = (quad % 100) as usize;
644+
buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
645+
buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
646+
buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
647+
buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
648+
}
636649

637-
// Format per two digits from the lookup table.
638-
if remain > 9 {
639-
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
640-
// and the if condition ensures at least 2 more decimals.
641-
unsafe { core::hint::assert_unchecked(offset >= 2) }
642-
// SAFETY: The offset counts down from its initial buf.len()
643-
// without underflow due to the previous precondition.
644-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
645-
offset -= 2;
646-
647-
let pair = (remain % 100) as usize;
648-
remain /= 100;
649-
buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
650-
buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
651-
}
650+
// Format per two digits from the lookup table.
651+
if remain > 9 {
652+
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
653+
// and the if condition ensures at least 2 more decimals.
654+
unsafe { core::hint::assert_unchecked(offset >= 2) }
655+
// SAFETY: The offset counts down from its initial buf.len()
656+
// without underflow due to the previous precondition.
657+
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
658+
offset -= 2;
659+
660+
let pair = (remain % 100) as usize;
661+
remain /= 100;
662+
buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
663+
buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
664+
}
652665

653-
// Format the last remaining digit, if any.
654-
if remain != 0 {
655-
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
656-
// and the if condition ensures (at least) 1 more decimals.
657-
unsafe { core::hint::assert_unchecked(offset >= 1) }
658-
// SAFETY: The offset counts down from its initial buf.len()
659-
// without underflow due to the previous precondition.
660-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
661-
offset -= 1;
662-
663-
// Either the compiler sees that remain < 10, or it prevents
664-
// a boundary check up next.
665-
let last = (remain & 15) as usize;
666-
buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
667-
// not used: remain = 0;
668-
}
666+
// Format the last remaining digit, if any.
667+
if remain != 0 {
668+
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
669+
// and the if condition ensures (at least) 1 more decimals.
670+
unsafe { core::hint::assert_unchecked(offset >= 1) }
671+
// SAFETY: The offset counts down from its initial buf.len()
672+
// without underflow due to the previous precondition.
673+
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
674+
offset -= 1;
675+
676+
// Either the compiler sees that remain < 10, or it prevents
677+
// a boundary check up next.
678+
let last = (remain & 15) as usize;
679+
buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
680+
// not used: remain = 0;
681+
}
669682

670-
// SAFETY: All buf content since offset is set.
671-
let written = unsafe { buf.get_unchecked(offset..) };
672-
// SAFETY: Writes use ASCII from the lookup table exclusively.
673-
let as_str = unsafe {
674-
str::from_utf8_unchecked(slice::from_raw_parts(
675-
MaybeUninit::slice_as_ptr(written),
676-
written.len(),
677-
))
678-
};
679-
f.pad_integral(is_nonnegative, "", as_str)
683+
// SAFETY: All buf content since offset is set.
684+
let written = unsafe { buf.get_unchecked(offset..) };
685+
// SAFETY: Writes use ASCII from the lookup table exclusively.
686+
unsafe {
687+
str::from_utf8_unchecked(slice::from_raw_parts(
688+
MaybeUninit::slice_as_ptr(written),
689+
written.len(),
690+
))
691+
}
692+
}
680693
}
681694

682695
/// Encodes the 16 least-significant decimals of n into `buf[OFFSET .. OFFSET +
683696
/// 16 ]`.
684-
fn enc_16lsd<const OFFSET: usize>(buf: &mut [MaybeUninit<u8>; 39], n: u64) {
697+
fn enc_16lsd<const OFFSET: usize>(buf: &mut [MaybeUninit<u8>], n: u64) {
685698
// Consume the least-significant decimals from a working copy.
686699
let mut remain = n;
687700

0 commit comments

Comments
 (0)