use std::collections::HashMap;
fn get_alphabet(hex: bool) -> &'static str {
if hex {
"0123456789ABCDEFGHIJKLMNOPQRSTUV"
} else {
"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
}
}
fn create_decode_map(alphabet: &str) -> HashMap<char, u8> {
alphabet
.chars()
.enumerate()
.map(|(i, c)| (c, i as u8))
.collect()
}
fn base32_encode(data: &[u8], alphabet: &str, padding: bool) -> String {
if data.is_empty() {
return String::new();
}
let mut result = String::with_capacity((data.len() * 8 + 4) / 5);
let mut buffer = 0u16;
let mut bits_left = 0;
for &byte in data {
buffer = (buffer << 8) | (byte as u16);
bits_left += 8;
while bits_left >= 5 {
let val = (buffer >> (bits_left - 5)) & 0x1F;
result.push(alphabet.chars().nth(val as usize).unwrap());
bits_left -= 5;
}
}
if bits_left > 0 {
buffer <<= 5 - bits_left;
let val = (buffer & 0x1F) as usize;
result.push(alphabet.chars().nth(val).unwrap());
}
if padding {
while (result.len() % 8) != 0 {
result.push('=');
}
}
result
}
fn base32_decode(data: &str, alphabet: &str) -> Result<Vec<u8>, String> {
let decode_map = create_decode_map(alphabet);
let mut result = Vec::new();
let mut buffer = 0u16;
let mut bits_left = 0;
for c in data.chars().filter(|&c| c != '=') {
let val = decode_map
.get(&c.to_ascii_uppercase())
.ok_or_else(|| format!("Invalid Base32 character: {}", c))?;
buffer = (buffer << 5) | (*val as u16);
bits_left += 5;
if bits_left >= 8 {
result.push((buffer >> (bits_left - 8)) as u8);
bits_left -= 8;
}
}
if bits_left > 0 && bits_left >= 5 {
buffer <<= 8 - bits_left;
result.push((buffer >> (bits_left - 8)) as u8);
}
Ok(result)
}
#[derive(Default)]
pub struct Base32;
impl Base32 {
pub fn encode(data: &[u8], padding: Option<bool>) -> String {
let alphabet = get_alphabet(false);
base32_encode(data, alphabet, padding.unwrap_or(true))
}
pub fn decode(data: &str) -> Result<Vec<u8>, String> {
let alphabet = get_alphabet(false);
base32_decode(data, alphabet)
}
}
#[derive(Default)]
pub struct Base32Hex;
impl Base32Hex {
pub fn encode(data: &[u8], padding: Option<bool>) -> String {
let alphabet = get_alphabet(true);
base32_encode(data, alphabet, padding.unwrap_or(true))
}
pub fn decode(data: &str) -> Result<Vec<u8>, String> {
let alphabet = get_alphabet(true);
base32_decode(data, alphabet)
}
}