aranya_base58/
base58.rs

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
25/// 58^i
26const 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
56/// Implemented by types that can encode themselves as Base58.
57pub trait ToBase58 {
58    /// A Base58 string.
59    type Output: Borrow<str>;
60
61    /// Encodes itself as a Base58 string.
62    fn to_base58(&self) -> Self::Output;
63}
64
65/// The Base58 could not be decoded.
66#[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
89// Generate PartialEq for $lhs and $rhs.
90macro_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
110// Generate fixed-size encodings.
111macro_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                /// Number of bytes used in `data`.
119                n: usize,
120            }
121
122            impl $name {
123                /// The maximum size in bytes of the encoded
124                /// value.
125                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                    // It's valid Base58, so just copy it over.
196                    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                /// Returns a string slice of the string's contents.
257                #[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                /// Returns a byte slice of the string's contents.
264                #[inline]
265                pub fn as_bytes(&self) -> &[u8] {
266                    &self.data[self.n..]
267                }
268
269                /// Decodes `s` as bytes.
270                pub fn decode<T: AsRef<[u8]>>(s: T) -> Result<[u8; $size], DecodeError> {
271                    let mut x = Uint::<{ $size/8 }, $size>::new();
272                    // Work 10 bytes at a time (see `RADIX`, etc).
273                    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                /// Encodes `b` as a Base58 string.
292                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/// An unsigned N-bit integer.
334///
335/// - `W` is the number of words
336/// - `B` is the number of bytes (`B = W*8`)
337///
338/// This should just be `N`, but `const_generic_exprs` is still
339/// +nightly.
340#[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    /// Decodes an integer from big-endian format.
351    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    /// Encodes the integer in big-endian format.
362    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    /// Reports whether `self == 0.`
373    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    /// Sets `*self = self*y + r` and reports whether the result
383    /// overflowed.
384    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    /// Computes `(q, r) = self/(58^10)` such that
393    ///
394    /// ```text
395    /// q = x/y
396    /// r = x - y*q
397    /// ```
398    ///
399    /// and sets `*self = q`.
400    fn quo_radix(&mut self) -> u64 {
401        /// 58^10
402        const RADIX: u64 = 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58;
403        // Reciprocal of `RADIX`.
404        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        // Compare words high to low.
417        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}