1use core::{
2 borrow::Borrow,
3 cmp::{Ord, Ordering, PartialEq, PartialOrd},
4 ffi::CStr,
5 fmt,
6 hash::{Hash, Hasher},
7 ops::Deref,
8 result::Result,
9 str::{self, FromStr},
10};
11
12use buggy::{Bug, BugExt};
13
14use crate::arith::{div_ww, mul_add_ww};
15
16const ALPHABET: [u8; 58] = [
17 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',
18 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',
19 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',
20 b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z',
21];
22
23const RADII: [u64; 11] = [
25 0,
26 58,
27 58 * 58,
28 58 * 58 * 58,
29 58 * 58 * 58 * 58,
30 58 * 58 * 58 * 58 * 58,
31 58 * 58 * 58 * 58 * 58 * 58,
32 58 * 58 * 58 * 58 * 58 * 58 * 58,
33 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58,
34 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58,
35 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58,
36];
37
38const B58: [u8; 256] = [
39 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
40 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
41 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 255, 255,
42 255, 255, 255, 255, 255, 9, 10, 11, 12, 13, 14, 15, 16, 255, 17, 18, 19, 20, 21, 255, 22, 23,
43 24, 25, 26, 27, 28, 29, 30, 31, 32, 255, 255, 255, 255, 255, 255, 33, 34, 35, 36, 37, 38, 39,
44 40, 41, 42, 43, 255, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 255, 255, 255,
45 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
46 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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,
52];
53
54pub trait ToBase58 {
56 type Output: Borrow<str>;
58
59 fn to_base58(&self) -> Self::Output;
61}
62
63#[derive(Clone, Debug)]
65pub enum DecodeError {
66 BadInput,
68 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::BUFFER_SIZE],
118 }
119
120 impl $name {
121 pub const B58_SIZE: usize = ($size * 1375) / 1000;
123
124 pub const BUFFER_SIZE: usize = Self::B58_SIZE + 1;
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.as_str(), 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.as_str(), 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 AsRef<CStr> for $name {
157 #[inline]
158 fn as_ref(&self) -> &CStr {
159 self.as_cstr()
160 }
161 }
162
163 impl Borrow<str> for $name {
164 #[inline]
165 fn borrow(&self) -> &str {
166 self.as_str()
167 }
168 }
169
170 impl Deref for $name {
171 type Target = str;
172
173 #[inline]
174 fn deref(&self) -> &str {
175 self.as_str()
176 }
177 }
178
179 impl TryFrom<&str> for $name {
180 type Error = DecodeError;
181
182 #[inline]
183 fn try_from(s: &str) -> Result<Self, DecodeError> {
184 Self::from_str(s)
185 }
186 }
187
188 impl TryFrom<$name> for [u8; $size] {
189 type Error = DecodeError;
190
191 #[inline]
192 fn try_from(s: $name) -> Result<Self, DecodeError> {
193 $name::decode(&s)
194 }
195 }
196
197 impl FromStr for $name {
198 type Err = DecodeError;
199
200 fn from_str(s: &str) -> Result<Self, DecodeError> {
201 let b = s.as_bytes();
202 Self::decode(b)?;
203 let mut v = Self::default();
205 let start = Self::B58_SIZE.checked_sub(b.len()).assume("decode ensures it will fit")?;
206 v.data[start..Self::B58_SIZE].copy_from_slice(b);
207 Ok(v)
208 }
209 }
210
211 impl Default for $name {
212 #[inline]
213 fn default() -> Self {
214 let mut this = Self {
215 data: [b'1'; Self::BUFFER_SIZE],
216 };
217 this.data[Self::BUFFER_SIZE - 1] = 0;
218 this
219 }
220 }
221
222 impl Eq for $name {}
223 impl PartialEq for $name {
224 #[inline]
225 fn eq(&self, other: &Self) -> bool {
226 PartialEq::eq(&self[..], &other[..])
227 }
228 }
229 impl_eq!($name, str);
230 impl_eq!($name, &'a str);
231 #[cfg(feature = "alloc")]
232 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
233 impl_eq!(alloc::borrow::Cow<'a, str>, $name);
234 #[cfg(feature = "alloc")]
235 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
236 impl_eq!($name, alloc::string::String);
237
238 impl Ord for $name {
239 #[inline]
240 fn cmp(&self, other: &Self) -> Ordering {
241 Ord::cmp(&self[..], &other[..])
242 }
243 }
244
245 impl PartialOrd for $name {
246 #[inline]
247 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
248 Some(self.cmp(other))
249 }
250 }
251
252 impl Hash for $name {
253 #[inline]
254 fn hash<H: Hasher>(&self, hasher: &mut H) {
255 (**self).hash(hasher)
256 }
257 }
258
259 impl ToBase58 for [u8; $size] {
260 type Output = $name;
261
262 fn to_base58(&self) -> Self::Output {
263 $name::encode(self)
264 }
265 }
266
267 impl $name {
268 #[inline]
270 pub fn as_str(&self) -> &str {
271 str::from_utf8(self.as_bytes())
272 .expect("should be valid UTF-8")
273 }
274
275 #[inline]
277 pub fn as_bytes(&self) -> &[u8; Self::B58_SIZE] {
278 self.data[..Self::B58_SIZE].try_into().expect("array size matches slice")
279 }
280
281 #[inline]
283 pub fn as_cstr(&self) -> &CStr {
284 CStr::from_bytes_with_nul(&self.data).expect("should be valid C string")
285 }
286
287 pub fn decode<T: AsRef<[u8]>>(s: T) -> Result<[u8; $size], DecodeError> {
289 let mut x = Uint::<{ $size/8 }, $size>::new();
290 for chunk in s.as_ref().chunks(10) {
292 let total = chunk
293 .iter()
294 .map(|c| B58[*c as usize])
295 .try_fold(0, |acc: u64, v| {
296 if v == 255 {
297 Err(DecodeError::BadInput)
298 } else {
299 Ok(acc.checked_mul(58).assume("doesn't wrap")?.checked_add(u64::from(v)).assume("doesn't wrap")?)
300 }
301 })?;
302 if !x.fma(RADII[chunk.len()], total) {
303 return Err(DecodeError::BadInput);
304 }
305 }
306 Ok(x.to_be_bytes())
307 }
308
309 pub fn encode(b: &[u8; $size]) -> Self {
311 let mut dst = Self::default();
312
313 let mut x = Uint::<{ $size/8 }, $size>::from_be_bytes(b);
314
315 let mut i = Self::B58_SIZE;
316 while !x.is_zero() {
317 let mut r = x.quo_radix();
318 if x.is_zero() {
319 while r > 0 {
320 i = i.checked_sub(1).expect("i must be non-zero");
321 dst.data[i] = ALPHABET[(r % 58) as usize];
322 r /= 58;
323 }
324 } else {
325 for _ in 0..10 {
326 i = i.checked_sub(1).expect("i must be non-zero");
327 dst.data[i] = ALPHABET[(r % 58) as usize];
328 r /= 58;
329 }
330 }
331 }
332
333 dst
334 }
335 }
336 )+
337 };
338}
339encode_x! {
340 String16 => 16,
341 String32 => 32,
342 String64 => 64,
343}
344
345#[derive(Debug, Eq, PartialEq)]
353struct Uint<const W: usize, const B: usize> {
354 words: [u64; W],
355}
356
357impl<const W: usize, const B: usize> Uint<W, B> {
358 fn new() -> Self {
359 Self { words: [0u64; W] }
360 }
361
362 fn from_be_bytes(b: &[u8; B]) -> Self {
364 let mut z = [0u64; W];
365 for (bytes, word) in b.chunks_exact(8).rev().zip(&mut z) {
366 *word = u64::from_be_bytes(bytes.try_into().expect("len == 8"));
367 }
368 Self { words: z }
369 }
370
371 fn to_be_bytes(&self) -> [u8; B] {
373 let mut b = [0u8; B];
374 for (bytes, word) in b.chunks_exact_mut(8).rev().zip(self.words) {
375 bytes.copy_from_slice(&word.to_be_bytes());
376 }
377 b
378 }
379
380 fn is_zero(&self) -> bool {
382 for x in self.words {
383 if x != 0 {
384 return false;
385 }
386 }
387 true
388 }
389
390 fn fma(&mut self, y: u64, r: u64) -> bool {
393 let mut c = r;
394 for x in &mut self.words {
395 (c, *x) = mul_add_ww(*x, y, c);
396 }
397 c == 0
398 }
399
400 fn quo_radix(&mut self) -> u64 {
409 const RADIX: u64 = 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58;
411 const REC: u64 = 0x568df8b76cbf212c;
413
414 let mut r = 0;
415 for x in self.words.iter_mut().rev() {
416 (*x, r) = div_ww(r, *x, RADIX, REC);
417 }
418 r
419 }
420}
421
422impl<const W: usize, const B: usize> Ord for Uint<W, B> {
423 fn cmp(&self, other: &Self) -> Ordering {
424 self.words.iter().rev().cmp(other.words.iter().rev())
426 }
427}
428
429impl<const W: usize, const B: usize> PartialOrd for Uint<W, B> {
430 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
431 Some(Ord::cmp(self, other))
432 }
433}
434
435#[cfg(test)]
436mod test {
437 use std::io::Read;
438
439 use flate2::bufread::GzDecoder;
440
441 use super::*;
442
443 #[derive(Debug, serde_derive::Deserialize)]
444 struct TestCase {
445 input: String,
446 output: String,
447 }
448 macro_rules! impl_test {
449 ($name:ident, $type:ident) => {
450 #[test]
451 fn $name() {
452 const TEST_CASES: &[u8] =
453 include_bytes!(concat!("../testdata/", stringify!($name), ".json.gz"));
454 let tests: Vec<TestCase> = serde_json::from_slice(
455 &GzDecoder::new(TEST_CASES)
456 .bytes()
457 .collect::<Result<Vec<_>, _>>()
458 .unwrap(),
459 )
460 .unwrap();
461 for (i, tc) in tests.iter().enumerate() {
462 let test = format!("test case {i}");
463 let input = hex::decode(&tc.input)
464 .expect(&test)
465 .try_into()
466 .expect(&test);
467
468 let got = $type::encode(&input);
469 let padded = format!("{:1>width$}", tc.output, width = $type::B58_SIZE);
471 assert_eq!(got.as_str(), padded.as_str(), "{test}");
472
473 let got = $type::decode(&got).expect(&test);
474 assert_eq!(got.as_ref(), input, "{test}");
475 }
476 }
477 };
478 }
479 impl_test!(test_string16, String16);
480 impl_test!(test_string64, String64);
481
482 #[test]
483 fn test_from_str() {
484 let str = String16::from_str("abcd").unwrap();
485 assert_eq!(str.len(), 22);
486 assert_eq!(str, "111111111111111111abcd");
487
488 assert_eq!(str, String16::from_str(str.as_str()).unwrap());
489 }
490}