use crate::Error::InsufficientTargetSpace;
use crate::base_64::Base64Encoder;
use crate::base_64::decode::decode_block::decode_block;
use crate::base_64::decode::decode_block_last::decode_block_last;
use crate::base_64::decode::decoded_len::decoded_len;
use crate::base_64::decode::decoding_table::DecodingTable;
use crate::base_64::decode::split_last_block::split_last_block;
use crate::{Decoder, Error};
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub struct Base64Decoder {
table: DecodingTable,
padding: Option<u8>,
}
impl Base64Decoder {
const BLOCK_SIZE: usize = 4;
}
impl Base64Decoder {
pub fn new(v63: u8, v64: u8, padding: Option<u8>) -> Option<Self> {
if Base64Encoder::is_valid_config(v63, v64, padding) {
Some(Self {
table: DecodingTable::get_decoding_table(v63, v64),
padding,
})
} else {
None
}
}
}
impl Base64Decoder {
pub fn url_safe_decoder() -> Self {
Self::new(
Base64Encoder::URL_SAFE_V63,
Base64Encoder::URL_SAFE_V64,
Base64Encoder::URL_SAFE_PADDING,
)
.unwrap()
}
}
impl Default for Base64Decoder {
fn default() -> Self {
Self {
table: DecodingTable::default(),
padding: Base64Encoder::DEFAULT_PADDING,
}
}
}
impl Decoder for Base64Decoder {
fn decoded_len(&self, data: &[u8]) -> Result<usize, Error> {
Ok(decoded_len(data, self.padding))
}
fn decode_to_slice(&self, data: &[u8], target: &mut [u8]) -> Result<usize, Error> {
let decoded_len: usize = self.decoded_len(data)?;
if decoded_len > target.len() {
Err(InsufficientTargetSpace)
} else {
let target: &mut [u8] = &mut target[..decoded_len];
let table: &[u8; 256] = self.table.decoding_table();
let (full_blocks, last_block) = split_last_block(data);
let mut d: usize = 0;
let mut t: usize = 0;
for _ in 0..(full_blocks.len() / Self::BLOCK_SIZE) {
t += unsafe { decode_block(table, &full_blocks[d..], &mut target[t..]) };
d += Self::BLOCK_SIZE;
}
t += unsafe { decode_block_last(table, self.padding, last_block, &mut target[t..]) };
debug_assert_eq!(decoded_len, t);
Ok(decoded_len)
}
}
}
#[cfg(test)]
#[cfg(feature = "dev")]
mod tests {
use crate::base_64::Base64Decoder;
use crate::test::test_decoder;
#[test]
fn decode() {
let test_cases: &[(&str, &[u8])] = &[
("", b""),
("AAAA", b"\x00\x00\x00"),
("89+/", b"\xF3\xDF\xBF"),
("////", b"\xFF\xFF\xFF"),
("AAAAAAAA", b"\x00\x00\x00\x00\x00\x00"),
("////////", b"\xFF\xFF\xFF\xFF\xFF\xFF"),
("AA==", b"\x00"),
("Ag==", b"\x02"),
("/w==", b"\xFF"),
("AAA=", b"\x00\x00"),
("AAE=", b"\x00\x01"),
("//8=", b"\xFF\xFF"),
("/////", b"\xFF\xFF\xFF\xFC"),
("//////", b"\xFF\xFF\xFF\xFF"),
("//////=", b"\xFF\xFF\xFF\xFF"),
("//////==", b"\xFF\xFF\xFF\xFF"),
("///////", b"\xFF\xFF\xFF\xFF\xFF"),
("///////=", b"\xFF\xFF\xFF\xFF\xFF"),
];
let decoder: Base64Decoder = Base64Decoder::default();
test_decoder(&decoder, test_cases);
}
}