1use num::bigint::ToBigUint;
2use num::{BigUint, Zero, One};
3use num::traits::ToPrimitive;
4use std::fmt;
5
6pub use self::FromBase58Error::*;
7
8static BTC_ALPHA: &'static[u8] = b"123456789\
9 ABCDEFGHJKLMNPQRSTUVWXYZ\
10 abcdefghijkmnopqrstuvwxyz";
11
12static FLICKR_ALPHA: &'static[u8] = b"123456789\
13 abcdefghijkmnopqrstuvwxyz\
14 ABCDEFGHJKLMNPQRSTUVWXYZ";
15
16pub trait FromBase58 {
20 fn from_base58(&self) -> Result<Vec<u8>, FromBase58Error>;
23}
24
25
26#[derive(Clone, Copy)]
28pub enum FromBase58Error {
29 InvalidBase58Byte(u8, usize),
31}
32
33impl fmt::Debug for FromBase58Error {
34 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35 match *self {
36 InvalidBase58Byte(ch, idx) =>
37 write!(f, "Invalid character '{}' at position {}", ch, idx),
38 }
39 }
40}
41
42impl fmt::Display for FromBase58Error {
43 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44 fmt::Debug::fmt(&self, f)
45 }
46}
47
48
49
50impl FromBase58 for str {
51 fn from_base58(&self) -> Result<Vec<u8>, FromBase58Error> {
52 self.as_bytes().from_base58()
53 }
54}
55
56impl FromBase58 for [u8] {
57 fn from_base58(&self) -> Result<Vec<u8>, FromBase58Error> {
60 let radix = 58.to_biguint().unwrap();
61 let mut x: BigUint = Zero::zero();
62 let mut rad_mult: BigUint = One::one();
63
64 for (idx, &byte) in self.iter().enumerate().rev() {
66 let first_idx = BTC_ALPHA.iter()
67 .enumerate()
68 .find(|x| *x.1 == byte)
69 .map(|x| x.0);
70 match first_idx {
71 Some(i) => { x = x + i.to_biguint().unwrap() * &rad_mult; },
72 None => return Err(InvalidBase58Byte(self[idx], idx))
73 }
74
75 rad_mult = &rad_mult * &radix;
76 }
77
78 let mut r = Vec::with_capacity(self.len());
79 for _ in self.iter().take_while(|&x| *x == BTC_ALPHA[0]) {
80 r.push(0);
81 }
82 if x > Zero::zero() {
83 r.extend(x.to_bytes_be());
85 }
86 Ok(r)
87 }
88}
89
90
91pub trait ToBase58 {
93 fn to_base58(&self) -> String;
96}
97
98impl ToBase58 for [u8] {
99 fn to_base58(&self) -> String {
107 let radix = 58.to_biguint().unwrap();
108 let mut x = BigUint::from_bytes_be(&self);
109 let mut ans = vec![];
110 while x > Zero::zero() {
111 let rem = (&x % &radix).to_usize().unwrap();
112 ans.push(BTC_ALPHA[rem]);
113 x = &x / &radix;
114 }
115
116 for _ in self.iter().take_while(|&x| *x == 0) {
118 ans.push(BTC_ALPHA[0]);
119 }
120 ans.reverse();
121 String::from_utf8(ans).unwrap()
122 }
123}
124
125
126#[cfg(test)]
127mod tests {
128 use base58::{FromBase58, ToBase58};
129
130 #[test]
131 fn test_from_base58_basic() {
132 assert_eq!("".from_base58().unwrap(), b"");
133 assert_eq!("Z".from_base58().unwrap(), &[32]);
134 assert_eq!("n".from_base58().unwrap(), &[45]);
135 assert_eq!("q".from_base58().unwrap(), &[48]);
136 assert_eq!("r".from_base58().unwrap(), &[49]);
137 assert_eq!("z".from_base58().unwrap(), &[57]);
138 assert_eq!("4SU".from_base58().unwrap(), &[45, 49]);
139 assert_eq!("4k8".from_base58().unwrap(), &[49, 49]);
140 assert_eq!("ZiCa".from_base58().unwrap(), &[97, 98, 99]);
141 assert_eq!("3mJr7AoUXx2Wqd".from_base58().unwrap(), b"1234598760");
142 assert_eq!("3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f".from_base58().unwrap(), b"abcdefghijklmnopqrstuvwxyz");
143 }
144
145 #[test]
146 fn test_from_base58_bytes() {
147 assert_eq!(b"ZiCa".from_base58().unwrap(), b"abc");
148 }
149
150 #[test]
151 fn test_from_base58_invalid_char() {
152 assert!("0".from_base58().is_err());
153 assert!("O".from_base58().is_err());
154 assert!("I".from_base58().is_err());
155 assert!("l".from_base58().is_err());
156 assert!("3mJr0".from_base58().is_err());
157 assert!("O3yxU".from_base58().is_err());
158 assert!("3sNI".from_base58().is_err());
159 assert!("4kl8".from_base58().is_err());
160 assert!("s!5<".from_base58().is_err());
161 assert!("t$@mX<*".from_base58().is_err());
162 }
163
164 #[test]
165 fn test_from_base58_initial_zeros() {
166 assert_eq!("1ZiCa".from_base58().unwrap(), b"\0abc");
167 assert_eq!("11ZiCa".from_base58().unwrap(), b"\0\0abc");
168 assert_eq!("111ZiCa".from_base58().unwrap(), b"\0\0\0abc");
169 assert_eq!("1111ZiCa".from_base58().unwrap(), b"\0\0\0\0abc");
170 }
171
172 #[test]
173 fn test_to_base58_basic() {
174 assert_eq!(b"".to_base58(), "");
175 assert_eq!(&[32].to_base58(), "Z");
176 assert_eq!(&[45].to_base58(), "n");
177 assert_eq!(&[48].to_base58(), "q");
178 assert_eq!(&[49].to_base58(), "r");
179 assert_eq!(&[57].to_base58(), "z");
180 assert_eq!(&[45, 49].to_base58(), "4SU");
181 assert_eq!(&[49, 49].to_base58(), "4k8");
182 assert_eq!(b"abc".to_base58(), "ZiCa");
183 assert_eq!(b"1234598760".to_base58(), "3mJr7AoUXx2Wqd");
184 assert_eq!(b"abcdefghijklmnopqrstuvwxyz".to_base58(), "3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f");
185 }
186
187 #[test]
188 fn test_to_base58_initial_zeros() {
189 assert_eq!(b"\0abc".to_base58(), "1ZiCa");
190 assert_eq!(b"\0\0abc".to_base58(), "11ZiCa");
191 assert_eq!(b"\0\0\0abc".to_base58(), "111ZiCa");
192 assert_eq!(b"\0\0\0\0abc".to_base58(), "1111ZiCa");
193 }
194
195 #[test]
196 fn test_base58_random() {
197 use rand::{thread_rng, Rng};
198
199 for _ in 0..200 {
200 let times = thread_rng().gen_range(1, 100);
201 let v = thread_rng().gen_iter::<u8>().take(times)
202 .collect::<Vec<_>>();
203 assert_eq!(v.to_base58()
204 .from_base58()
205 .unwrap(),
206 v);
207 }
208 }
209}