use core::num::NonZeroUsize;
use super::super::internal::{
decode_state::DecodeState,
decode_step::DecodeStep,
lifecycle::LifecycleGuard,
};
use crate::codec::assert_unit_bounds;
use crate::{
CapacityError,
Codec,
CodecDecodeError,
DecodeContext,
DecodeFailure,
TranscodeDecodeEngineError,
TranscodeDecodeHooks,
TranscodeError,
TranscodeProgress,
Transcoder,
};
type DecodeEngineErrorOf<C, H> = TranscodeDecodeEngineError<
<C as Codec>::DecodeError,
<H as TranscodeDecodeHooks<C>>::Error,
>;
#[derive(Debug, Default)]
pub struct TranscodeDecodeEngine<C, H> {
pub(super) codec: C,
pub(super) hooks: H,
lifecycle: LifecycleGuard,
}
impl<C, H> TranscodeDecodeEngine<C, H>
where
C: Codec,
H: TranscodeDecodeHooks<C>,
{
#[inline]
#[must_use]
pub fn new(codec: C, hooks: H) -> Self {
assert_unit_bounds::<C>();
Self {
codec,
hooks,
lifecycle: LifecycleGuard::new(),
}
}
#[must_use = "capacity planning can fail on overflow"]
#[inline(always)]
pub fn max_output_len(
&self,
input_len: usize,
) -> Result<usize, CapacityError> {
self.hooks.max_output_len(&self.codec, input_len)
}
#[must_use = "capacity planning can fail on overflow"]
#[inline(always)]
pub fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
C::MAX_DECODE_FLUSH_VALUES
.checked_add(self.hooks.max_finish_output_len(&self.codec))
.ok_or(CapacityError::OutputLengthOverflow)
}
#[inline(always)]
#[must_use = "capacity planning can fail on overflow"]
pub fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
Ok(C::MAX_DECODE_RESET_VALUES)
}
pub fn reset(
&mut self,
output: &mut [C::Value],
output_index: usize,
) -> Result<usize, TranscodeError<DecodeEngineErrorOf<C, H>>> {
self.lifecycle.on_reset();
let required = C::MAX_DECODE_RESET_VALUES;
TranscodeError::ensure_output_capacity(
output.len(),
output_index,
required,
)?;
self.hooks.reset_hooks(&mut self.codec);
let written = unsafe {
self.codec.decode_reset(output, output_index)
}
.map_err(|error| {
TranscodeError::domain(TranscodeDecodeEngineError::codec(
CodecDecodeError::decode_reset(error),
))
})?;
assert!(
written <= required,
"Codec::decode_reset wrote beyond its reset bound",
);
Ok(written)
}
pub fn transcode(
&mut self,
input: &[C::Unit],
input_index: usize,
output: &mut [C::Value],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeError<DecodeEngineErrorOf<C, H>>>
{
self.lifecycle.on_transcode();
TranscodeError::ensure_transcode_indices(
input.len(),
input_index,
output.len(),
output_index,
)?;
let min_units = C::MIN_UNITS_PER_VALUE;
let mut state =
DecodeState::new(input, input_index, output, output_index);
while state.has_input() {
let context = state.context();
let available = context.available();
if available < min_units.get() {
return Ok(state.need_input_progress_with(min_units, available));
}
if state.needs_output() {
return Ok(state.need_output_progress());
}
let step = self.decode_step(state.input(), context)?;
if let Some(progress) = state.apply_decode_step(step) {
return Ok(progress);
}
}
Ok(state.complete_progress())
}
pub fn finish(
&mut self,
output: &mut [C::Value],
output_index: usize,
) -> Result<usize, TranscodeError<DecodeEngineErrorOf<C, H>>> {
self.lifecycle.on_finish_attempt();
let required = self.max_finish_output_len()?;
TranscodeError::ensure_output_capacity(
output.len(),
output_index,
required,
)?;
let flushed = unsafe { self.codec.decode_flush(output, output_index) }
.map_err(|error| {
TranscodeError::domain(TranscodeDecodeEngineError::codec(
CodecDecodeError::decode_flush(error),
))
})?;
assert!(
flushed <= C::MAX_DECODE_FLUSH_VALUES,
"Codec::decode_flush wrote beyond its flush bound",
);
let written = self
.hooks
.finish_hooks(&mut self.codec, output, output_index + flushed)
.map_err(|error| {
TranscodeError::domain(TranscodeDecodeEngineError::hook(error))
})?;
assert!(
flushed + written <= required,
"TranscodeDecodeEngine hook wrote beyond its finish bound",
);
self.lifecycle.on_finish_success();
Ok(flushed + written)
}
#[inline(always)]
pub(super) fn decode_step(
&mut self,
input: &[C::Unit],
context: DecodeContext,
) -> Result<DecodeStep<C::Value>, TranscodeError<DecodeEngineErrorOf<C, H>>>
{
debug_assert!(
context.available() >= C::MIN_UNITS_PER_VALUE.get(),
"decode_step requires at least Codec::MIN_UNITS_PER_VALUE input units",
);
let result = unsafe { self.codec.decode(input, context.input_index()) };
self.handle_decode_result(context, result)
}
fn handle_decode_result(
&mut self,
context: DecodeContext,
result: Result<(C::Value, NonZeroUsize), DecodeFailure<C::DecodeError>>,
) -> Result<DecodeStep<C::Value>, TranscodeError<DecodeEngineErrorOf<C, H>>>
{
match result {
Ok((value, consumed)) => {
assert!(
consumed.get() <= context.available(),
"Codec::decode consumed beyond available input",
);
Ok(DecodeStep::decoded(value, consumed, context.input_index()))
}
Err(DecodeFailure::Incomplete { required_total }) => {
assert!(
required_total.get() > context.available(),
"Codec::decode incomplete required_total must exceed available input",
);
Ok(DecodeStep::need_input(required_total, context.available()))
}
Err(DecodeFailure::Invalid { source, consumed }) => {
let action = self
.hooks
.handle_invalid_decode(
&mut self.codec,
source,
consumed,
context,
)
.map_err(|error| {
TranscodeError::domain(
TranscodeDecodeEngineError::hook(error),
)
})?;
Ok(action.into_step(context.input_index(), context.available()))
}
}
}
}
impl<C, H> Transcoder<C::Unit, C::Value> for TranscodeDecodeEngine<C, H>
where
C: Codec,
H: TranscodeDecodeHooks<C>,
{
type Error = DecodeEngineErrorOf<C, H>;
#[inline(always)]
fn max_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
TranscodeDecodeEngine::max_output_len(self, input_len)
}
#[inline(always)]
fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
TranscodeDecodeEngine::max_finish_output_len(self)
}
#[inline(always)]
fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
TranscodeDecodeEngine::max_reset_output_len(self)
}
#[inline(always)]
fn reset(
&mut self,
output: &mut [C::Value],
output_index: usize,
) -> Result<usize, TranscodeError<Self::Error>> {
TranscodeDecodeEngine::reset(self, output, output_index)
}
#[inline(always)]
fn transcode(
&mut self,
input: &[C::Unit],
input_index: usize,
output: &mut [C::Value],
output_index: usize,
) -> core::result::Result<TranscodeProgress, TranscodeError<Self::Error>>
{
TranscodeDecodeEngine::transcode(
self,
input,
input_index,
output,
output_index,
)
}
#[inline(always)]
fn finish(
&mut self,
output: &mut [C::Value],
output_index: usize,
) -> Result<usize, TranscodeError<Self::Error>> {
TranscodeDecodeEngine::finish(self, output, output_index)
}
}