1mod error;
2
3pub use error::DecodeError;
4
5static ALPHABET: [char; 32] = [
6 '0', '1', '2', '3', '4', '5', '6', '7',
7 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
8 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q',
9 'r', 's', 't', 'v', 'w', 'x', 'y', 'z',
10];
11
12const LOOKUP_TABLE_LENGTH: u8 = 38;
13static REVERSE_ALPHABET: [u8; LOOKUP_TABLE_LENGTH as usize] = [
15 24, 25, 26, 255, 27, 28, 29, 30, 31, 255, 0, 1, 2,
16 3, 4, 5, 6, 7, 8, 9, 255, 10, 11, 12, 13, 14,
17 15, 16, 17, 255, 18, 19, 255, 20, 21, 255, 22, 23
18];
19
20pub const SHORT_LENGTH: usize = 22;
21
22#[inline]
24fn lookup(value: u8) -> u8 {
25 let value = value % LOOKUP_TABLE_LENGTH;
26 REVERSE_ALPHABET[value as usize]
27}
28
29pub fn base32_decode(input: &str) -> Result<[u8; 16], DecodeError> {
30 if input.len() != 27 {
31 return Err(DecodeError::InvalidLength);
32 }
33 let input: [u8; 27] = input.as_bytes().try_into().unwrap();
34 if !input[22] == b'_' {
35 return Err(DecodeError::NoSeparator);
36 }
37
38 let mut intermediate = [0u8; 26];
40 for i in 0..22 {
41 intermediate[i] = lookup(input[i]);
42 }
43 for i in 23..27 {
44 intermediate[i - 1] = lookup(input[i]);
45 }
46
47 let mut combined = 0u8;
51 for &c in &intermediate {
52 combined |= c;
53 }
54 let has_invalid = combined == 255;
55 if has_invalid {
56 let mut idx255 = intermediate.iter().position(|&c| c == 255).unwrap();
57 if idx255 >= 22 {
58 idx255 += 1;
59 }
60 let c = input[idx255];
61 return Err(DecodeError::InvalidCharacter(c as char));
62 }
63
64 let mut result = [0u8; 16];
65 for i in 0..3 {
67 let j = i * 8;
68 let k = i * 5;
69 let d0 = intermediate[j];
70 let d1 = intermediate[j + 1];
71 let d2 = intermediate[j + 2];
72 let d3 = intermediate[j + 3];
73 let d4 = intermediate[j + 4];
74 let d5 = intermediate[j + 5];
75 let d6 = intermediate[j + 6];
76 let d7 = intermediate[j + 7];
77 let d8 = intermediate[j + 8];
78
79 result[k] = d0 << 5 | d1;
80 result[k + 1] = d2 << 3 | (d3 >> 2);
81 result[k + 2] = d3 << 6 | (d4 << 1) | (d5 >> 4);
82 result[k + 3] = d5 << 4 | (d6 >> 1);
83 result[k + 4] = d6 << 7 | (d7 << 2) | (d8 >> 3);
84 }
85
86 result[15] = intermediate[24] << 5 | intermediate[25];
87
88 Ok(result)
89}
90
91#[inline]
92fn alphabet(i: u8) -> char {
93 ALPHABET[(i & 0x1F) as usize]
97}
98
99pub fn base32_encode(data: [u8; 16]) -> String {
100 let mut encoded = String::with_capacity(27);
101 encoded.push(alphabet(data[0] >> 5));
103 encoded.push(alphabet(data[0]));
105 encoded.push(alphabet(data[1] >> 3));
107 encoded.push(alphabet(data[1] << 2 | data[2] >> 6));
109 encoded.push(alphabet(data[2] >> 1));
111 encoded.push(alphabet(data[2] << 4 | data[3] >> 4));
113 encoded.push(alphabet(data[3] << 1 | data[4] >> 7));
115 encoded.push(alphabet(data[4] >> 2));
117 encoded.push(alphabet(data[4] << 3 | data[5] >> 5));
119 encoded.push(alphabet(data[5]));
121 encoded.push(alphabet(data[6] >> 3));
123 encoded.push(alphabet(data[6] << 2 | data[7] >> 6));
125 encoded.push(alphabet(data[7] >> 1));
127 encoded.push(alphabet(data[7] << 4 | data[8] >> 4));
129 encoded.push(alphabet(data[8] << 1 | data[9] >> 7));
131 encoded.push(alphabet(data[9] >> 2));
133 encoded.push(alphabet(data[9] << 3 | data[10] >> 5));
135 encoded.push(alphabet(data[10]));
137 encoded.push(alphabet(data[11] >> 3));
139 encoded.push(alphabet(data[11] << 2 | data[12] >> 6));
141 encoded.push(alphabet(data[12] >> 1));
143 encoded.push(alphabet(data[12] << 4 | data[13] >> 4));
145 encoded.push('_');
146 encoded.push(alphabet(data[13] << 1 | data[14] >> 7));
148 encoded.push(alphabet(data[14] >> 2));
150 encoded.push(alphabet(data[14] << 3 | data[15] >> 5));
152 encoded.push(alphabet(data[15]));
154 encoded
155}
156
157#[cfg(test)]
158mod test {
159 use super::*;
160
161 #[test]
194 fn test_rand() {
195 use rand::RngCore;
196 let mut rng = rand::thread_rng();
197 for _ in 0..3000000 {
198 let mut s = [0u8; 16];
199 rng.fill_bytes(&mut s);
200 let mut t = [0u8; 16];
201 rng.fill_bytes(&mut t);
202
203 let s_enc = base32_encode(s);
205 let dec = base32_decode(&s_enc).unwrap();
206 assert_eq!(dec, s);
207
208 let t_enc = base32_encode(t);
210 let dec = base32_decode(&t_enc).unwrap();
211 assert_eq!(dec, t);
212
213 if s < t {
214 assert_eq!(s_enc < t_enc, true);
215 } else {
216 assert_eq!(s_enc > t_enc, true);
217 }
218 }
219 }
220}