From bad8e761a828a351715b03b9f5116da3bda944da Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 29 May 2025 13:54:35 +0200 Subject: [PATCH 1/2] avoid allocations in fmt machinery, but still keep it under (different) feature flag --- Cargo.toml | 2 ++ src/fmt.rs | 37 +++++++++++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e2a226f7..6ecb2375 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -124,6 +124,7 @@ std = [ "bytes?/std", "fastrlp-03?/std", "fastrlp-04?/std", + "fmt", "num-bigint?/std", "num-integer?/std", "num-traits?/std", @@ -148,6 +149,7 @@ alloc = [ "valuable?/alloc", "zeroize?/alloc", ] +fmt = [] # nightly-only features nightly = [] diff --git a/src/fmt.rs b/src/fmt.rs index 568129fc..41866e9a 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -1,5 +1,5 @@ #![allow(clippy::missing_inline_in_public_items)] // allow format functions -#![cfg(feature = "alloc")] +#![cfg(feature = "fmt")] use crate::Uint; use core::{ @@ -51,13 +51,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 +93,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 +105,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); } } From 2a222afea370c653d2d4bb3f8137276811d3480e Mon Sep 17 00:00:00 2001 From: Alexander Date: Tue, 8 Jul 2025 02:06:52 +0200 Subject: [PATCH 2/2] remove feature as suggested --- Cargo.toml | 2 -- src/fmt.rs | 1 - 2 files changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b43be94a..b859e7e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -125,7 +125,6 @@ std = [ "bytes?/std", "fastrlp-03?/std", "fastrlp-04?/std", - "fmt", "num-bigint?/std", "num-integer?/std", "num-traits?/std", @@ -150,7 +149,6 @@ alloc = [ "valuable?/alloc", "zeroize?/alloc", ] -fmt = [] # nightly-only features nightly = [] diff --git a/src/fmt.rs b/src/fmt.rs index 41866e9a..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 = "fmt")] use crate::Uint; use core::{