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