1use num_bigint::{BigUint};
7use num_traits::{Zero, One, ToPrimitive};
8use num_integer::Integer;
9use failure::Fail;
10
11pub fn encode(bytes: &[u8]) -> String {
13 if bytes.is_empty() { return "".into() }
14
15 let mut input = vec![1u8];
16 input.extend_from_slice(bytes);
17
18 let mut result = String::new();
19 let mut val = BigUint::from_bytes_be(&input);
20 let base:BigUint = (BASE.to_owned() as u64).into();
21
22 while val > BigUint::zero() {
23 let remainder = val.mod_floor(&base).to_usize().unwrap_or(0);
24 result.push(ALPHABET[remainder]);
25 val /= &base;
26 }
27
28 result
29}
30
31pub fn decode(input: &str) -> Result<Vec<u8>, Error> {
33 let mut val:BigUint = BigUint::zero();
34 let mut base_mul = BigUint::one();
35 let base:BigUint = (BASE.to_owned() as u64).into();
36
37 for c in input.chars().into_iter() {
38 let remainder:BigUint = char_to_remainder(c)?.into();
39 val += remainder*&base_mul;
40 base_mul *= &base;
41 }
42
43 Ok(val.to_bytes_be()[1..].to_vec())
44}
45
46#[derive(Debug, Fail)]
47pub enum Error {
48 #[fail(display = "Invalid character '{}'", character)]
49 BadCharacter { character: char}
50}
51
52const BASE: usize = 62;
54const ALPHABET: [char; BASE] = [
55 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
56 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
57 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
58 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
59 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
60 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
61 'y', 'z',
62];
63
64fn char_to_remainder(c: char) -> Result<u64, Error> {
66 let i = match c {
67 '0'...'9' => ((c as u64) % ('0' as u64)),
68 'A'...'Z' => ((c as u64) % ('A' as u64)) + 10,
69 'a'...'z' => ((c as u64) % ('a' as u64)) + 36,
70 _ => return Err(Error::BadCharacter { character: c })
71 };
72
73 Ok(i)
74}
75
76#[test]
77fn test_encode() {
78 let cases:Vec<Vec<u8>> = vec![
79 vec![],
80 vec![0u8],
81 vec![1u8],
82 vec![0u8,0u8],
83 vec![0u8,1u8],
84 vec![1u8,0u8],
85 vec![1u8,1u8],
86 vec![0, 0, 0, 1],
87 [62u8; 10].to_vec(),
88 [63u8; 10].to_vec(),
89 [1u8; 10].to_vec(),
90 [0u8; 10].to_vec(),
91 "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt".as_bytes().to_vec(),
92 vec![0xDE, 0xAD, 0xBE, 0xEF],
93 ];
94
95 cases.into_iter().enumerate().for_each(|(case, input)| {
96 let encoded = encode(&input);
97 let decoded = decode(&encoded).expect("error decoding base62 input");
98 assert_eq!(input, decoded, "\nCase {}:\nbase62({})\nmismatch: \nencoded({:?}) != \ndecoded({:?})", case, encoded, input, decoded);
99 })
100}
101
102#[test]
103fn test_invalid() {
104 decode("abc-").expect_err("expected invalid '-'");
105 decode("wSBzv9UB5PeI/26").expect_err("expected invalid '/'");
106 decode("jSO+uL8").expect_err("expected invalid '+");
107}