const BASE32_ALPHABET: &[u8; 32] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
pub fn encode(data: &[u8]) -> String {
let mut encoded = String::with_capacity((data.len() * 8).div_ceil(5));
let mut buffer = 0_u16;
let mut bits_left = 0;
for &byte in data {
buffer <<= 8;
buffer |= byte as u16;
bits_left += 8;
while bits_left >= 5 {
let index = (buffer >> (bits_left - 5)) & 0x1F;
encoded.push(BASE32_ALPHABET[index as usize] as char);
bits_left -= 5;
}
}
if bits_left > 0 {
let index = (buffer << (5 - bits_left)) & 0x1F;
encoded.push(BASE32_ALPHABET[index as usize] as char);
}
encoded
}
pub fn decode(data: &str) -> Result<Vec<u8>, DecodeBase32Error> {
let mut output = Vec::with_capacity((data.len() * 5) / 8);
let mut buffer = 0_u32;
let mut bits_left = 0;
for b in data.bytes() {
let val = match b {
b'A'..=b'Z' => b - b'A',
b'a'..=b'z' => b - b'a',
b'2'..=b'7' => b - b'2' + 26,
_ => return Err(DecodeBase32Error::InvalidChar(b as char)),
} as u32;
buffer = (buffer << 5) | val;
bits_left += 5;
if bits_left >= 8 {
output.push((buffer >> (bits_left - 8)) as u8);
bits_left -= 8;
}
}
Ok(output)
}
#[derive(Debug, Clone)]
pub enum DecodeBase32Error {
InvalidChar(char),
}
impl std::fmt::Display for DecodeBase32Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DecodeBase32Error::InvalidChar(c) => write!(f, "invalid base32 character: '{c}'"),
}
}
}
impl std::error::Error for DecodeBase32Error {}