1use std::collections::BTreeMap;
2
3use anyhow::Context;
4
5const RFC4648: [u8; 32] = *b"0123456789abcdefghijklmnopqrstuv";
7
8pub fn encode(input: &[u8]) -> String {
10 let mut output = Vec::with_capacity(((8 * input.len()) / 5) + 1);
11
12 let mut buffer = 0;
13 let mut bits = 0;
14
15 for byte in input {
16 buffer |= (*byte as usize) << bits;
17 bits += 8;
18
19 while bits >= 5 {
20 output.push(RFC4648[buffer & 0b11111]);
21
22 buffer >>= 5;
23 bits -= 5;
24 }
25 }
26
27 if bits > 0 {
28 output.push(RFC4648[buffer & 0b11111]);
29 }
30
31 String::from_utf8(output).unwrap()
32}
33
34pub fn decode(input: &str) -> anyhow::Result<Vec<u8>> {
37 let decode_table = RFC4648
38 .iter()
39 .enumerate()
40 .map(|(i, c)| (*c, i))
41 .collect::<BTreeMap<u8, usize>>();
42
43 let mut output = Vec::with_capacity(((5 * input.len()) / 8) + 1);
44
45 let mut buffer = 0;
46 let mut bits = 0;
47
48 for byte in input.as_bytes() {
49 let value = decode_table
50 .get(byte)
51 .copied()
52 .context("Invalid character encountered")?;
53
54 buffer |= value << bits;
55 bits += 5;
56
57 while bits >= 8 {
58 output.push((buffer & 0xFF) as u8);
59
60 buffer >>= 8;
61 bits -= 8;
62 }
63 }
64
65 Ok(output)
66}
67
68#[test]
69fn test_base_32_roundtrip() {
70 let data: [u8; 10] = [0x50, 0xAB, 0x3F, 0x77, 0x01, 0xCD, 0x55, 0xFE, 0x10, 0x99];
71
72 for n in 1..10 {
73 assert_eq!(decode(&encode(&data[0..n])).unwrap(), data[0..n]);
74 }
75}