basex_rs/
lib.rs

1use std::collections::HashMap;
2
3const LEN: usize = 58;
4pub const ALPHABET_BITCOIN: &[u8; LEN] =
5    b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
6pub const ALPHABET_RIPPLE: &[u8; LEN] =
7    b"rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz";
8pub const ALPHABET_FLICKR: &[u8; LEN] =
9    b"123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
10pub const ALPHABET_SKYWELL: &[u8; LEN] =
11    b"jpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65rkm8oFqi1tuvAxyz";
12
13pub struct BaseX<'a> {
14    pub alphabet: &'a [u8],
15}
16
17impl<'a> BaseX<'a> {
18    pub fn with_alphabet(alphabet: &'a [u8]) -> Self {
19        BaseX { alphabet }
20    }
21
22    pub fn to_bs58(&self, input: &[u8]) -> String {
23        let base = self.alphabet.len() as u16;
24
25        let mut digits: Vec<u16> = vec![0u16; 1];
26
27        let mut i = 0;
28        while i < input.len() {
29            let mut j = 0;
30            let mut carry: u16 = input[i] as u16;
31
32            let digits_len = digits.len();
33            while j < digits_len {
34                carry += digits.as_slice()[j] << 8;
35
36                digits.as_mut_slice()[j] = carry % base;
37
38                carry /= base;
39
40                j += 1;
41            }
42
43            while carry > 0 {
44                digits.push(carry % base);
45                carry /= base;
46            }
47
48            i += 1;
49        }
50
51        let mut output = "".to_string();
52
53        // deal with leading zeros
54        let mut k = 0;
55        while input[k] == 0 && k < input.len() - 1 {
56            output.push(self.alphabet[0] as char);
57
58            k += 1;
59        }
60
61        // convert digits to a string
62        let mut q: i32 = (digits.len() - 1) as i32;
63        while q >= 0 {
64            let uu: u8 = self.alphabet[digits[q as usize] as usize];
65            let xx = uu as char;
66
67            output.push(xx);
68
69            q -= 1;
70        }
71
72        output
73    }
74
75    pub fn from_bs58(&self, input: &String) -> Option<Vec<u8>> {
76        if input.is_empty() {
77            return None;
78        }
79
80        let alphabet_map = lookup_table(self.alphabet);
81        let base = self.alphabet.len() as u16;
82        let ledger = self.alphabet[0] as char;
83
84        let mut bytes: Vec<u8> = vec![];
85        let mut i = 0;
86        while i < input.len() {
87            let c = input.as_str().chars().nth(i)?;
88            let val = alphabet_map.get(&c)?;
89            let mut j = 0;
90            let mut carry: u16 = *val as u16;
91            while j < bytes.len() {
92                carry += bytes[j] as u16 * base;
93                bytes[j] = carry as u8;
94                carry >>= 8;
95
96                j += 1;
97            }
98
99            while carry > 0 {
100                bytes.push(carry as u8);
101                carry >>= 8;
102            }
103
104            i += 1;
105        }
106
107        // deal with leading zeros
108        let mut k = 0;
109        while input.as_str().chars().nth(k).unwrap() == ledger && k < input.len() - 1 {
110            bytes.push(0);
111            k += 1;
112        }
113
114        bytes.as_mut_slice().reverse();
115
116        Some(bytes)
117    }
118}
119
120/// Pre-compute lookup table
121fn lookup_table(alphabet: &[u8]) -> HashMap<char, usize> {
122    let mut map: HashMap<char, usize> = HashMap::new();
123    alphabet.iter().enumerate().for_each(|(idx, key)| {
124        map.insert(*key as char, idx);
125    });
126
127    map
128}
129
130#[cfg(test)]
131mod test_set {
132    use super::*;
133
134    #[test]
135    fn to_bs58_works() {
136        let src = vec![28, 215, 33, 155];
137        let encoded = BaseX::with_alphabet(ALPHABET_BITCOIN).to_bs58(&src);
138        assert_eq!(encoded, "jkuzA".to_string());
139    }
140
141    #[test]
142    fn from_bs58_works() {
143        let src = "jkuzA".to_string();
144        let decoded = BaseX::with_alphabet(ALPHABET_BITCOIN).from_bs58(&src);
145        assert_eq!(decoded, Some(vec![28, 215, 33, 155]));
146    }
147}