use core::num::NonZeroUsize;
use super::super::internal::applied_decode_invalid_action::AppliedDecodeInvalidAction;
use super::super::internal::decode_state::DecodeState;
use super::super::internal::lifecycle_guard::LifecycleGuard;
use super::DecodeContext;
use super::DecodeIncompleteAction;
use super::DecodeInvalidAction;
use super::DecodeOutcome;
use super::TranscodeDecodeHooks;
use crate::CapacityError;
use crate::Codec;
use crate::DecodeFailure;
use crate::TranscodeDecodeError;
use crate::TranscodeDecodeErrorOf;
use crate::TranscodeDecoder;
use crate::TranscodeFailure;
use crate::TranscodeProgress;
use crate::Transcoder;
use crate::codec::assert_unit_bounds;
#[derive(Debug)]
pub struct TranscodeDecodeEngine<C, H> {
pub(super) codec: C,
pub(super) hooks: H,
lifecycle: LifecycleGuard,
}
impl<C, H> Default for TranscodeDecodeEngine<C, H>
where
C: Codec + Default,
H: TranscodeDecodeHooks<C> + Default,
{
#[inline]
fn default() -> Self {
Self::new(C::default(), H::default())
}
}
#[inline]
fn add_decode_output_bounds(first: usize, second: usize) -> Result<usize, CapacityError> {
first.checked_add(second).ok_or(CapacityError::OutputLengthOverflow)
}
#[inline]
fn sum_decode_output_bounds(reset: usize, transcode: usize, finish: usize) -> Result<usize, CapacityError> {
let before_finish = add_decode_output_bounds(reset, transcode)?;
add_decode_output_bounds(before_finish, finish)
}
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(),
}
}
#[inline]
#[must_use]
pub const fn codec(&self) -> &C {
&self.codec
}
#[inline]
#[must_use]
pub fn codec_mut(&mut self) -> &mut C {
&mut self.codec
}
#[inline]
#[must_use]
pub const fn hooks(&self) -> &H {
&self.hooks
}
#[inline]
#[must_use]
pub fn hooks_mut(&mut self) -> &mut H {
&mut self.hooks
}
#[inline]
#[must_use]
pub fn into_parts(self) -> (C, H) {
let Self { codec, hooks, .. } = self;
(codec, hooks)
}
#[inline]
#[must_use = "capacity planning can fail on overflow"]
pub fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
self.hooks.max_transcode_output_len(&self.codec, input_len)
}
#[inline]
#[must_use = "capacity planning can fail on overflow"]
pub fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
add_decode_output_bounds(
C::MAX_DECODE_FINISH_VALUES,
self.hooks.max_finish_output_len(&self.codec),
)
}
#[inline]
pub fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
Ok(C::MAX_DECODE_RESET_VALUES)
}
#[inline]
#[must_use = "capacity planning can fail on overflow"]
pub fn max_total_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
let transcode = self.max_transcode_output_len(input_len)?;
let finish = self.max_finish_output_len()?;
sum_decode_output_bounds(C::MAX_DECODE_RESET_VALUES, transcode, finish)
}
pub fn reset(&mut self, output: &mut [C::Value], output_index: usize) -> Result<usize, TranscodeDecodeErrorOf<C>> {
let required = C::MAX_DECODE_RESET_VALUES;
TranscodeFailure::ensure_output_capacity(output.len(), output_index, required)?;
self.lifecycle.on_reset_start();
self.hooks.reset_hooks(&mut self.codec);
let written = unsafe {
self.codec.decode_reset(output, output_index)
}
.map_err(TranscodeDecodeError::domain_reset)?;
assert!(written <= required, "Codec::decode_reset wrote beyond its reset bound",);
self.lifecycle.on_reset_success();
Ok(written)
}
#[inline]
pub fn transcode(
&mut self,
input: &[C::Unit],
input_index: usize,
output: &mut [C::Value],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeDecodeErrorOf<C>> {
self.transcode_with_eof(input, input_index, output, output_index, false)
}
#[inline]
pub fn transcode_eof(
&mut self,
input: &[C::Unit],
input_index: usize,
output: &mut [C::Value],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeDecodeErrorOf<C>> {
self.transcode_with_eof(input, input_index, output, output_index, true)
}
pub fn finish(&mut self, output: &mut [C::Value], output_index: usize) -> Result<usize, TranscodeDecodeErrorOf<C>> {
self.lifecycle.on_finish_attempt()?;
let hook_required = self.hooks.max_finish_output_len(&self.codec);
let required = C::MAX_DECODE_FINISH_VALUES
.checked_add(hook_required)
.ok_or(CapacityError::OutputLengthOverflow)?;
TranscodeFailure::ensure_output_capacity(output.len(), output_index, required)?;
self.lifecycle.on_finish_start();
let finished =
unsafe { self.codec.decode_finish(output, output_index) }.map_err(TranscodeDecodeError::domain_finish)?;
assert!(
finished <= C::MAX_DECODE_FINISH_VALUES,
"Codec::decode_finish wrote beyond its finish bound",
);
let written = self
.hooks
.finish_hooks(&mut self.codec, output, output_index + finished)?;
assert!(
written <= hook_required,
"TranscodeDecodeEngine hook wrote beyond its finish bound",
);
self.lifecycle.on_finish_success();
Ok(finished + written)
}
#[inline]
pub fn transcode_complete_into(
&mut self,
input: &[C::Unit],
output: &mut [C::Value],
) -> Result<usize, TranscodeDecodeErrorOf<C>> {
<Self as Transcoder>::transcode_complete_into(self, input, output)
}
pub(crate) fn decode_one<R, F>(
&mut self,
input: &[C::Unit],
context: DecodeContext,
end_of_input: bool,
consume: F,
) -> Result<(DecodeOutcome, Option<R>), TranscodeDecodeErrorOf<C>>
where
F: FnOnce(C::Value, usize) -> R,
{
debug_assert!(
context.available() > 0,
"decode_one requires at least one source input unit",
);
if context.available() < C::MIN_UNITS_PER_VALUE {
let required_total =
NonZeroUsize::new(C::MIN_UNITS_PER_VALUE).expect("Codec::MIN_UNITS_PER_VALUE is non-zero");
if !end_of_input {
return Ok((DecodeOutcome::need_input(required_total), None));
}
return self.apply_incomplete_decode(None, required_total, context, consume);
}
let result = unsafe {
if end_of_input {
self.codec.decode_eof(input, context.input_index())
} else {
self.codec.decode(input, context.input_index())
}
};
match result {
Ok((value, consumed)) => {
assert!(
consumed.get() <= context.available(),
"Codec::decode consumed beyond available input",
);
assert!(
consumed.get() <= C::MAX_DECODE_UNITS_PER_VALUE,
"Codec::decode consumed beyond Codec::MAX_DECODE_UNITS_PER_VALUE",
);
let consumed_value = consume(value, context.input_index());
Ok((
DecodeOutcome::emitted(consumed, NonZeroUsize::MIN),
Some(consumed_value),
))
}
Err(DecodeFailure::Incomplete { source, required_total }) => {
assert!(
required_total.get() > context.available(),
"Codec::decode incomplete required_total must exceed available input",
);
assert!(
required_total.get() <= C::MAX_DECODE_UNITS_PER_VALUE,
"Codec::decode incomplete required_total exceeded Codec::MAX_DECODE_UNITS_PER_VALUE",
);
if !end_of_input {
return Ok((DecodeOutcome::need_input(required_total), None));
}
self.apply_incomplete_decode(source, required_total, context, consume)
}
Err(DecodeFailure::Invalid { source, consumed }) => {
let action = match self
.hooks
.handle_invalid_decode(&mut self.codec, &source, consumed, context)?
{
DecodeInvalidAction::Reject => {
return Err(TranscodeDecodeError::domain_main_with_consumed(
source,
context.input_index(),
consumed,
));
}
DecodeInvalidAction::Skip { consumed } => AppliedDecodeInvalidAction::Skip { consumed },
DecodeInvalidAction::Emit { value, consumed } => {
AppliedDecodeInvalidAction::Emit { value, consumed }
}
};
Ok(Self::apply_invalid_decode_action(action, context, consume))
}
}
}
fn transcode_with_eof(
&mut self,
input: &[C::Unit],
input_index: usize,
output: &mut [C::Value],
output_index: usize,
end_of_input: bool,
) -> Result<TranscodeProgress, TranscodeDecodeErrorOf<C>> {
self.lifecycle.on_transcode()?;
TranscodeFailure::ensure_transcode_indices(input.len(), input_index, output.len(), output_index)?;
let min_units = NonZeroUsize::new(C::MIN_UNITS_PER_VALUE).expect("Codec::MIN_UNITS_PER_VALUE is non-zero");
let min_units_len = min_units.get();
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_len {
if !end_of_input {
return Ok(state.need_input_progress_with(min_units));
}
if state.needs_output() {
return Ok(state.need_output_progress());
}
let output_index = state.output_cursor();
let output = state.output_mut();
let (outcome, _) = self.decode_one(input, context, true, |value, _input_index| {
unsafe {
*output.get_unchecked_mut(output_index) = value;
}
})?;
if let Some(progress) = state.apply_decode_outcome(outcome) {
return Ok(progress);
}
continue;
}
if state.needs_output() {
return Ok(state.need_output_progress());
}
let output_index = state.output_cursor();
let output = state.output_mut();
let (outcome, _) = self.decode_one(input, context, end_of_input, |value, _input_index| {
unsafe {
*output.get_unchecked_mut(output_index) = value;
}
})?;
if let Some(progress) = state.apply_decode_outcome(outcome) {
return Ok(progress);
}
}
Ok(state.complete_progress())
}
fn apply_invalid_decode_action<R, F>(
action: AppliedDecodeInvalidAction<C::Value>,
context: DecodeContext,
consume: F,
) -> (DecodeOutcome, Option<R>)
where
F: FnOnce(C::Value, usize) -> R,
{
match action {
AppliedDecodeInvalidAction::Skip { consumed } => {
let read = DecodeInvalidAction::<C::Value>::bound_consumed(consumed, context.available());
(DecodeOutcome::skipped(read), None)
}
AppliedDecodeInvalidAction::Emit { value, consumed } => {
let read = DecodeInvalidAction::<C::Value>::bound_consumed(consumed, context.available());
let consumed_value = consume(value, context.input_index());
(DecodeOutcome::emitted(read, NonZeroUsize::MIN), Some(consumed_value))
}
}
}
fn apply_incomplete_decode<R, F>(
&mut self,
source: Option<C::DecodeError>,
required_total: NonZeroUsize,
context: DecodeContext,
consume: F,
) -> Result<(DecodeOutcome, Option<R>), TranscodeDecodeErrorOf<C>>
where
F: FnOnce(C::Value, usize) -> R,
{
match self
.hooks
.handle_incomplete_decode(&mut self.codec, source.as_ref(), required_total, context)?
{
DecodeIncompleteAction::Reject => match source {
Some(source) => Err(TranscodeDecodeError::domain_main(source, context.input_index())),
None => Err(TranscodeFailure::incomplete_input(
context.input_index(),
required_total.get(),
context.available(),
)
.into()),
},
DecodeIncompleteAction::Skip => {
let read =
NonZeroUsize::new(context.available()).expect("incomplete decode tail must contain source units");
Ok((DecodeOutcome::skipped(read), None))
}
DecodeIncompleteAction::Emit { value } => {
let read =
NonZeroUsize::new(context.available()).expect("incomplete decode tail must contain source units");
let result = consume(value, context.input_index());
Ok((DecodeOutcome::emitted(read, NonZeroUsize::MIN), Some(result)))
}
}
}
}
impl<C, H> Transcoder for TranscodeDecodeEngine<C, H>
where
C: Codec,
H: TranscodeDecodeHooks<C>,
{
type Input = C::Unit;
type Output = C::Value;
type Error = TranscodeDecodeErrorOf<C>;
#[inline]
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
TranscodeDecodeEngine::max_transcode_output_len(self, input_len)
}
#[inline]
fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
TranscodeDecodeEngine::max_finish_output_len(self)
}
#[inline]
fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
TranscodeDecodeEngine::max_reset_output_len(self)
}
fn reset(&mut self, output: &mut [C::Value], output_index: usize) -> Result<usize, TranscodeDecodeErrorOf<C>> {
TranscodeDecodeEngine::reset(self, output, output_index)
}
fn transcode(
&mut self,
input: &[C::Unit],
input_index: usize,
output: &mut [C::Value],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeDecodeErrorOf<C>> {
TranscodeDecodeEngine::transcode(self, input, input_index, output, output_index)
}
fn transcode_eof(
&mut self,
input: &[C::Unit],
input_index: usize,
output: &mut [C::Value],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeDecodeErrorOf<C>> {
TranscodeDecodeEngine::transcode_eof(self, input, input_index, output, output_index)
}
fn finish(&mut self, output: &mut [C::Value], output_index: usize) -> Result<usize, TranscodeDecodeErrorOf<C>> {
TranscodeDecodeEngine::finish(self, output, output_index)
}
}
impl<C, H> TranscodeDecoder for TranscodeDecodeEngine<C, H>
where
C: Codec,
H: TranscodeDecodeHooks<C>,
{
type DecodeError = C::DecodeError;
}