use core::num::NonZeroUsize;
use super::{
buffered_decode_hooks::BufferedDecodeHooks,
decode_action::DecodeAction,
decode_context::DecodeContext,
decode_state::DecodeState,
decode_step::DecodeStep,
finish_error::FinishError,
transcode_progress::TranscodeProgress,
};
use crate::{
CapacityError,
Codec,
codec::assert_unit_bounds,
};
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
pub struct BufferedDecodeEngine<C, H> {
pub(super) codec: C,
pub(super) hooks: H,
}
impl<C, H> BufferedDecodeEngine<C, H>
where
C: Codec,
H: BufferedDecodeHooks<C>,
{
#[must_use]
#[inline(always)]
pub const fn new(codec: C, hooks: H) -> Self {
Self { codec, hooks }
}
#[must_use = "capacity planning can fail on overflow"]
#[inline(always)]
pub fn max_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
assert_unit_bounds::<C>(&self.codec);
self.hooks.max_output_len(&self.codec, input_len)
}
#[must_use]
#[inline(always)]
pub fn max_finish_output_len(&self) -> usize {
self.hooks.max_finish_output_len(&self.codec)
}
#[inline(always)]
pub fn reset(&mut self) {
self.hooks.reset(&self.codec);
}
pub fn transcode(
&mut self,
input: &[C::Unit],
input_index: usize,
output: &mut [C::Value],
output_index: usize,
) -> Result<TranscodeProgress, H::Error> {
if input_index > input.len() {
return Err(self.hooks.invalid_input_index(&self.codec, input_index, input.len()));
}
if output_index > output.len() {
return Err(self.hooks.invalid_output_index(&self.codec, output_index, output.len()));
}
assert_unit_bounds::<C>(&self.codec);
let mut state = DecodeState::new(input, input_index, output, output_index);
while state.has_input() {
let context = state.context();
let step = self.decode_step(state.input(), context)?;
if let Some(progress) = step.apply_to_decode_state(&mut state) {
return Ok(progress);
}
}
Ok(state.complete_progress())
}
pub fn finish(&mut self, output: &mut [C::Value], output_index: usize) -> Result<usize, FinishError<H::Error>> {
let required = self.max_finish_output_len();
FinishError::ensure_output_capacity(output.len(), output_index, required)?;
let output_end = output_index + required;
let output = &mut output[..output_end];
let written = self
.hooks
.finish(&self.codec, output, output_index)
.map_err(FinishError::source)?;
assert!(
written <= required,
"BufferedDecodeEngine hook wrote beyond its finish bound",
);
Ok(written)
}
#[inline(always)]
pub(crate) unsafe fn decode_unchecked_at(
&self,
input: &[C::Unit],
input_index: usize,
) -> Result<(C::Value, NonZeroUsize), C::DecodeError> {
unsafe { self.codec.decode_unchecked(input, input_index) }
}
#[inline(always)]
pub(crate) fn handle_decode_error(
&mut self,
error: C::DecodeError,
context: DecodeContext,
) -> Result<DecodeAction<C::Value>, H::Error> {
self.hooks.handle_decode_error(&self.codec, error, context)
}
#[inline]
pub(super) fn decode_step(
&mut self,
input: &[C::Unit],
context: DecodeContext,
) -> Result<DecodeStep<C::Value>, H::Error> {
let min_units = self.codec.min_units_per_value().get();
if context.available < min_units {
let additional = NonZeroUsize::new(min_units - context.available).expect("missing input is non-zero");
return Ok(DecodeStep::need_input(additional, context.available));
}
let result = unsafe { self.decode_unchecked_at(input, context.input_index) };
self.handle_decode_result(context, result)
}
#[inline]
fn handle_decode_result(
&mut self,
context: DecodeContext,
result: Result<(C::Value, NonZeroUsize), C::DecodeError>,
) -> Result<DecodeStep<C::Value>, H::Error> {
match result {
Ok((value, consumed)) => {
assert!(
consumed.get() <= context.available,
"Codec::decode_unchecked consumed beyond available input",
);
Ok(DecodeStep::decoded(value, consumed, context.input_index))
}
Err(error) => {
let action = self.handle_decode_error(error, context)?;
Ok(action.into_step(context.input_index, context.available))
}
}
}
}