1#![allow(clippy::missing_inline_in_public_items)] #![cfg(feature = "alloc")]
3
4use crate::Uint;
5use core::{
6 fmt::{self, Write},
7 mem::MaybeUninit,
8};
9
10mod base {
11 pub(super) trait Base {
12 const MAX: u64;
14 const WIDTH: usize;
19 const PREFIX: &'static str;
21 }
22
23 pub(super) struct Binary;
24 impl Base for Binary {
25 const MAX: u64 = 1 << 63;
26 const WIDTH: usize = 63;
27 const PREFIX: &'static str = "0b";
28 }
29
30 pub(super) struct Octal;
31 impl Base for Octal {
32 const MAX: u64 = 1 << 63;
33 const WIDTH: usize = 21;
34 const PREFIX: &'static str = "0o";
35 }
36
37 pub(super) struct Decimal;
38 impl Base for Decimal {
39 const MAX: u64 = 10_000_000_000_000_000_000;
40 const WIDTH: usize = 19;
41 const PREFIX: &'static str = "";
42 }
43
44 pub(super) struct Hexadecimal;
45 impl Base for Hexadecimal {
46 const MAX: u64 = 1 << 60;
47 const WIDTH: usize = 15;
48 const PREFIX: &'static str = "0x";
49 }
50}
51use base::Base;
52
53macro_rules! write_digits {
54 ($self:expr, $f:expr; $base:ty, $base_char:literal) => {
55 if LIMBS == 0 || $self.is_zero() {
56 return $f.pad_integral(true, <$base>::PREFIX, "0");
57 }
58 let mut buffer = DisplayBuffer::<BITS>::new();
60 for (i, spigot) in $self.to_base_be(<$base>::MAX).enumerate() {
61 write!(
62 buffer,
63 concat!("{:0width$", $base_char, "}"),
64 spigot,
65 width = if i == 0 { 0 } else { <$base>::WIDTH },
66 )
67 .unwrap();
68 }
69 return $f.pad_integral(true, <$base>::PREFIX, buffer.as_str());
70 };
71}
72
73impl<const BITS: usize, const LIMBS: usize> fmt::Display for Uint<BITS, LIMBS> {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 write_digits!(self, f; base::Decimal, "");
76 }
77}
78
79impl<const BITS: usize, const LIMBS: usize> fmt::Debug for Uint<BITS, LIMBS> {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 fmt::Display::fmt(self, f)
82 }
83}
84
85impl<const BITS: usize, const LIMBS: usize> fmt::Binary for Uint<BITS, LIMBS> {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 write_digits!(self, f; base::Binary, "b");
88 }
89}
90
91impl<const BITS: usize, const LIMBS: usize> fmt::Octal for Uint<BITS, LIMBS> {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 write_digits!(self, f; base::Octal, "o");
94 }
95}
96
97impl<const BITS: usize, const LIMBS: usize> fmt::LowerHex for Uint<BITS, LIMBS> {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 write_digits!(self, f; base::Hexadecimal, "x");
100 }
101}
102
103impl<const BITS: usize, const LIMBS: usize> fmt::UpperHex for Uint<BITS, LIMBS> {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 write_digits!(self, f; base::Hexadecimal, "X");
106 }
107}
108
109struct DisplayBuffer<const SIZE: usize> {
110 buf: [MaybeUninit<u8>; SIZE],
111 len: usize,
112}
113
114impl<const SIZE: usize> DisplayBuffer<SIZE> {
115 #[inline]
116 const fn new() -> Self {
117 Self {
118 buf: unsafe { MaybeUninit::uninit().assume_init() },
119 len: 0,
120 }
121 }
122
123 #[inline]
124 fn as_str(&self) -> &str {
125 unsafe { core::str::from_utf8_unchecked(&self.as_bytes_full()[..self.len]) }
129 }
130
131 #[inline]
132 const unsafe fn as_bytes_full(&self) -> &[u8] {
133 unsafe { &*(self.buf.as_slice() as *const [_] as *const [u8]) }
134 }
135}
136
137impl<const SIZE: usize> fmt::Write for DisplayBuffer<SIZE> {
138 fn write_str(&mut self, s: &str) -> fmt::Result {
139 if self.len + s.len() > SIZE {
140 return Err(fmt::Error);
141 }
142 unsafe {
143 let dst = self.buf.as_mut_ptr().add(self.len).cast();
144 core::ptr::copy_nonoverlapping(s.as_ptr(), dst, s.len());
145 }
146 self.len += s.len();
147 Ok(())
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154 use proptest::{prop_assert_eq, proptest};
155
156 #[allow(unused_imports)]
157 use alloc::string::ToString;
158
159 #[allow(clippy::unreadable_literal)]
160 const N: Uint<256, 4> = Uint::from_limbs([
161 0xa8ec92344438aaf4_u64,
162 0x9819ebdbd1faaab1_u64,
163 0x573b1a7064c19c1a_u64,
164 0xc85ef7d79691fe79_u64,
165 ]);
166
167 #[test]
168 fn test_num() {
169 assert_eq!(
170 N.to_string(),
171 "90630363884335538722706632492458228784305343302099024356772372330524102404852"
172 );
173 assert_eq!(
174 format!("{N:x}"),
175 "c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4"
176 );
177 assert_eq!(
178 format!("{N:b}"),
179 "1100100001011110111101111101011110010110100100011111111001111001010101110011101100011010011100000110010011000001100111000001101010011000000110011110101111011011110100011111101010101010101100011010100011101100100100100011010001000100001110001010101011110100"
180 );
181 assert_eq!(
182 format!("{N:o}"),
183 "14413675753626443771712563543234062301470152300636573364375252543243544443210416125364"
184 );
185 }
186
187 #[test]
188 fn test_fmt() {
189 proptest!(|(value: u128)| {
190 let n: Uint<128, 2> = Uint::from(value);
191
192 prop_assert_eq!(format!("{n:b}"), format!("{value:b}"));
193 prop_assert_eq!(format!("{n:064b}"), format!("{value:064b}"));
194 prop_assert_eq!(format!("{n:#b}"), format!("{value:#b}"));
195
196 prop_assert_eq!(format!("{n:o}"), format!("{value:o}"));
197 prop_assert_eq!(format!("{n:064o}"), format!("{value:064o}"));
198 prop_assert_eq!(format!("{n:#o}"), format!("{value:#o}"));
199
200 prop_assert_eq!(format!("{n:}"), format!("{value:}"));
201 prop_assert_eq!(format!("{n:064}"), format!("{value:064}"));
202 prop_assert_eq!(format!("{n:#}"), format!("{value:#}"));
203 prop_assert_eq!(format!("{n:?}"), format!("{value:?}"));
204 prop_assert_eq!(format!("{n:064}"), format!("{value:064?}"));
205 prop_assert_eq!(format!("{n:#?}"), format!("{value:#?}"));
206
207 prop_assert_eq!(format!("{n:x}"), format!("{value:x}"));
208 prop_assert_eq!(format!("{n:064x}"), format!("{value:064x}"));
209 prop_assert_eq!(format!("{n:#x}"), format!("{value:#x}"));
210
211 prop_assert_eq!(format!("{n:X}"), format!("{value:X}"));
212 prop_assert_eq!(format!("{n:064X}"), format!("{value:064X}"));
213 prop_assert_eq!(format!("{n:#X}"), format!("{value:#X}"));
214 });
215 }
216}