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 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 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 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
120fn 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}