use core::num::NonZeroUsize;
use super::super::internal::{
decode_state::DecodeState,
decode_step::DecodeStep,
};
use crate::codec::assert_unit_bounds;
use crate::{
CapacityError,
Codec,
DecodeAction,
DecodeContext,
TranscodeDecodeHooks,
TranscodeError,
TranscodeProgress,
Transcoder,
};
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
pub struct TranscodeDecodeEngine<C, H> {
pub(super) codec: C,
pub(super) hooks: H,
}
impl<C, H> TranscodeDecodeEngine<C, H>
where
C: Codec,
H: TranscodeDecodeHooks<C>,
{
#[must_use]
#[inline]
pub fn new(codec: C, hooks: H) -> Self {
assert_unit_bounds::<C>(&codec);
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> {
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> {
self.codec
.max_decode_flush_values()
.checked_add(self.hooks.max_finish_output_len(&self.codec))
.ok_or(CapacityError::OutputLengthOverflow)
}
#[must_use]
#[inline(always)]
pub fn max_reset_output_len(&self) -> usize {
0
}
#[inline(always)]
pub fn reset(
&mut self,
output: &mut [C::Value],
output_index: usize,
) -> Result<usize, TranscodeError<H::Error>> {
TranscodeError::ensure_output_index(output.len(), output_index)?;
self.hooks
.reset(&mut self.codec)
.map_err(TranscodeError::domain)?;
Ok(0)
}
pub fn transcode(
&mut self,
input: &[C::Unit],
input_index: usize,
output: &mut [C::Value],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeError<H::Error>> {
TranscodeError::ensure_transcode_indices(
input.len(),
input_index,
output.len(),
output_index,
)?;
let min_units = self.codec.min_units_per_value().get();
let mut state =
DecodeState::new(input, input_index, output, output_index);
while state.has_input() {
let context = state.context();
if context.available() < min_units {
let additional = qubit_io::nz!(min_units - context.available());
return Ok(state.need_input_progress_with(
additional,
context.available(),
));
}
if state.needs_output() {
return Ok(state.need_output_progress());
}
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, TranscodeError<H::Error>> {
let required = self
.max_finish_output_len()
.map_err(|_| TranscodeError::OutputLengthOverflow)?;
TranscodeError::ensure_output_capacity(
output.len(),
output_index,
required,
)?;
let flushed = unsafe { self.codec.decode_flush(output, output_index) }
.map_err(|error| {
TranscodeError::domain(
self.hooks.map_decode_flush_error(&mut self.codec, error),
)
})?;
assert!(
flushed <= self.codec.max_decode_flush_values(),
"Codec::decode_flush wrote beyond its flush bound",
);
let written = self
.hooks
.finish(&mut self.codec, output, output_index + flushed)
.map_err(TranscodeError::domain)?;
assert!(
flushed + written <= required,
"TranscodeDecodeEngine hook wrote beyond its finish bound",
);
Ok(flushed + written)
}
#[inline(always)]
pub(crate) unsafe fn decode_at(
&mut self,
input: &[C::Unit],
input_index: usize,
) -> Result<(C::Value, NonZeroUsize), C::DecodeError> {
unsafe { self.codec.decode(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(&mut self.codec, error, context)
}
#[inline]
pub(super) fn decode_step(
&mut self,
input: &[C::Unit],
context: DecodeContext,
) -> Result<DecodeStep<C::Value>, TranscodeError<H::Error>> {
let min_units = self.codec.min_units_per_value().get();
if context.available() < min_units {
let additional = qubit_io::nz!(min_units - context.available());
return Ok(DecodeStep::need_input(additional, context.available()));
}
let result = unsafe { self.decode_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>, TranscodeError<H::Error>> {
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(error) => {
let action = self
.handle_decode_error(error, context)
.map_err(TranscodeError::domain)?;
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 = H::Error;
#[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> {
Ok(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)
}
}