Skip to main content

thruput_u256/
fmt.rs

1//! Formatting.
2//!
3//! `Debug` is unconditional -- contract state structs `#[derive(Debug)]` over
4//! `U256`, and gating it would break them in guest builds -- so it renders hex,
5//! which needs no division. `Display`/`FromStr` need a decimal division loop no
6//! release guest build should carry, so they sit behind `fmt`.
7
8use crate::U256;
9use core::fmt;
10
11/// 2^256 - 1 is 64 hexadecimal digits.
12pub(crate) const HEX_DIGITS: usize = 64;
13
14/// 2^256 - 1 is 78 decimal digits. Only the decimal path uses this, and that
15/// is behind `fmt`.
16#[cfg(feature = "fmt")]
17pub(crate) const DEC_DIGITS: usize = 78;
18
19pub(crate) const LOWER: [u8; 16] = *b"0123456789abcdef";
20
21// Only `UpperHex` uses this, and that is behind `fmt` -- so in a guest build
22// the table itself must go too, or it is dead rodata.
23#[cfg(feature = "fmt")]
24pub(crate) const UPPER: [u8; 16] = *b"0123456789ABCDEF";
25
26/// Renders the significant hex digits into `buf`, returning them as a string.
27/// Digits are written from the back, so the return value borrows the tail.
28pub(crate) fn hex_digits<'a>(
29    n: &U256,
30    buf: &'a mut [u8; HEX_DIGITS],
31    table: &[u8; 16],
32) -> &'a str {
33    let mut pos = buf.len();
34    let mut v = *n;
35
36    if v.is_zero() {
37        pos -= 1;
38        buf[pos] = b'0';
39    } else {
40        while !v.is_zero() {
41            pos -= 1;
42            buf[pos] = table[(v.to_limbs()[0] & 0xf) as usize];
43            v = v.wrapping_shr(4);
44        }
45    }
46
47    core::str::from_utf8(&buf[pos..]).expect("hex digits are ASCII")
48}
49
50impl fmt::Debug for U256 {
51    /// Renders as `0x`-prefixed hex.
52    ///
53    /// Unlike the `LowerHex`/`UpperHex` impls this does not honour width or
54    /// fill flags -- `Debug` is for diagnostics, and a padded `{:?}` on an
55    /// integer is not a thing callers ask for. Use `{:x}` when the exact
56    /// textual shape matters.
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        let mut buf = [0u8; HEX_DIGITS];
59        f.write_str("0x")?;
60        f.write_str(hex_digits(self, &mut buf, &LOWER))
61    }
62}
63
64#[cfg(feature = "fmt")]
65impl fmt::LowerHex for U256 {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        let mut buf = [0u8; HEX_DIGITS];
68        let digits = hex_digits(self, &mut buf, &LOWER);
69        // `pad_integral` applies width, fill, alignment and zero-padding, and
70        // emits the prefix only under `#` -- so `{:064x}` and `{:#x}` behave
71        // as they do for the primitive integers. Fixed-width hex is the
72        // common spelling for a 256-bit word, so this has to be right.
73        f.pad_integral(true, "0x", digits)
74    }
75}
76
77#[cfg(feature = "fmt")]
78impl fmt::UpperHex for U256 {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        let mut buf = [0u8; HEX_DIGITS];
81        let digits = hex_digits(self, &mut buf, &UPPER);
82        // Lowercase `0x` even here: `format!("{:#X}", 255u8)` is `"0xFF"`.
83        f.pad_integral(true, "0x", digits)
84    }
85}
86
87/// Renders the decimal digits into `buf`, returning them as a string.
88#[cfg(feature = "fmt")]
89pub(crate) fn dec_digits<'a>(
90    n: &U256,
91    buf: &'a mut [u8; DEC_DIGITS],
92) -> &'a str {
93    let mut pos = buf.len();
94    let mut v = *n;
95
96    if v.is_zero() {
97        pos -= 1;
98        buf[pos] = b'0';
99    }
100
101    // Nine digits at a time: 10^9 is the largest power of ten fitting in a
102    // u32, so each step is one `div_rem_small` rather than nine.
103    while !v.is_zero() {
104        let (q, r) = v.div_rem_small(1_000_000_000);
105        v = q;
106        let mut r = r;
107        // The leading chunk emits only its significant digits; every other
108        // chunk is zero-padded to nine, or interior zeros would be lost.
109        let count = if v.is_zero() {
110            let mut d = 0;
111            let mut t = r;
112            while t != 0 {
113                d += 1;
114                t /= 10;
115            }
116            d
117        } else {
118            9
119        };
120        let mut i = 0;
121        while i < count {
122            pos -= 1;
123            buf[pos] = b'0' + (r % 10) as u8;
124            r /= 10;
125            i += 1;
126        }
127    }
128
129    core::str::from_utf8(&buf[pos..]).expect("decimal digits are ASCII")
130}
131
132#[cfg(feature = "fmt")]
133impl fmt::Display for U256 {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        let mut buf = [0u8; DEC_DIGITS];
136        let digits = dec_digits(self, &mut buf);
137        f.pad_integral(true, "", digits)
138    }
139}
140
141/// Returned when a string cannot be parsed as a [`U256`].
142#[cfg(feature = "fmt")]
143#[derive(Debug, Clone, Copy, PartialEq, Eq)]
144pub enum ParseU256Error {
145    /// The input was empty.
146    Empty,
147    /// The input contained a character that is not a decimal digit.
148    InvalidDigit,
149    /// The value does not fit in 256 bits.
150    Overflow,
151}
152
153#[cfg(feature = "fmt")]
154impl fmt::Display for ParseU256Error {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        f.write_str(match self {
157            Self::Empty => "cannot parse an empty string as U256",
158            Self::InvalidDigit => "invalid decimal digit",
159            Self::Overflow => "value does not fit in 256 bits",
160        })
161    }
162}
163
164#[cfg(all(feature = "fmt", feature = "std"))]
165impl std::error::Error for ParseU256Error {}
166
167#[cfg(feature = "fmt")]
168impl core::str::FromStr for U256 {
169    type Err = ParseU256Error;
170
171    fn from_str(s: &str) -> Result<Self, Self::Err> {
172        if s.is_empty() {
173            return Err(ParseU256Error::Empty);
174        }
175
176        let mut acc = U256::ZERO;
177        for byte in s.bytes() {
178            if !byte.is_ascii_digit() {
179                return Err(ParseU256Error::InvalidDigit);
180            }
181            let (scaled, overflow) = acc.overflowing_mul_u32(10);
182            if overflow {
183                return Err(ParseU256Error::Overflow);
184            }
185            let (next, overflow) =
186                scaled.overflowing_add(U256::from_u32((byte - b'0') as u32));
187            if overflow {
188                return Err(ParseU256Error::Overflow);
189            }
190            acc = next;
191        }
192
193        Ok(acc)
194    }
195}