use std::collections::HashMap;
fn get_alphabet(url_safe: bool) -> &'static str {
if url_safe {
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
} else {
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
}
}
fn create_decode_map(alphabet: &str) -> HashMap<char, u8> {
alphabet
.chars()
.enumerate()
.map(|(i, c)| (c, i as u8))
.collect()
}
fn base64_encode(data: &[u8], alphabet: &str, padding: bool) -> String {
let mut result = String::new();
let mut buffer = 0u32;
let mut shift = 0;
for &byte in data {
buffer = (buffer << 8) | (byte as u32);
shift += 8;
while shift >= 6 {
shift -= 6;
result.push(
alphabet
.chars()
.nth(((buffer >> shift) & 0x3f) as usize)
.unwrap(),
);
}
}
if shift > 0 {
result.push(
alphabet
.chars()
.nth(((buffer << (6 - shift)) & 0x3f) as usize)
.unwrap(),
);
}
if padding {
let pad_count = (4 - (result.len() % 4)) % 4;
result.extend(std::iter::repeat('=').take(pad_count));
}
result
}
fn base64_decode(data: &str, alphabet: &str) -> Result<Vec<u8>, String> {
let decode_map = create_decode_map(alphabet);
let mut result = Vec::new();
let mut buffer = 0u32;
let mut bits_collected = 0;
for c in data.chars().take_while(|&c| c != '=') {
let value = decode_map
.get(&c)
.ok_or_else(|| format!("Invalid Base64 character: {}", c))?;
buffer = (buffer << 6) | (*value as u32);
bits_collected += 6;
if bits_collected >= 8 {
bits_collected -= 8;
result.push(((buffer >> bits_collected) & 0xff) as u8);
}
}
Ok(result)
}
#[derive(Default)]
pub struct Base64;
impl Base64 {
pub fn encode(data: &[u8], padding: Option<bool>) -> String {
let alphabet = get_alphabet(false);
base64_encode(data, alphabet, padding.unwrap_or(true))
}
pub fn decode(data: &str) -> Result<Vec<u8>, String> {
let url_safe = data.contains('-') || data.contains('_');
let alphabet = get_alphabet(url_safe);
base64_decode(data, alphabet)
}
pub fn encode_string(data: &str, padding: Option<bool>) -> String {
Self::encode(data.as_bytes(), padding)
}
pub fn decode_string(data: &str) -> Result<String, String> {
let bytes = Self::decode(data)?;
String::from_utf8(bytes).map_err(|e| format!("Invalid UTF-8 sequence: {}", e))
}
}
#[derive(Default)]
pub struct Base64Url;
impl Base64Url {
pub fn encode(data: &[u8], padding: Option<bool>) -> String {
let alphabet = get_alphabet(true);
base64_encode(data, alphabet, padding.unwrap_or(true))
}
pub fn decode(data: &str) -> Result<Vec<u8>, String> {
let alphabet = get_alphabet(true);
base64_decode(data, alphabet)
}
pub fn encode_string(data: &str, padding: Option<bool>) -> String {
Self::encode(data.as_bytes(), padding)
}
pub fn decode_string(data: &str) -> Result<String, String> {
let bytes = Self::decode(data)?;
String::from_utf8(bytes).map_err(|e| format!("Invalid UTF-8 sequence: {}", e))
}
}