spideroak_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 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    /// The input is not valid base58.
69    BadInput,
70    /// An internal bug occurred.
71    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
91// Generate PartialEq for $lhs and $rhs.
92macro_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
112// Generate fixed-size encodings.
113macro_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                /// Number of bytes used in `data`.
121                n: usize,
122            }
123
124            impl $name {
125                /// The maximum size in bytes of the encoded
126                /// value.
127                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                    // It's valid Base58, so just copy it over.
198                    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                /// Returns a string slice of the string's contents.
259                #[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                /// Returns a byte slice of the string's contents.
266                #[inline]
267                pub fn as_bytes(&self) -> &[u8] {
268                    &self.data[self.n..]
269                }
270
271                /// Decodes `s` as bytes.
272                pub fn decode<T: AsRef<[u8]>>(s: T) -> Result<[u8; $size], DecodeError> {
273                    let mut x = Uint::<{ $size/8 }, $size>::new();
274                    // Work 10 bytes at a time (see `RADIX`, etc).
275                    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                /// Encodes `b` as a Base58 string.
294                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/// An unsigned N-bit integer.
336///
337/// - `W` is the number of words
338/// - `B` is the number of bytes (`B = W*8`)
339///
340/// This should just be `N`, but `const_generic_exprs` is still
341/// +nightly.
342#[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    /// Decodes an integer from big-endian format.
353    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    /// Encodes the integer in big-endian format.
364    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    /// Reports whether `self == 0.`
375    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    /// Sets `*self = self*y + r` and reports whether the result
385    /// overflowed.
386    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    /// Computes `(q, r) = self/(58^10)` such that
395    ///
396    /// ```text
397    /// q = x/y
398    /// r = x - y*q
399    /// ```
400    ///
401    /// and sets `*self = q`.
402    fn quo_radix(&mut self) -> u64 {
403        /// 58^10
404        const RADIX: u64 = 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58;
405        // Reciprocal of `RADIX`.
406        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        // Compare words high to low.
419        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}