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