#![allow(unsafe_code)]
use super::lenient::Quantum;
use crate::{
base64::{Base64, alphabet::INVALID},
buffer::SpareCapacity,
};
const CHUNK: usize = 4096;
#[derive(Debug, Clone, Copy)]
pub struct Decoder {
engine: Base64,
quantum: Quantum,
}
impl Base64 {
pub fn decoder(&self) -> Decoder {
Decoder {
engine: *self,
quantum: Quantum::default(),
}
}
}
impl Decoder {
pub fn decode_symbols(&mut self, input: &[u8], out: &mut Vec<u8>) -> usize {
let tables = self.engine.decode_tables();
let mut consumed = 0;
loop {
let rest = input.get(consumed..).unwrap_or_default();
let chunk = rest.get(..CHUNK).unwrap_or(rest);
let mut read = 0;
unsafe {
out.append_with(chunk.len() / 4 * 3 + 3, |dst| {
let mut written = 0;
loop {
if self.quantum.is_empty() {
let (done, out) = tables.decode_quads(
chunk.get(read..).unwrap_or_default(),
dst.get_mut(written..).unwrap_or_default(),
);
read += done;
written += out;
}
let Some(&byte) = chunk.get(read) else {
break;
};
let value = tables.decode[byte as usize];
if value == INVALID {
break;
}
written += self.quantum.push(value, dst, written);
read += 1;
}
written
})
};
consumed += read;
if read < CHUNK {
return consumed;
}
}
}
pub fn pad(&mut self, out: &mut Vec<u8>) {
unsafe { out.append_with(2, |dst| self.quantum.flush(dst)) };
}
pub fn finish(mut self, out: &mut Vec<u8>) {
self.pad(out);
}
pub fn is_aligned(&self) -> bool {
self.quantum.is_empty()
}
}