use super::{ForgivingBase64, ForgivingError};
use crate::v2::{BackendFault, Progress, Status};
impl ForgivingBase64 {
pub fn validate(self, input: &str) -> Result<(), ForgivingError> {
self.decoded_len(input).map(|_| ())
}
pub fn decoded_len(self, input: &str) -> Result<usize, ForgivingError> {
measure(input)
}
pub fn decode_into(self, input: &str, output: &mut [u8]) -> Result<usize, ForgivingError> {
let required = self.decoded_len(input)?;
if output.len() < required {
return Err(ForgivingError::OutputTooSmall {
required,
available: output.len(),
});
}
decode_validated(input, &mut output[..required])?;
Ok(required)
}
}
fn measure(input: &str) -> Result<usize, ForgivingError> {
let mut decoder = ForgivingBase64.decoder();
let mut offset = 0;
let mut total = 0usize;
let mut scratch = [0u8; 3];
while offset < input.len() {
let step = decoder.update(&input[offset..], &mut scratch)?;
let progress = step.progress();
require_progress(progress)?;
offset += progress.input_consumed();
total = total
.checked_add(progress.output_produced())
.ok_or(ForgivingError::PositionOverflow)?;
}
loop {
let step = decoder.finish(&mut scratch)?;
total = total
.checked_add(step.progress().output_produced())
.ok_or(ForgivingError::PositionOverflow)?;
match step.status() {
Status::Complete => return Ok(total),
Status::OutputFull(_) => require_output_progress(step.progress())?,
Status::NeedInput => return Err(impossible_state()),
}
}
}
fn decode_validated(input: &str, output: &mut [u8]) -> Result<(), ForgivingError> {
let mut decoder = ForgivingBase64.decoder();
let mut input_offset = 0;
let mut output_offset = 0;
while input_offset < input.len() {
let step = decoder.update(&input[input_offset..], &mut output[output_offset..])?;
let progress = step.progress();
require_progress(progress)?;
input_offset += progress.input_consumed();
output_offset += progress.output_produced();
}
loop {
let step = decoder.finish(&mut output[output_offset..])?;
output_offset += step.progress().output_produced();
match step.status() {
Status::Complete => return Ok(()),
Status::OutputFull(_) => require_output_progress(step.progress())?,
Status::NeedInput => return Err(impossible_state()),
}
}
}
fn require_progress(progress: Progress) -> Result<(), ForgivingError> {
if progress.input_consumed() == 0 && progress.output_produced() == 0 {
Err(impossible_state())
} else {
Ok(())
}
}
fn require_output_progress(progress: Progress) -> Result<(), ForgivingError> {
if progress.output_produced() == 0 {
Err(impossible_state())
} else {
Ok(())
}
}
const fn impossible_state() -> ForgivingError {
ForgivingError::Backend(BackendFault::ImpossibleState)
}