1#[cfg(feature = "alloc")]
2use alloc::string::String;
3use core::{
4 borrow::Borrow,
5 cmp::{Ord, Ordering, PartialEq, PartialOrd},
6 fmt,
7 hash::{Hash, Hasher},
8 ops::Deref,
9 result::Result,
10 str::{self, FromStr},
11};
12
13use aranya_buggy::{Bug, BugExt};
14use byteorder::{BigEndian, ByteOrder};
15
16use crate::arith::{div_ww, mul_add_ww};
17
18const ALPHABET: [u8; 58] = [
19 b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E', b'F', b'G',
20 b'H', b'J', b'K', b'L', b'M', b'N', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y',
21 b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'm', b'n', b'o', b'p',
22 b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z',
23];
24
25const RADII: [u64; 11] = [
27 0,
28 58,
29 58 * 58,
30 58 * 58 * 58,
31 58 * 58 * 58 * 58,
32 58 * 58 * 58 * 58 * 58,
33 58 * 58 * 58 * 58 * 58 * 58,
34 58 * 58 * 58 * 58 * 58 * 58 * 58,
35 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58,
36 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58,
37 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58,
38];
39
40const B58: [u8; 256] = [
41 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
42 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
43 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 255, 255,
44 255, 255, 255, 255, 255, 9, 10, 11, 12, 13, 14, 15, 16, 255, 17, 18, 19, 20, 21, 255, 22, 23,
45 24, 25, 26, 27, 28, 29, 30, 31, 32, 255, 255, 255, 255, 255, 255, 33, 34, 35, 36, 37, 38, 39,
46 40, 41, 42, 43, 255, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 255, 255, 255,
47 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
48 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
49 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
50 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
51 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
52 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
53 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
54];
55
56pub trait ToBase58 {
58 type Output: Borrow<str>;
60
61 fn to_base58(&self) -> Self::Output;
63}
64
65#[derive(Clone, Debug)]
67pub enum DecodeError {
68 BadInput,
69 Bug(Bug),
70}
71
72impl From<Bug> for DecodeError {
73 fn from(bug: Bug) -> Self {
74 DecodeError::Bug(bug)
75 }
76}
77
78impl fmt::Display for DecodeError {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 match self {
81 Self::BadInput => write!(f, "bad input"),
82 Self::Bug(bug) => write!(f, "{bug}"),
83 }
84 }
85}
86
87impl core::error::Error for DecodeError {}
88
89macro_rules! impl_eq {
91 ($lhs:ty, $rhs:ty) => {
92 #[allow(unused_lifetimes)]
93 impl<'a, 'b> PartialEq<$rhs> for $lhs {
94 #[inline]
95 fn eq(&self, other: &$rhs) -> bool {
96 PartialEq::eq(&self[..], &other[..])
97 }
98 }
99
100 #[allow(unused_lifetimes)]
101 impl<'a, 'b> PartialEq<$lhs> for $rhs {
102 #[inline]
103 fn eq(&self, other: &$lhs) -> bool {
104 PartialEq::eq(&self[..], &other[..])
105 }
106 }
107 };
108}
109
110macro_rules! encode_x {
112 ($($name:ident => $size:expr),+ $(,)?) => {
113 $(
114 #[doc = concat!("A Base58-encoded ", stringify!($size), "-byte value.")]
115 #[derive(Copy, Clone)]
116 pub struct $name {
117 data: [u8; Self::MAX_SIZE],
118 n: usize,
120 }
121
122 impl $name {
123 pub const MAX_SIZE: usize = ($size*1375)/1000;
126 }
127
128 impl fmt::Display for $name {
129 #[inline]
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 fmt::Display::fmt(&**self, f)
132 }
133 }
134
135 impl fmt::Debug for $name {
136 #[inline]
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 fmt::Debug::fmt(&**self, f)
139 }
140 }
141
142 impl AsRef<[u8]> for $name {
143 #[inline]
144 fn as_ref(&self) -> &[u8] {
145 self.as_bytes()
146 }
147 }
148
149 impl AsRef<str> for $name {
150 #[inline]
151 fn as_ref(&self) -> &str {
152 self.as_str()
153 }
154 }
155
156 impl Borrow<str> for $name {
157 #[inline]
158 fn borrow(&self) -> &str {
159 self.as_str()
160 }
161 }
162
163 impl Deref for $name {
164 type Target = str;
165
166 #[inline]
167 fn deref(&self) -> &str {
168 self.as_str()
169 }
170 }
171
172 impl TryFrom<&str> for $name {
173 type Error = DecodeError;
174
175 #[inline]
176 fn try_from(s: &str) -> Result<Self, DecodeError> {
177 Self::from_str(s)
178 }
179 }
180
181 impl TryFrom<$name> for [u8; $size] {
182 type Error = DecodeError;
183
184 #[inline]
185 fn try_from(s: $name) -> Result<Self, DecodeError> {
186 $name::decode(&s)
187 }
188 }
189
190 impl FromStr for $name {
191 type Err = DecodeError;
192
193 fn from_str(s: &str) -> Result<Self, DecodeError> {
194 Self::decode(s)?;
195 let mut v = Self::default();
197 v.data[..s.len()].copy_from_slice(s.as_bytes());
198 Ok(v)
199 }
200 }
201
202 impl Default for $name {
203 #[inline]
204 fn default() -> Self {
205 Self {
206 data: [49u8; Self::MAX_SIZE],
207 n: 0,
208 }
209 }
210 }
211
212 impl Eq for $name {}
213 impl PartialEq for $name {
214 #[inline]
215 fn eq(&self, other: &Self) -> bool {
216 PartialEq::eq(&self[..], &other[..])
217 }
218 }
219 impl_eq!($name, str);
220 impl_eq!($name, &'a str);
221 #[cfg(feature = "std")]
222 impl_eq!(std::borrow::Cow<'a, str>, $name);
223 #[cfg(feature = "alloc")]
224 impl_eq!($name, String);
225
226 impl Ord for $name {
227 #[inline]
228 fn cmp(&self, other: &Self) -> Ordering {
229 Ord::cmp(&self[..], &other[..])
230 }
231 }
232
233 impl PartialOrd for $name {
234 #[inline]
235 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
236 Some(self.cmp(other))
237 }
238 }
239
240 impl Hash for $name {
241 #[inline]
242 fn hash<H: Hasher>(&self, hasher: &mut H) {
243 (**self).hash(hasher)
244 }
245 }
246
247 impl ToBase58 for [u8; $size] {
248 type Output = $name;
249
250 fn to_base58(&self) -> Self::Output {
251 $name::encode(self)
252 }
253 }
254
255 impl $name {
256 #[inline]
258 pub fn as_str(&self) -> &str {
259 str::from_utf8(self.as_bytes())
260 .expect("should be valid UTF-8")
261 }
262
263 #[inline]
265 pub fn as_bytes(&self) -> &[u8] {
266 &self.data[self.n..]
267 }
268
269 pub fn decode<T: AsRef<[u8]>>(s: T) -> Result<[u8; $size], DecodeError> {
271 let mut x = Uint::<{ $size/8 }, $size>::new();
272 for chunk in s.as_ref().chunks(10) {
274 let total = chunk
275 .iter()
276 .map(|c| B58[*c as usize])
277 .try_fold(0, |acc: u64, v| {
278 if v == 255 {
279 Err(DecodeError::BadInput)
280 } else {
281 Ok(acc.checked_mul(58).assume("doesn't wrap")?.checked_add(u64::from(v)).assume("doesn't wrap")?)
282 }
283 })?;
284 if !x.fma(RADII[chunk.len()], total) {
285 return Err(DecodeError::BadInput);
286 }
287 }
288 Ok(x.to_be_bytes())
289 }
290
291 pub fn encode(b: &[u8; $size]) -> $name {
293 let mut dst = [0u8; Self::MAX_SIZE];
294 let mut x = Uint::<{ $size/8 }, $size>::from_be_bytes(b);
295
296 let mut i = dst.len();
297 while !x.is_zero() {
298 let mut r = x.quo_radix();
299 if x.is_zero() {
300 while r > 0 {
301 i = i.checked_sub(1).expect("i must be non-zero");
302 dst[i] = ALPHABET[(r % 58) as usize];
303 r /= 58;
304 }
305 } else {
306 for _ in 0..10 {
307 i = i.checked_sub(1).expect("i must be non-zero");
308 dst[i] = ALPHABET[(r % 58) as usize];
309 r /= 58;
310 }
311 }
312 }
313
314 for c in b {
315 if *c != 0 {
316 break;
317 }
318 i = i.checked_sub(1).expect("i must be non-zero");
319 dst[i] = b'1';
320 }
321 $name{ data: dst, n: i }
322 }
323 }
324 )+
325 };
326}
327encode_x! {
328 String16 => 16,
329 String32 => 32,
330 String64 => 64,
331}
332
333#[derive(Debug, Eq, PartialEq)]
341struct Uint<const W: usize, const B: usize> {
342 words: [u64; W],
343}
344
345impl<const W: usize, const B: usize> Uint<W, B> {
346 fn new() -> Self {
347 Self { words: [0u64; W] }
348 }
349
350 fn from_be_bytes(b: &[u8; B]) -> Self {
352 let mut z = [0u64; W];
353 let mut j = B;
354 for x in &mut z {
355 j = j.checked_sub(8).expect("j must be >= 8");
356 *x = BigEndian::read_u64(&b[j..]);
357 }
358 Self { words: z }
359 }
360
361 fn to_be_bytes(&self) -> [u8; B] {
363 let mut b = [0u8; B];
364 let mut i = B;
365 for x in self.words {
366 i = i.checked_sub(8).expect("i must be >= 8");
367 BigEndian::write_u64(&mut b[i..], x);
368 }
369 b
370 }
371
372 fn is_zero(&self) -> bool {
374 for x in self.words {
375 if x != 0 {
376 return false;
377 }
378 }
379 true
380 }
381
382 fn fma(&mut self, y: u64, r: u64) -> bool {
385 let mut c = r;
386 for x in &mut self.words {
387 (c, *x) = mul_add_ww(*x, y, c);
388 }
389 c == 0
390 }
391
392 fn quo_radix(&mut self) -> u64 {
401 const RADIX: u64 = 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58;
403 const REC: u64 = 0x568df8b76cbf212c;
405
406 let mut r = 0;
407 for x in self.words.iter_mut().rev() {
408 (*x, r) = div_ww(r, *x, RADIX, REC);
409 }
410 r
411 }
412}
413
414impl<const W: usize, const B: usize> Ord for Uint<W, B> {
415 fn cmp(&self, other: &Self) -> Ordering {
416 self.words.iter().rev().cmp(other.words.iter().rev())
418 }
419}
420
421impl<const W: usize, const B: usize> PartialOrd for Uint<W, B> {
422 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
423 Some(Ord::cmp(self, other))
424 }
425}
426
427#[cfg(test)]
428mod test {
429 use std::io::Read;
430
431 use flate2::bufread::GzDecoder;
432 use serde::{Deserialize, Serialize};
433
434 use super::*;
435
436 #[derive(Serialize, Deserialize, Debug)]
437 struct TestCase {
438 input: String,
439 output: String,
440 }
441 macro_rules! impl_test {
442 ($name:ident, $type:ident) => {
443 #[test]
444 fn $name() {
445 const TEST_CASES: &[u8] =
446 include_bytes!(concat!("../testdata/", stringify!($name), ".json.gz"));
447 let tests: Vec<TestCase> = serde_json::from_slice(
448 &GzDecoder::new(TEST_CASES)
449 .bytes()
450 .collect::<Result<Vec<_>, _>>()
451 .unwrap(),
452 )
453 .unwrap();
454 for (i, tc) in tests.iter().enumerate() {
455 let input = hex::decode(&tc.input)
456 .expect(&format!("{i}"))
457 .try_into()
458 .expect(&format!("{i}"));
459
460 let got = $type::encode(&input);
461 assert_eq!(got, tc.output.as_str(), "{i}");
462
463 let got = $type::decode(&got).expect(&format!("{i}"));
464 assert_eq!(got.as_ref(), input, "{i}");
465 }
466 }
467 };
468 }
469 impl_test!(test_string16, String16);
470 impl_test!(test_string64, String64);
471}