use core::num::NonZeroUsize;
use super::super::internal::{
applied_decode_invalid_action::AppliedDecodeInvalidAction,
decode_state::DecodeState,
lifecycle_guard::LifecycleGuard,
};
use super::{
DecodeContext,
DecodeIncompleteAction,
DecodeInvalidAction,
DecodeOutcome,
TranscodeDecodeHooks,
};
use crate::codec::assert_unit_bounds;
use crate::{
CapacityError,
Codec,
DecodeFailure,
TranscodeDecodeError,
TranscodeDecodeErrorOf,
TranscodeDecoder,
TranscodeFailure,
TranscodeProgress,
Transcoder,
};
#[derive(Debug, Default)]
pub struct TranscodeDecodeEngine<C, H> {
pub(super) codec: C,
pub(super) hooks: H,
lifecycle: LifecycleGuard,
}
fn add_decode_output_bounds(
first: usize,
second: usize,
) -> Result<usize, CapacityError> {
first
.checked_add(second)
.ok_or(CapacityError::OutputLengthOverflow)
}
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(always)]
#[must_use]
pub const fn codec(&self) -> &C {
&self.codec
}
#[inline(always)]
#[must_use]
pub fn codec_mut(&mut self) -> &mut C {
&mut self.codec
}
#[inline(always)]
#[must_use]
pub const fn hooks(&self) -> &H {
&self.hooks
}
#[inline(always)]
#[must_use]
pub fn hooks_mut(&mut self) -> &mut H {
&mut self.hooks
}
#[inline(always)]
#[must_use]
pub fn into_parts(self) -> (C, H) {
let Self { codec, hooks, .. } = self;
(codec, hooks)
}
#[inline(always)]
#[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(always)]
#[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(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)
}
#[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)
}
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(always)]
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)
}
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));
}
match self.hooks.handle_incomplete_decode(
&mut self.codec,
None,
min_units,
context,
)? {
DecodeIncompleteAction::Reject => {
return Err(TranscodeFailure::incomplete_input(
context.input_index(),
min_units.get(),
available,
)
.into());
}
DecodeIncompleteAction::Skip => {
let read = NonZeroUsize::new(available).expect(
"incomplete decode tail must contain source units",
);
if let Some(progress) = state
.apply_decode_outcome(DecodeOutcome::skipped(read))
{
return Ok(progress);
}
continue;
}
DecodeIncompleteAction::Emit { value } => {
if state.needs_output() {
return Ok(state.need_output_progress());
}
let output_index = state.output_cursor();
unsafe {
*state
.output_mut()
.get_unchecked_mut(output_index) = value;
}
let read = NonZeroUsize::new(available).expect(
"incomplete decode tail must contain source units",
);
if let Some(progress) = state.apply_decode_outcome(
DecodeOutcome::emitted(read, NonZeroUsize::MIN),
) {
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())
}
pub fn finish(
&mut self,
output: &mut [C::Value],
output_index: usize,
) -> Result<usize, TranscodeDecodeErrorOf<C>> {
self.lifecycle.on_finish_attempt()?;
let required = self.max_finish_output_len()?;
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!(
finished + written <= required,
"TranscodeDecodeEngine hook wrote beyond its finish bound",
);
self.lifecycle.on_finish_success();
Ok(finished + written)
}
#[inline(always)]
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 match self.hooks.handle_incomplete_decode(
&mut self.codec,
None,
required_total,
context,
)? {
DecodeIncompleteAction::Reject => {
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 consumed_value = consume(value, context.input_index());
Ok((
DecodeOutcome::emitted(read, NonZeroUsize::MIN),
Some(consumed_value),
))
}
};
}
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,
));
}
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 consumed_value =
consume(value, context.input_index());
Ok((
DecodeOutcome::emitted(read, NonZeroUsize::MIN),
Some(consumed_value),
))
}
}
}
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 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),
)
}
}
}
}
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(always)]
fn max_transcode_output_len(
&self,
input_len: usize,
) -> Result<usize, CapacityError> {
TranscodeDecodeEngine::max_transcode_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, TranscodeDecodeErrorOf<C>> {
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,
) -> Result<TranscodeProgress, TranscodeDecodeErrorOf<C>> {
TranscodeDecodeEngine::transcode(
self,
input,
input_index,
output,
output_index,
)
}
#[inline(always)]
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,
)
}
#[inline(always)]
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;
}