diff --git a/src/fmt.rs b/src/fmt.rs index 568129fc..2d026a6a 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -1,5 +1,4 @@ #![allow(clippy::missing_inline_in_public_items)] // allow format functions -#![cfg(feature = "alloc")] use crate::Uint; use core::{ @@ -51,13 +50,34 @@ mod base { use base::Base; macro_rules! write_digits { - ($self:expr, $f:expr; $base:ty, $base_char:literal) => { + ($self:expr, $f:expr; $base:ty, $base_char:literal, $be_repr_capacity_factor:literal) => { if LIMBS == 0 || $self.is_zero() { return $f.pad_integral(true, <$base>::PREFIX, "0"); } // Use `BITS` for all bases since `generic_const_exprs` is not yet stable. let mut buffer = DisplayBuffer::::new(); - for (i, spigot) in $self.to_base_be(<$base>::MAX).enumerate() { + // Use some MaybeUninit to avoid allocating + let mut be_buffer = + [[const { MaybeUninit::::uninit() }; LIMBS]; $be_repr_capacity_factor]; + // Two passes: + // - collect + let mut num_initialized = 0; + for (i, spigot) in $self.to_base_le(<$base>::MAX).enumerate() { + let major = i / LIMBS; + let minor = i % LIMBS; + be_buffer[major][minor].write(spigot); + num_initialized += 1; + } + // - write + // Safety: We will cast everything to slice, with length being number of + // initialized elements, and walk back + let src = unsafe { + core::slice::from_raw_parts( + (&be_buffer as *const [MaybeUninit; LIMBS]).cast::(), + num_initialized, + ) + }; + for (i, spigot) in src.iter().rev().enumerate() { write!( buffer, concat!("{:0width$", $base_char, "}"), @@ -72,7 +92,7 @@ macro_rules! write_digits { impl fmt::Display for Uint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write_digits!(self, f; base::Decimal, ""); + write_digits!(self, f; base::Decimal, "", 2); } } @@ -84,25 +104,25 @@ impl fmt::Debug for Uint { impl fmt::Binary for Uint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write_digits!(self, f; base::Binary, "b"); + write_digits!(self, f; base::Binary, "b", 2); } } impl fmt::Octal for Uint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write_digits!(self, f; base::Octal, "o"); + write_digits!(self, f; base::Octal, "o", 2); } } impl fmt::LowerHex for Uint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write_digits!(self, f; base::Hexadecimal, "x"); + write_digits!(self, f; base::Hexadecimal, "x", 2); } } impl fmt::UpperHex for Uint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write_digits!(self, f; base::Hexadecimal, "X"); + write_digits!(self, f; base::Hexadecimal, "X", 2); } }