1#![allow(clippy::missing_inline_in_public_items)] use crate::Uint;
4use core::{
5 fmt::{self, Write},
6 mem::MaybeUninit,
7};
8
9mod base {
10 pub(super) trait Base {
11 const BASE: u64;
13 const PREFIX: &'static str;
15
16 const MAX: u64 = crate::utils::max_pow_u64(Self::BASE);
18 const WIDTH: usize;
22 }
23
24 pub(super) struct Binary;
25 impl Base for Binary {
26 const BASE: u64 = 2;
27 const PREFIX: &'static str = "0b";
28 const WIDTH: usize = 63;
29 }
30
31 pub(super) struct Octal;
32 impl Base for Octal {
33 const BASE: u64 = 8;
34 const PREFIX: &'static str = "0o";
35 const WIDTH: usize = 21;
36 }
37
38 pub(super) struct Decimal;
39 impl Base for Decimal {
40 const BASE: u64 = 10;
41 const PREFIX: &'static str = "";
42 const WIDTH: usize = 19;
43 }
44
45 pub(super) struct Hexadecimal;
46 impl Base for Hexadecimal {
47 const BASE: u64 = 16;
48 const PREFIX: &'static str = "0x";
49 const WIDTH: usize = 15;
50 }
51}
52use base::Base;
53
54macro_rules! impl_fmt {
55 ($tr:path; $base:ty, $base_char:literal) => {
56 impl<const BITS: usize, const LIMBS: usize> $tr for Uint<BITS, LIMBS> {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 if let Ok(small) = u64::try_from(self) {
59 return <u64 as $tr>::fmt(&small, f);
60 }
61 if let Ok(small) = u128::try_from(self) {
62 return <u128 as $tr>::fmt(&small, f);
63 }
64
65 let mut s = StackString::<BITS>::new();
67 let mut first = true;
68 for spigot in self.to_base_be_2(<$base>::MAX) {
69 write!(
70 s,
71 concat!("{:0width$", $base_char, "}"),
72 spigot,
73 width = if first { 0 } else { <$base>::WIDTH },
74 )
75 .unwrap();
76 first = false;
77 }
78 f.pad_integral(true, <$base>::PREFIX, s.as_str())
79 }
80 }
81 };
82}
83
84impl<const BITS: usize, const LIMBS: usize> fmt::Debug for Uint<BITS, LIMBS> {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 fmt::Display::fmt(self, f)
87 }
88}
89
90impl_fmt!(fmt::Display; base::Decimal, "");
91impl_fmt!(fmt::Binary; base::Binary, "b");
92impl_fmt!(fmt::Octal; base::Octal, "o");
93impl_fmt!(fmt::LowerHex; base::Hexadecimal, "x");
94impl_fmt!(fmt::UpperHex; base::Hexadecimal, "X");
95
96pub(crate) struct StackString<const SIZE: usize> {
98 len: usize,
99 buf: [MaybeUninit<u8>; SIZE],
100}
101
102impl<const SIZE: usize> StackString<SIZE> {
103 #[inline]
104 pub(crate) const fn new() -> Self {
105 Self {
106 len: 0,
107 buf: unsafe { MaybeUninit::uninit().assume_init() },
108 }
109 }
110
111 #[inline]
112 pub(crate) const fn as_str(&self) -> &str {
113 unsafe { core::str::from_utf8_unchecked(self.as_bytes()) }
117 }
118
119 #[inline]
120 const fn as_bytes(&self) -> &[u8] {
121 unsafe { core::slice::from_raw_parts(self.buf.as_ptr().cast(), self.len) }
122 }
123}
124
125impl<const SIZE: usize> fmt::Write for StackString<SIZE> {
126 fn write_str(&mut self, s: &str) -> fmt::Result {
127 if self.len + s.len() > SIZE {
128 return Err(fmt::Error);
129 }
130 unsafe {
131 let dst = self.buf.as_mut_ptr().add(self.len).cast();
132 core::ptr::copy_nonoverlapping(s.as_ptr(), dst, s.len());
133 }
134 self.len += s.len();
135 Ok(())
136 }
137
138 fn write_char(&mut self, c: char) -> fmt::Result {
139 let clen = c.len_utf8();
140 if self.len + clen > SIZE {
141 return Err(fmt::Error);
142 }
143 c.encode_utf8(unsafe {
144 core::slice::from_raw_parts_mut(self.buf.as_mut_ptr().add(self.len).cast(), clen)
145 });
146 self.len += clen;
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}