1const BASE32_ALPHABET: &[u8; 32] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
4
5pub fn encode(data: &[u8]) -> String {
14 let mut encoded = String::with_capacity((data.len() * 8).div_ceil(5));
15
16 let mut buffer = 0_u16;
17 let mut bits_left = 0;
18
19 for &byte in data {
20 buffer <<= 8;
21 buffer |= byte as u16;
22 bits_left += 8;
23
24 while bits_left >= 5 {
25 let index = (buffer >> (bits_left - 5)) & 0x1F;
26 encoded.push(BASE32_ALPHABET[index as usize] as char);
27 bits_left -= 5;
28 }
29 }
30
31 if bits_left > 0 {
32 let index = (buffer << (5 - bits_left)) & 0x1F;
33 encoded.push(BASE32_ALPHABET[index as usize] as char);
34 }
35
36 encoded
37}
38
39pub fn decode(data: &str) -> Result<Vec<u8>, DecodeBase32Error> {
53 let mut output = Vec::with_capacity((data.len() * 5) / 8);
54
55 let mut buffer = 0_u32;
56 let mut bits_left = 0;
57
58 for b in data.bytes() {
59 let val = match b {
60 b'A'..=b'Z' => b - b'A',
61 b'a'..=b'z' => b - b'a',
62 b'2'..=b'7' => b - b'2' + 26,
63 _ => return Err(DecodeBase32Error::InvalidChar(b as char)),
64 } as u32;
65
66 buffer = (buffer << 5) | val;
67 bits_left += 5;
68
69 if bits_left >= 8 {
70 output.push((buffer >> (bits_left - 8)) as u8);
71 bits_left -= 8;
72 }
73 }
74
75 Ok(output)
76}
77
78#[derive(Debug, Clone)]
79pub enum DecodeBase32Error {
80 InvalidChar(char),
81}
82
83impl std::fmt::Display for DecodeBase32Error {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 match self {
86 DecodeBase32Error::InvalidChar(c) => write!(f, "invalid base32 character: '{c}'"),
87 }
88 }
89}
90
91impl std::error::Error for DecodeBase32Error {}