use crate::{Transform, TransformError, TransformerCategory};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Base64Decode;
impl Transform for Base64Decode {
fn name(&self) -> &'static str {
"Base64 Decode"
}
fn id(&self) -> &'static str {
"base64decode"
}
fn description(&self) -> &'static str {
"Decode Base64 text to plain text"
}
fn category(&self) -> TransformerCategory {
TransformerCategory::Decoder
}
fn transform(&self, input: &str) -> Result<String, TransformError> {
let decoded = base64_decode(input).map_err(|_| TransformError::Base64DecodeError)?;
String::from_utf8(decoded).map_err(|_| TransformError::Utf8Error)
}
fn default_test_input(&self) -> &'static str {
"SGVsbG8sIFdvcmxkIQ=="
}
}
pub(crate) fn base64_decode(input: &str) -> Result<Vec<u8>, &'static str> {
fn create_lookup_table() -> [i8; 256] {
let mut table = [-1i8; 256];
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
.iter()
.enumerate()
.for_each(|(i, &c)| table[c as usize] = i as i8);
table
}
let lookup = create_lookup_table();
let input = input.trim().as_bytes();
let padding = input.iter().rev().take_while(|&&c| c == b'=').count();
let output_len = input.len() * 3 / 4 - padding;
let mut output = vec![0u8; output_len];
let mut output_index = 0;
for chunk in input.chunks(4) {
if chunk.len() < 2 {
return Err("Invalid base64 length");
}
let b0 = lookup[chunk[0] as usize];
let b1 = lookup[chunk[1] as usize];
if b0 < 0 || b1 < 0 {
return Err("Invalid base64 character");
}
if output_index < output_len {
output[output_index] = ((b0 << 2) | (b1 >> 4)) as u8;
output_index += 1;
}
if chunk.len() > 2 {
if chunk[2] == b'=' {
if chunk.len() < 4 || chunk[3] != b'=' {
return Err("Invalid base64 padding");
}
continue; }
let b2 = lookup[chunk[2] as usize];
if b2 < 0 {
return Err("Invalid base64 character");
}
if output_index < output_len {
output[output_index] = (((b1 & 0xF) << 4) | (b2 >> 2)) as u8;
output_index += 1;
}
if chunk.len() > 3 {
if chunk[3] == b'=' {
continue; }
let b3 = lookup[chunk[3] as usize];
if b3 < 0 {
return Err("Invalid base64 character");
}
if output_index < output_len {
output[output_index] = (((b2 & 0x3) << 6) | b3) as u8;
output_index += 1;
}
}
}
}
Ok(output)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_base64_decode() {
let transformer = Base64Decode;
assert_eq!(
transformer
.transform(transformer.default_test_input())
.unwrap(),
"Hello, World!"
);
assert_eq!(transformer.transform("").unwrap(), "");
assert_eq!(transformer.transform("YQ==").unwrap(), "a");
}
}