use core::cell::Cell;
use core::convert::Infallible;
use core::num::NonZeroUsize;
use std::rc::Rc;
use qubit_codec as codec;
use qubit_codec::CapacityError;
use qubit_codec::Codec;
use qubit_codec::TranscodeDecodeError;
use qubit_codec::TranscodeDecoder;
use qubit_codec::TranscodeFailure;
use qubit_codec::TranscodeStatus;
use qubit_codec::Transcoder;
use qubit_codec::engine::DecodeContext;
use qubit_codec::engine::DecodeIncompleteAction;
use qubit_codec::engine::DecodeInvalidAction;
use qubit_codec::engine::TranscodeDecodeEngine;
use qubit_codec::engine::TranscodeDecodeHooks;
use crate::nonzero;
#[must_use]
fn non_zero_consumed(consumed: usize) -> NonZeroUsize {
NonZeroUsize::new(consumed).expect("decode policy must consume at least one source unit")
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct PrefixCodec;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct UnderfilledFinishCodec;
impl Codec for UnderfilledFinishCodec {
type Value = u8;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = Infallible;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_FINISH_VALUES: usize = 2;
unsafe fn decode(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
Ok((input[input_index], NonZeroUsize::MIN))
}
unsafe fn encode(
&mut self,
value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
output[output_index] = *value;
Ok(1)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum PrefixDecodeError {
Invalid { consumed: usize },
}
impl Codec for PrefixCodec {
type Value = u8;
type Unit = u8;
type DecodeError = PrefixDecodeError;
type EncodeError = Infallible;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_UNITS_PER_VALUE: usize = 2;
unsafe fn decode(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
debug_assert!(input_index < input.len());
let first = unsafe { *input.as_ptr().add(input_index) };
match first {
0xfe if input.len() - input_index < 2 => Err(codec::DecodeFailure::incomplete_with_source(
PrefixDecodeError::Invalid { consumed: 1 },
nonzero(2),
)),
0xfe => {
let value = unsafe { *input.as_ptr().add(input_index + 1) };
Ok((value, nonzero(2)))
}
0xff => Err(codec::DecodeFailure::invalid(
PrefixDecodeError::Invalid { consumed: 1 },
NonZeroUsize::MIN,
)),
value => Ok((value, NonZeroUsize::MIN)),
}
}
unsafe fn encode(
&mut self,
value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
debug_assert!(output_index < output.len());
unsafe {
*output.as_mut_ptr().add(output_index) = *value;
}
Ok(1)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct EofAwarePrefixCodec;
impl Codec for EofAwarePrefixCodec {
type Value = u8;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = Infallible;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_UNITS_PER_VALUE: usize = 2;
unsafe fn decode(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
if input[input_index] == 0xfe && input.len() - input_index == 1 {
return Err(codec::DecodeFailure::incomplete(nonzero(2)));
}
Ok((input[input_index], NonZeroUsize::MIN))
}
unsafe fn decode_eof(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
if input[input_index] == 0xfe && input.len() - input_index == 1 {
return Ok((b'?', NonZeroUsize::MIN));
}
unsafe { self.decode(input, input_index) }
}
unsafe fn encode(
&mut self,
value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
output[output_index] = *value;
Ok(1)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct EofAwareHooks;
impl TranscodeDecodeHooks<EofAwarePrefixCodec> for EofAwareHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut EofAwarePrefixCodec,
error: &Infallible,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<EofAwarePrefixCodec>> {
match *error {}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct HintOnlyCodec;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum HintOnlyDecodeError {
Invalid,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct UnknownInvalidCodec;
impl Codec for UnknownInvalidCodec {
type Value = u8;
type Unit = u8;
type DecodeError = PrefixDecodeError;
type EncodeError = Infallible;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
unsafe fn decode(
&mut self,
_input: &[u8],
_input_index: usize,
) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
Err(codec::DecodeFailure::invalid_unknown(PrefixDecodeError::Invalid {
consumed: 0,
}))
}
unsafe fn encode(
&mut self,
value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
output[output_index] = *value;
Ok(1)
}
}
impl Codec for HintOnlyCodec {
type Value = u8;
type Unit = u8;
type DecodeError = HintOnlyDecodeError;
type EncodeError = Infallible;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_UNITS_PER_VALUE: usize = 2;
unsafe fn decode(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
debug_assert!(input_index < input.len());
match input[input_index] {
0xaa => Err(codec::DecodeFailure::invalid(HintOnlyDecodeError::Invalid, nonzero(2))),
value => Ok((value, NonZeroUsize::MIN)),
}
}
unsafe fn encode(
&mut self,
value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
debug_assert!(output_index < output.len());
output[output_index] = *value;
Ok(1)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct OverconsumingCodec;
impl Codec for OverconsumingCodec {
type Value = u8;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = Infallible;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
unsafe fn decode(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
debug_assert!(input_index < input.len());
Ok((input[input_index], unsafe { NonZeroUsize::new_unchecked(2) }))
}
unsafe fn encode(
&mut self,
value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
debug_assert!(output_index < output.len());
output[output_index] = *value;
Ok(1)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct OverlongIncompleteCodec;
impl Codec for OverlongIncompleteCodec {
type Value = u8;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = Infallible;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
unsafe fn decode(
&mut self,
_input: &[u8],
_input_index: usize,
) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
Err(codec::DecodeFailure::incomplete(nonzero(2)))
}
unsafe fn encode(
&mut self,
value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
output[output_index] = *value;
Ok(1)
}
}
#[derive(Debug, Eq, PartialEq)]
struct DropTrackedValue {
value: u8,
drops: Rc<Cell<usize>>,
}
impl DropTrackedValue {
fn new(value: u8, drops: Rc<Cell<usize>>) -> Self {
Self { value, drops }
}
}
impl Drop for DropTrackedValue {
fn drop(&mut self) {
self.drops.set(self.drops.get() + 1);
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct DropTrackedCodec {
drops: Rc<Cell<usize>>,
}
impl Codec for DropTrackedCodec {
type Value = DropTrackedValue;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = Infallible;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
unsafe fn decode(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<(DropTrackedValue, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
Ok((
DropTrackedValue::new(input[input_index], self.drops.clone()),
NonZeroUsize::MIN,
))
}
unsafe fn encode(
&mut self,
value: &DropTrackedValue,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
output[output_index] = value.value;
Ok(1)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct DropTrackedHooks;
impl TranscodeDecodeHooks<DropTrackedCodec> for DropTrackedHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut DropTrackedCodec,
error: &Infallible,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<DropTrackedValue>, codec::TranscodeDecodeErrorOf<DropTrackedCodec>> {
match *error {}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct ReplacingHooks;
impl TranscodeDecodeHooks<PrefixCodec> for ReplacingHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut PrefixCodec,
error: &PrefixDecodeError,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<PrefixCodec>> {
match error {
PrefixDecodeError::Invalid { consumed } => Ok(DecodeInvalidAction::Emit {
value: 99,
consumed: non_zero_consumed(*consumed),
}),
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct EofReplacingHooks;
impl TranscodeDecodeHooks<PrefixCodec> for EofReplacingHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut PrefixCodec,
_error: &PrefixDecodeError,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<PrefixCodec>> {
Ok(DecodeInvalidAction::Reject)
}
fn handle_incomplete_decode(
&mut self,
_codec: &mut PrefixCodec,
source: Option<&PrefixDecodeError>,
required_total: NonZeroUsize,
context: DecodeContext,
) -> Result<DecodeIncompleteAction<u8>, codec::TranscodeDecodeErrorOf<PrefixCodec>> {
assert_eq!(Some(&PrefixDecodeError::Invalid { consumed: 1 }), source,);
assert_eq!(nonzero(2), required_total);
assert_eq!(1, context.available());
Ok(DecodeIncompleteAction::Emit { value: 99 })
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct OverconsumingHooks;
impl TranscodeDecodeHooks<OverconsumingCodec> for OverconsumingHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut OverconsumingCodec,
error: &Infallible,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<OverconsumingCodec>> {
match *error {}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct OverlongIncompleteHooks;
impl TranscodeDecodeHooks<OverlongIncompleteCodec> for OverlongIncompleteHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut OverlongIncompleteCodec,
error: &Infallible,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<OverlongIncompleteCodec>> {
match *error {}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct SkippingHooks;
impl TranscodeDecodeHooks<PrefixCodec> for SkippingHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut PrefixCodec,
error: &PrefixDecodeError,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<PrefixCodec>> {
match error {
PrefixDecodeError::Invalid { consumed } => Ok(DecodeInvalidAction::Skip {
consumed: non_zero_consumed(*consumed),
}),
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct HintOnlySkippingHooks;
impl TranscodeDecodeHooks<HintOnlyCodec> for HintOnlySkippingHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut HintOnlyCodec,
error: &HintOnlyDecodeError,
consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<HintOnlyCodec>> {
match error {
HintOnlyDecodeError::Invalid => Ok(DecodeInvalidAction::Skip {
consumed: consumed.expect("codec should report invalid width"),
}),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct FinishHooks {
pending_suffix: bool,
finish_bound: usize,
}
impl Default for FinishHooks {
fn default() -> Self {
Self {
pending_suffix: true,
finish_bound: 1,
}
}
}
impl TranscodeDecodeHooks<PrefixCodec> for FinishHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut PrefixCodec,
error: &PrefixDecodeError,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<PrefixCodec>> {
match error {
PrefixDecodeError::Invalid { consumed } => Ok(DecodeInvalidAction::Skip {
consumed: non_zero_consumed(*consumed),
}),
}
}
fn max_finish_output_len(&self, _codec: &PrefixCodec) -> usize {
self.finish_bound
}
fn finish_hooks(
&mut self,
_codec: &mut PrefixCodec,
output: &mut [u8],
output_index: usize,
) -> Result<usize, codec::TranscodeDecodeErrorOf<PrefixCodec>> {
if !self.pending_suffix {
return Ok(0);
}
output[output_index] = 0xee;
self.pending_suffix = false;
Ok(1)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum InvalidDecodeInvalidActionKind {
Skip,
Emit,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct InvalidDecodeInvalidActionHooks {
kind: InvalidDecodeInvalidActionKind,
}
impl TranscodeDecodeHooks<PrefixCodec> for InvalidDecodeInvalidActionHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut PrefixCodec,
_error: &PrefixDecodeError,
_consumed: Option<NonZeroUsize>,
context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<PrefixCodec>> {
match self.kind {
InvalidDecodeInvalidActionKind::Skip => Ok(DecodeInvalidAction::Skip {
consumed: non_zero_consumed(context.available() + 1),
}),
InvalidDecodeInvalidActionKind::Emit => Ok(DecodeInvalidAction::Emit {
value: 77,
consumed: non_zero_consumed(context.available() + 1),
}),
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct OverwritingFinishHooks;
impl TranscodeDecodeHooks<PrefixCodec> for OverwritingFinishHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut PrefixCodec,
error: &PrefixDecodeError,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<PrefixCodec>> {
match error {
PrefixDecodeError::Invalid { consumed } => Ok(DecodeInvalidAction::Skip {
consumed: non_zero_consumed(*consumed),
}),
}
}
fn max_finish_output_len(&self, _codec: &PrefixCodec) -> usize {
1
}
fn finish_hooks(
&mut self,
_codec: &mut PrefixCodec,
output: &mut [u8],
output_index: usize,
) -> Result<usize, codec::TranscodeDecodeErrorOf<PrefixCodec>> {
assert_eq!(3, output.len());
output[output_index] = 0xee;
Ok(1)
}
}
struct UnderfilledFinishHooks {
bound: usize,
reported: usize,
}
impl TranscodeDecodeHooks<UnderfilledFinishCodec> for UnderfilledFinishHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut UnderfilledFinishCodec,
error: &Infallible,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<UnderfilledFinishCodec>> {
match *error {}
}
fn max_finish_output_len(&self, _codec: &UnderfilledFinishCodec) -> usize {
self.bound
}
fn finish_hooks(
&mut self,
_codec: &mut UnderfilledFinishCodec,
_output: &mut [u8],
_output_index: usize,
) -> Result<usize, codec::TranscodeDecodeErrorOf<UnderfilledFinishCodec>> {
Ok(self.reported)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct OverreportingFinishHooks;
impl TranscodeDecodeHooks<PrefixCodec> for OverreportingFinishHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut PrefixCodec,
error: &PrefixDecodeError,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<PrefixCodec>> {
match error {
PrefixDecodeError::Invalid { consumed } => Ok(DecodeInvalidAction::Skip {
consumed: non_zero_consumed(*consumed),
}),
}
}
fn max_finish_output_len(&self, _codec: &PrefixCodec) -> usize {
1
}
fn finish_hooks(
&mut self,
_codec: &mut PrefixCodec,
output: &mut [u8],
output_index: usize,
) -> Result<usize, codec::TranscodeDecodeErrorOf<PrefixCodec>> {
output[output_index] = 0xee;
Ok(2)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct MinTwoCodec;
impl Codec for MinTwoCodec {
type Value = u8;
type Unit = u8;
type DecodeError = PrefixDecodeError;
type EncodeError = Infallible;
const MIN_UNITS_PER_VALUE: usize = 2;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 2;
const MAX_DECODE_UNITS_PER_VALUE: usize = 2;
unsafe fn decode(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
debug_assert!(input_index + 1 < input.len());
Ok((input[input_index].wrapping_add(input[input_index + 1]), unsafe {
NonZeroUsize::new_unchecked(2)
}))
}
unsafe fn encode(
&mut self,
value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
debug_assert!(output_index < output.len());
output[output_index] = *value;
Ok(1)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct OverflowFlushCodec;
impl Codec for OverflowFlushCodec {
type Value = u8;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = Infallible;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_FINISH_VALUES: usize = usize::MAX;
unsafe fn decode(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
Ok((input[input_index], NonZeroUsize::MIN))
}
unsafe fn encode(
&mut self,
value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
output[output_index] = *value;
Ok(1)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct OverflowFinishHooks;
impl TranscodeDecodeHooks<OverflowFlushCodec> for OverflowFinishHooks {
fn max_finish_output_len(&self, _codec: &OverflowFlushCodec) -> usize {
1
}
fn handle_invalid_decode(
&mut self,
_codec: &mut OverflowFlushCodec,
error: &Infallible,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<OverflowFlushCodec>> {
match *error {}
}
}
impl TranscodeDecodeHooks<MinTwoCodec> for ReplacingHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut MinTwoCodec,
error: &PrefixDecodeError,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<MinTwoCodec>> {
match error {
PrefixDecodeError::Invalid { consumed } => Ok(DecodeInvalidAction::Emit {
value: 99,
consumed: non_zero_consumed(*consumed),
}),
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct EofSkippingHooks;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct CountedIncompleteHooks {
calls: usize,
action: CountedIncompleteAction,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
enum CountedIncompleteAction {
#[default]
Emit,
Skip,
Reject,
}
impl TranscodeDecodeHooks<MinTwoCodec> for CountedIncompleteHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut MinTwoCodec,
_error: &PrefixDecodeError,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<MinTwoCodec>> {
Ok(DecodeInvalidAction::Reject)
}
fn handle_incomplete_decode(
&mut self,
_codec: &mut MinTwoCodec,
_source: Option<&PrefixDecodeError>,
_required_total: NonZeroUsize,
_context: DecodeContext,
) -> Result<DecodeIncompleteAction<u8>, codec::TranscodeDecodeErrorOf<MinTwoCodec>> {
self.calls += 1;
Ok(match self.action {
CountedIncompleteAction::Emit => DecodeIncompleteAction::Emit {
value: 90 + self.calls as u8,
},
CountedIncompleteAction::Skip => DecodeIncompleteAction::Skip,
CountedIncompleteAction::Reject => DecodeIncompleteAction::Reject,
})
}
}
impl TranscodeDecodeHooks<MinTwoCodec> for EofSkippingHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut MinTwoCodec,
_error: &PrefixDecodeError,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<MinTwoCodec>> {
Ok(DecodeInvalidAction::Reject)
}
fn handle_incomplete_decode(
&mut self,
_codec: &mut MinTwoCodec,
source: Option<&PrefixDecodeError>,
required_total: NonZeroUsize,
context: DecodeContext,
) -> Result<DecodeIncompleteAction<u8>, codec::TranscodeDecodeErrorOf<MinTwoCodec>> {
assert_eq!(None, source);
assert_eq!(nonzero(2), required_total);
assert_eq!(1, context.available());
Ok(DecodeIncompleteAction::Skip)
}
}
#[test]
fn test_transcode_decode_engine_exposes_codec_hooks_and_parts() {
let mut engine = TranscodeDecodeEngine::new(PrefixCodec, ReplacingHooks);
assert_eq!(&PrefixCodec, engine.codec());
assert_eq!(&ReplacingHooks, engine.hooks());
*engine.codec_mut() = PrefixCodec;
*engine.hooks_mut() = ReplacingHooks;
let (codec, hooks) = engine.into_parts();
assert_eq!(PrefixCodec, codec);
assert_eq!(ReplacingHooks, hooks);
}
#[test]
fn test_transcode_decode_engine_reports_finish_bound_overflow() {
let mut decoder = TranscodeDecodeEngine::<_, _>::new(OverflowFlushCodec, OverflowFinishHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
assert_eq!(
Err(CapacityError::OutputLengthOverflow),
decoder.max_finish_output_len(),
);
assert_eq!(
Err(CapacityError::OutputLengthOverflow),
decoder.max_total_output_len(0),
);
let error = decoder
.finish(&mut output, 0)
.expect_err("finish should report capacity overflow before writing");
assert_eq!(
codec::TranscodeDecodeError::Failure(codec::TranscodeFailure::output_length_overflow()),
error
);
}
#[test]
fn test_transcode_decode_engine_drops_replaced_output_value() {
let drops = Rc::new(Cell::new(0));
let codec = DropTrackedCodec { drops: drops.clone() };
let mut decoder = TranscodeDecodeEngine::new(codec, DropTrackedHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [DropTrackedValue::new(99, drops.clone())];
let progress = decoder
.transcode(&[7], 0, &mut output, 0)
.expect("tracked value should decode into output");
assert_eq!(TranscodeStatus::Complete, progress.status());
assert_eq!(1, progress.read());
assert_eq!(1, progress.written());
assert_eq!(7, output[0].value);
assert_eq!(
1,
drops.get(),
"decoded output should replace and drop the previous initialized value",
);
drop(output);
assert_eq!(2, drops.get());
}
#[test]
fn test_transcode_decode_engine_reports_finish_bounds() {
type Decoder = TranscodeDecodeEngine<PrefixCodec, ReplacingHooks>;
type TranscodeCompleteIntoFn =
fn(&mut Decoder, &[u8], &mut [u8]) -> Result<usize, TranscodeDecodeError<PrefixDecodeError>>;
let mut decoder = TranscodeDecodeEngine::<_, _>::new(PrefixCodec, ReplacingHooks);
let max_total_output_len: fn(&Decoder, usize) -> Result<usize, CapacityError> = Decoder::max_total_output_len;
let transcode_complete_into: TranscodeCompleteIntoFn = Decoder::transcode_complete_into;
let mut output = [0_u8; 1];
assert_eq!(Ok(3), decoder.max_transcode_output_len(3));
assert_eq!(Ok(3), max_total_output_len(&decoder, 3));
assert_eq!(Ok(0), decoder.max_finish_output_len());
let mut all_output = [0_u8; 3];
let written = transcode_complete_into(&mut decoder, &[1, 2, 3], &mut all_output)
.expect("complete decode should fit the planned output");
assert_eq!(3, written);
assert_eq!(&[1, 2, 3], &all_output[..written]);
decoder.reset(&mut [], 0).expect("reset");
let written = decoder
.finish(&mut output, 0)
.expect("generic decoder finish is a no-op");
assert_eq!(0, written);
}
#[test]
fn test_transcode_decode_engine_delegates_finish_to_hooks() {
let mut decoder = TranscodeDecodeEngine::<_, _>::new(PrefixCodec, FinishHooks::default());
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
assert_eq!(Ok(1), decoder.max_finish_output_len());
let error = decoder
.finish(&mut [], 0)
.expect_err("finish should reject insufficient output before calling hooks");
assert_eq!(
codec::TranscodeDecodeError::Failure(codec::TranscodeFailure::insufficient_output(0, 1, 0)),
error,
);
assert_eq!(Ok(1), decoder.max_finish_output_len());
let written = decoder.finish(&mut output, 0).expect("hook should write final output");
assert_eq!(1, written);
assert_eq!([0xee], output);
assert_eq!(Ok(1), decoder.max_finish_output_len());
}
#[test]
fn test_transcode_decode_engine_finish_passes_full_output_to_hooks() {
let mut decoder = TranscodeDecodeEngine::<_, _>::new(PrefixCodec, OverwritingFinishHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0xa1, 0, 0xb2];
let written = decoder
.finish(&mut output, 1)
.expect("hook should receive the caller-provided output slice");
assert_eq!(1, written);
assert_eq!([0xa1, 0xee, 0xb2], output);
}
#[test]
#[should_panic(expected = "TranscodeDecodeEngine hook wrote beyond its finish bound")]
fn test_transcode_decode_engine_finish_panics_when_hook_exceeds_zero_bound() {
let hooks = UnderfilledFinishHooks { bound: 0, reported: 1 };
let mut decoder = TranscodeDecodeEngine::new(UnderfilledFinishCodec, hooks);
decoder.reset(&mut [0; 2], 0).expect("initialize stream");
let _ = decoder.finish(&mut [0; 2], 0);
}
#[test]
fn test_transcode_decode_engine_finish_accepts_hook_at_its_bound() {
let hooks = UnderfilledFinishHooks { bound: 1, reported: 1 };
let mut decoder = TranscodeDecodeEngine::new(UnderfilledFinishCodec, hooks);
decoder.reset(&mut [0; 2], 0).expect("initialize stream");
assert_eq!(1, decoder.finish(&mut [0; 3], 0).expect("finish within bound"));
}
#[test]
#[should_panic(expected = "TranscodeDecodeEngine hook wrote beyond its finish bound")]
fn test_transcode_decode_engine_finish_panics_when_hook_report_overflows() {
let hooks = UnderfilledFinishHooks {
bound: 1,
reported: usize::MAX,
};
let mut decoder = TranscodeDecodeEngine::new(UnderfilledFinishCodec, hooks);
decoder.reset(&mut [0; 2], 0).expect("initialize stream");
let _ = decoder.finish(&mut [0; 3], 0);
}
#[test]
#[should_panic(expected = "TranscodeDecodeEngine hook wrote beyond its finish bound")]
fn test_transcode_decode_engine_finish_panics_when_hook_overreports_bound() {
let mut decoder = TranscodeDecodeEngine::<_, _>::new(PrefixCodec, OverreportingFinishHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 2];
let _ = decoder.finish(&mut output, 0);
}
#[test]
fn test_transcode_decode_engine_finish_reports_output_index_beyond_buffer() {
let mut decoder = TranscodeDecodeEngine::<_, _>::new(PrefixCodec, FinishHooks::default());
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [];
let error = decoder
.finish(&mut output, 1)
.expect_err("out-of-range finish output index should be rejected");
assert_eq!(
codec::TranscodeDecodeError::Failure(codec::TranscodeFailure::invalid_output_index(1, 0)),
error,
);
}
#[test]
fn test_transcode_decode_engine_default_finish_reports_output_index_beyond_buffer() {
let mut decoder = TranscodeDecodeEngine::<_, _>::new(PrefixCodec, ReplacingHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [];
let error = decoder
.finish(&mut output, 1)
.expect_err("default finish should reject out-of-range output index");
assert_eq!(
codec::TranscodeDecodeError::Failure(codec::TranscodeFailure::invalid_output_index(1, 0)),
error,
);
}
#[test]
fn test_buffered_decode_hooks_default_finish_is_noop() {
let mut hooks = ReplacingHooks;
let mut output = [];
let written = TranscodeDecodeHooks::<PrefixCodec>::finish_hooks(&mut hooks, &mut PrefixCodec, &mut output, 1)
.expect("default hook finish should be a no-op");
assert_eq!(0, written);
}
#[test]
fn test_transcode_decode_engine_leaves_incomplete_input_to_caller() {
let mut decoder = TranscodeDecodeEngine::new(PrefixCodec, ReplacingHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
let progress = decoder
.transcode(&[0xfe], 0, &mut output, 0)
.expect("incomplete prefix should be reported");
assert_eq!(TranscodeStatus::NeedInput { required: nonzero(2) }, progress.status(),);
assert_eq!(0, progress.read());
assert_eq!(0, progress.written());
let progress = decoder
.transcode(&[0xfe, 7], 0, &mut output, 0)
.expect("caller-refilled prefix should decode");
assert_eq!(TranscodeStatus::Complete, progress.status());
assert_eq!(2, progress.read());
assert_eq!(1, progress.written());
assert_eq!([7], output);
}
#[test]
fn test_transcode_decode_engine_preserves_incomplete_source_at_eof() {
let mut decoder = TranscodeDecodeEngine::new(PrefixCodec, ReplacingHooks);
decoder.reset(&mut [], 0).expect("initialize stream");
let mut output = [0_u8; 1];
let error = decoder
.transcode_eof(&[0xfe], 0, &mut output, 0)
.expect_err("EOF must expose the codec incomplete source");
assert_eq!(
TranscodeDecodeError::domain_main(PrefixDecodeError::Invalid { consumed: 1 }, 0,),
error,
);
}
#[test]
fn test_transcode_decode_engine_applies_emit_policy_to_incomplete_eof() {
let mut decoder = TranscodeDecodeEngine::new(PrefixCodec, EofReplacingHooks);
decoder.reset(&mut [], 0).expect("initialize stream");
let mut output = [0_u8; 1];
let progress = decoder
.transcode_eof(&[0xfe], 0, &mut output, 0)
.expect("EOF incomplete policy should emit replacement");
assert_eq!(TranscodeStatus::Complete, progress.status());
assert_eq!(1, progress.read());
assert_eq!(1, progress.written());
assert_eq!([99], output);
}
#[test]
fn test_transcode_decode_engine_applies_skip_policy_to_short_eof_tail() {
let mut decoder = TranscodeDecodeEngine::new(MinTwoCodec, EofSkippingHooks);
decoder.reset(&mut [], 0).expect("initialize stream");
let mut output = [0_u8; 1];
let progress = decoder
.transcode_eof(&[7], 0, &mut output, 0)
.expect("EOF short-tail policy should skip input");
assert_eq!(TranscodeStatus::Complete, progress.status());
assert_eq!(1, progress.read());
assert_eq!(0, progress.written());
}
#[test]
fn test_transcode_decode_engine_rejects_short_eof_tail_by_default() {
let mut decoder = TranscodeDecodeEngine::new(MinTwoCodec, ReplacingHooks);
decoder.reset(&mut [], 0).expect("initialize stream");
let mut output = [0_u8; 1];
let error = decoder
.transcode_eof(&[7], 0, &mut output, 0)
.expect_err("default incomplete policy should reject the EOF tail");
assert_eq!(
TranscodeDecodeError::Failure(TranscodeFailure::incomplete_input(0, 2, 1)),
error,
);
}
#[test]
fn test_transcode_decode_engine_keeps_incomplete_eof_tail_when_emit_needs_output() {
let mut decoder = TranscodeDecodeEngine::new(PrefixCodec, EofReplacingHooks);
decoder.reset(&mut [], 0).expect("initialize stream");
let mut output = [];
let progress = decoder
.transcode_eof(&[0xfe], 0, &mut output, 0)
.expect("replacement should wait for output capacity");
assert_eq!(
TranscodeStatus::NeedOutput {
required: NonZeroUsize::MIN,
},
progress.status(),
);
assert_eq!(0, progress.read());
assert_eq!(0, progress.written());
}
#[test]
fn test_transcode_decode_engine_does_not_retry_incomplete_hook_without_output() {
let mut decoder = TranscodeDecodeEngine::new(MinTwoCodec, CountedIncompleteHooks::default());
decoder.reset(&mut [], 0).expect("initialize stream");
for _ in 0..2 {
let progress = decoder
.transcode_eof(&[7], 0, &mut [], 0)
.expect("missing output should be reported");
assert_eq!(
TranscodeStatus::NeedOutput {
required: NonZeroUsize::MIN
},
progress.status()
);
assert_eq!(0, progress.read());
assert_eq!(0, decoder.hooks().calls);
}
let mut output = [0_u8; 1];
let progress = decoder
.transcode_eof(&[7], 0, &mut output, 0)
.expect("incomplete policy should emit after capacity is supplied");
assert_eq!(TranscodeStatus::Complete, progress.status());
assert_eq!(1, progress.read());
assert_eq!(1, progress.written());
assert_eq!([91], output);
assert_eq!(1, decoder.hooks().calls);
}
#[test]
fn test_transcode_decode_engine_gates_incomplete_skip_and_reject_on_capacity() {
for action in [CountedIncompleteAction::Skip, CountedIncompleteAction::Reject] {
let mut decoder = TranscodeDecodeEngine::new(MinTwoCodec, CountedIncompleteHooks { calls: 0, action });
decoder.reset(&mut [], 0).expect("initialize stream");
let mut output = [0_u8; 1];
for _ in 0..2 {
let progress = decoder
.transcode_eof(&[7], 0, &mut [], 0)
.expect("EOF policy should wait for one output slot");
assert_eq!(
TranscodeStatus::NeedOutput {
required: NonZeroUsize::MIN,
},
progress.status(),
);
assert_eq!((0, 0), (progress.read(), progress.written()));
assert_eq!(0, decoder.hooks().calls);
}
let result = decoder.transcode_eof(&[7], 0, &mut output, 0);
match action {
CountedIncompleteAction::Skip => {
let progress = result.expect("Skip should consume the EOF tail");
assert_eq!(TranscodeStatus::Complete, progress.status());
assert_eq!((1, 0), (progress.read(), progress.written()));
}
CountedIncompleteAction::Reject => {
assert_eq!(
Err(TranscodeDecodeError::Failure(TranscodeFailure::incomplete_input(
0, 2, 1
)),),
result,
);
}
CountedIncompleteAction::Emit => unreachable!(),
}
assert_eq!(1, decoder.hooks().calls);
}
}
#[test]
fn test_transcode_decode_engine_uses_codec_eof_decode_path() {
let mut decoder = TranscodeDecodeEngine::new(EofAwarePrefixCodec, EofAwareHooks);
decoder.reset(&mut [], 0).expect("reset before EOF-aware decode");
let mut output = [0_u8; 1];
let progress = decoder
.transcode_eof(&[0xfe], 0, &mut output, 0)
.expect("EOF-aware decode should resolve the trailing prefix");
assert_eq!(TranscodeStatus::Complete, progress.status());
assert_eq!(1, progress.read());
assert_eq!(1, progress.written());
assert_eq!([b'?'], output);
}
#[test]
fn test_transcode_decode_engine_reports_short_minimum_input_without_consuming_tail() {
let mut decoder = TranscodeDecodeEngine::new(MinTwoCodec, ReplacingHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
let progress = decoder
.transcode(&[7], 0, &mut output, 0)
.expect("short input should request another unit");
assert_eq!(TranscodeStatus::NeedInput { required: nonzero(2) }, progress.status(),);
assert_eq!(0, progress.read());
assert_eq!(0, progress.written());
}
#[test]
fn test_transcode_decode_engine_reports_incomplete_input_before_missing_output() {
let mut decoder = TranscodeDecodeEngine::new(MinTwoCodec, ReplacingHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [];
let progress = decoder
.transcode(&[7], 0, &mut output, 0)
.expect("short input should request another unit before output capacity");
assert_eq!(TranscodeStatus::NeedInput { required: nonzero(2) }, progress.status(),);
assert_eq!(0, progress.read());
assert_eq!(0, progress.written());
}
#[test]
fn test_transcode_decode_engine_allows_policy_emit_for_invalid_input() {
let mut decoder = TranscodeDecodeEngine::new(PrefixCodec, ReplacingHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 2];
let progress = decoder
.transcode(&[0xff, 1], 0, &mut output, 0)
.expect("invalid input should be replaced");
assert_eq!(TranscodeStatus::Complete, progress.status());
assert_eq!(2, progress.read());
assert_eq!(2, progress.written());
assert_eq!([99, 1], output);
}
#[test]
fn test_transcode_decode_engine_reports_need_output_before_policy_emit() {
let mut decoder = TranscodeDecodeEngine::new(PrefixCodec, ReplacingHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [];
let progress = decoder
.transcode(&[0xff], 0, &mut output, 0)
.expect("replacement policy should stop before writing without output");
assert_eq!(TranscodeStatus::NeedOutput { required: nonzero(1) }, progress.status(),);
assert_eq!(0, progress.read());
assert_eq!(0, progress.written());
}
#[test]
fn test_transcode_decode_engine_allows_policy_skip_for_invalid_input() {
let mut decoder = TranscodeDecodeEngine::new(PrefixCodec, SkippingHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
let progress = decoder
.transcode(&[0xff, 1], 0, &mut output, 0)
.expect("invalid input should be skipped");
assert_eq!(TranscodeStatus::Complete, progress.status());
assert_eq!(2, progress.read());
assert_eq!(1, progress.written());
assert_eq!([1], output);
}
#[test]
fn test_transcode_decode_engine_passes_invalid_consumed_hint_to_hooks() {
let mut decoder = TranscodeDecodeEngine::new(HintOnlyCodec, HintOnlySkippingHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
let progress = decoder
.transcode(&[0xaa, 0xbb, 1], 0, &mut output, 0)
.expect("policy should skip invalid width from failure hint");
assert_eq!(TranscodeStatus::Complete, progress.status());
assert_eq!(3, progress.read());
assert_eq!(1, progress.written());
assert_eq!([1], output);
}
#[test]
#[should_panic(expected = "DecodeInvalidAction consumed units must not exceed available input")]
fn test_transcode_decode_engine_panics_on_invalid_skip_action() {
let mut decoder = TranscodeDecodeEngine::new(
PrefixCodec,
InvalidDecodeInvalidActionHooks {
kind: InvalidDecodeInvalidActionKind::Skip,
},
);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
let _ = decoder.transcode(&[0xff], 0, &mut output, 0);
}
#[test]
#[should_panic(expected = "DecodeInvalidAction consumed units must not exceed available input")]
fn test_transcode_decode_engine_panics_on_invalid_emit_action() {
let mut decoder = TranscodeDecodeEngine::new(
PrefixCodec,
InvalidDecodeInvalidActionHooks {
kind: InvalidDecodeInvalidActionKind::Emit,
},
);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
let _ = decoder.transcode(&[0xff], 0, &mut output, 0);
}
#[test]
fn test_transcode_decode_engine_reports_output_bounds_without_consuming_input() {
let mut decoder = TranscodeDecodeEngine::new(PrefixCodec, ReplacingHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [];
let progress = decoder
.transcode(&[1], 0, &mut output, 0)
.expect("empty output should request capacity");
assert_eq!(TranscodeStatus::NeedOutput { required: nonzero(1) }, progress.status(),);
assert_eq!(0, progress.read());
assert_eq!(0, progress.written());
let error = decoder
.transcode(&[1], 0, &mut output, 1)
.expect_err("out-of-range output index should fail");
assert_eq!(
codec::TranscodeDecodeError::Failure(codec::TranscodeFailure::invalid_output_index(1, 0)),
error,
);
}
#[test]
#[should_panic(expected = "Codec::decode consumed beyond available input")]
fn test_transcode_decode_engine_panics_when_codec_consumes_beyond_available_input() {
let mut decoder = TranscodeDecodeEngine::new(OverconsumingCodec, OverconsumingHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
let _ = decoder.transcode(&[1], 0, &mut output, 0);
}
#[test]
#[should_panic(expected = "Codec::decode consumed beyond Codec::MAX_DECODE_UNITS_PER_VALUE")]
fn test_transcode_decode_engine_panics_when_codec_consumes_beyond_decode_maximum() {
let mut decoder = TranscodeDecodeEngine::new(OverconsumingCodec, OverconsumingHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
let _ = decoder.transcode(&[1, 2], 0, &mut output, 0);
}
#[test]
#[should_panic(expected = "Codec::decode incomplete required_total exceeded Codec::MAX_DECODE_UNITS_PER_VALUE")]
fn test_transcode_decode_engine_panics_when_incomplete_hint_exceeds_codec_maximum() {
let mut decoder = TranscodeDecodeEngine::new(OverlongIncompleteCodec, OverlongIncompleteHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
let _ = decoder.transcode(&[1], 0, &mut output, 0);
}
#[test]
fn test_transcode_decode_engine_uses_hooks_for_invalid_input_index() {
let mut decoder = TranscodeDecodeEngine::new(PrefixCodec, ReplacingHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [];
let error = decoder
.transcode(&[1], 2, &mut output, 0)
.expect_err("invalid input index should be rejected");
assert_eq!(
codec::TranscodeDecodeError::Failure(codec::TranscodeFailure::invalid_input_index(2, 1)),
error,
);
}
#[test]
fn test_transcode_decode_engine_implements_buffered_transcoder() {
type Decoder = TranscodeDecodeEngine<PrefixCodec, ReplacingHooks>;
fn assert_decoder<T: TranscodeDecoder<Input = u8, Output = u8>>() {}
assert_decoder::<Decoder>();
let mut decoder = Decoder::new(PrefixCodec, ReplacingHooks);
decoder.reset(&mut [], 0).expect("reset before trait transcode");
let available = <Decoder as Transcoder>::max_transcode_output_len(&decoder, 1)
.expect("max_transcode_output_len should be callable through trait");
assert_eq!(1, available);
let mut output = [0_u8; 1];
let progress = <Decoder as Transcoder>::transcode(&mut decoder, &[0xfe, 7], 0, &mut output, 0)
.expect("trait transcode should decode a prefixed value");
assert_eq!(TranscodeStatus::Complete, progress.status());
assert_eq!(2, progress.read());
assert_eq!(1, progress.written());
let finish = Transcoder::finish(&mut decoder, &mut output, 0).expect("trait finish should delegate to hooks");
assert_eq!(0, finish);
let finish_output_len = <Decoder as Transcoder>::max_finish_output_len(&decoder)
.expect("max_finish_output_len should be callable through trait");
assert_eq!(0, finish_output_len);
assert_eq!(7, output[0]);
Transcoder::reset(&mut decoder, &mut output, 0).expect("reset");
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct FlushFailCodec;
#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
#[error("flush failed")]
struct FlushFailError;
impl Codec for FlushFailCodec {
type Value = u8;
type Unit = u8;
type DecodeError = FlushFailError;
type EncodeError = Infallible;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_FINISH_VALUES: usize = 1;
unsafe fn decode(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
Ok((input[input_index], NonZeroUsize::MIN))
}
unsafe fn encode(
&mut self,
value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
output[output_index] = *value;
Ok(1)
}
unsafe fn decode_finish(&mut self, _output: &mut [u8], _output_index: usize) -> Result<usize, Self::DecodeError> {
Err(FlushFailError)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct FlushMappingHooks;
impl TranscodeDecodeHooks<FlushFailCodec> for FlushMappingHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut FlushFailCodec,
error: &FlushFailError,
_consumed: Option<NonZeroUsize>,
context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<FlushFailCodec>> {
Err(TranscodeDecodeError::domain_main(*error, context.input_index()))
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct ResetObservingHooks {
called: Rc<Cell<bool>>,
}
impl TranscodeDecodeHooks<PrefixCodec> for ResetObservingHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut PrefixCodec,
error: &PrefixDecodeError,
_consumed: Option<NonZeroUsize>,
context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<PrefixCodec>> {
Err(TranscodeDecodeError::domain_main(*error, context.input_index()))
}
fn reset_hooks(&mut self, _codec: &mut PrefixCodec) {
self.called.set(true);
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct ResetFailCodec {
fail_reset: bool,
}
impl Default for ResetFailCodec {
fn default() -> Self {
Self { fail_reset: true }
}
}
impl Codec for ResetFailCodec {
type Value = u8;
type Unit = u8;
type DecodeError = PrefixDecodeError;
type EncodeError = Infallible;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_RESET_VALUES: usize = 1;
unsafe fn decode(
&mut self,
_input: &[u8],
_input_index: usize,
) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
Ok((0u8, NonZeroUsize::MIN))
}
unsafe fn encode(
&mut self,
_value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
debug_assert!(output_index < output.len());
unsafe {
*output.get_unchecked_mut(output_index) = 0;
}
Ok(1)
}
unsafe fn decode_reset(&mut self, output: &mut [u8], output_index: usize) -> Result<usize, Self::DecodeError> {
if self.fail_reset {
return Err(PrefixDecodeError::Invalid { consumed: 1 });
}
output[output_index] = 0xaa;
Ok(1)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct ResetErrorMappingHooks;
impl TranscodeDecodeHooks<ResetFailCodec> for ResetErrorMappingHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut ResetFailCodec,
error: &PrefixDecodeError,
_consumed: Option<NonZeroUsize>,
context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<ResetFailCodec>> {
Err(TranscodeDecodeError::domain_main(*error, context.input_index()))
}
}
#[test]
fn test_transcode_decode_engine_reports_max_reset_output_len() {
let decoder = TranscodeDecodeEngine::<PrefixCodec, ReplacingHooks>::new(PrefixCodec, ReplacingHooks);
assert_eq!(Ok(0), decoder.max_reset_output_len());
assert_eq!(Ok(0), Transcoder::max_reset_output_len(&decoder));
}
#[test]
fn test_transcode_decode_engine_reset_rejects_invalid_output_index() {
let mut decoder = TranscodeDecodeEngine::<PrefixCodec, ReplacingHooks>::new(PrefixCodec, ReplacingHooks);
let error = decoder
.reset(&mut [], 1)
.expect_err("reset should reject invalid output index");
assert_eq!(
codec::TranscodeDecodeError::Failure(codec::TranscodeFailure::invalid_output_index(1, 0)),
error,
);
}
#[test]
fn test_transcode_decode_engine_reset_calls_hook_before_reset() {
let called = Rc::new(Cell::new(false));
let hooks = ResetObservingHooks { called: called.clone() };
let mut decoder = TranscodeDecodeEngine::<_, _>::new(PrefixCodec, hooks);
decoder.reset(&mut [], 0).expect("reset should succeed");
assert!(called.get(), "decode hook before_reset should be called");
}
#[test]
fn test_transcode_decode_engine_finish_converts_decode_finish_errors() {
let mut decoder = TranscodeDecodeEngine::<_, _>::new(FlushFailCodec, FlushMappingHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
let error = decoder
.finish(&mut output, 0)
.expect_err("flush errors should be converted through the hook error type");
assert_eq!(TranscodeDecodeError::domain_finish(FlushFailError), error,);
}
#[test]
fn test_transcode_decode_engine_reset_converts_decode_reset_errors() {
let mut decoder = TranscodeDecodeEngine::<_, _>::new(ResetFailCodec::default(), ResetErrorMappingHooks);
let mut output = [0_u8; 1];
let error = decoder
.reset(&mut output, 0)
.expect_err("decode reset errors should be converted through the hook error type");
assert_eq!(
TranscodeDecodeError::domain_reset(PrefixDecodeError::Invalid { consumed: 1 }),
error,
);
}
#[test]
fn test_transcode_decode_engine_configurable_reset_supports_success_and_capacity_errors() {
let mut decoder = TranscodeDecodeEngine::<_, _>::new(ResetFailCodec { fail_reset: false }, ResetErrorMappingHooks);
let mut output = [0_u8; 1];
assert_eq!(1, decoder.reset(&mut output, 0).expect("reset should succeed"));
assert_eq!([0xaa], output);
let error = decoder
.reset(&mut [], 0)
.expect_err("reset should enforce its declared output bound");
assert_eq!(
codec::TranscodeDecodeError::Failure(codec::TranscodeFailure::insufficient_output(0, 1, 0)),
error
);
}
fn new_stateless_finish_engine() -> TranscodeDecodeEngine<PrefixCodec, FinishHooks> {
TranscodeDecodeEngine::<_, _>::new(
PrefixCodec,
FinishHooks {
pending_suffix: false,
finish_bound: 0,
},
)
}
#[test]
fn test_transcode_decode_engine_default_matches_new_and_checks_lifecycle() {
let mut default_engine = TranscodeDecodeEngine::<EofAwarePrefixCodec, EofAwareHooks>::default();
let mut explicit_engine =
TranscodeDecodeEngine::<EofAwarePrefixCodec, EofAwareHooks>::new(EofAwarePrefixCodec, EofAwareHooks);
let mut default_output = [0_u8; 2];
let mut explicit_output = [0_u8; 2];
assert_eq!(
Err(TranscodeDecodeError::Failure(TranscodeFailure::TranscodeBeforeReset)),
default_engine.transcode(&[4, 7], 0, &mut default_output, 0),
"default construction must retain the fresh lifecycle guard",
);
default_engine.reset(&mut [], 0).expect("reset default engine");
explicit_engine.reset(&mut [], 0).expect("reset explicit engine");
let default_progress = default_engine
.transcode(&[4, 7], 0, &mut default_output, 0)
.expect("default engine should transcode after reset");
let explicit_progress = explicit_engine
.transcode(&[4, 7], 0, &mut explicit_output, 0)
.expect("new engine should transcode after reset");
let default_finish = default_engine
.finish(&mut default_output, default_progress.written())
.expect("default engine should finish after transcode");
let explicit_finish = explicit_engine
.finish(&mut explicit_output, explicit_progress.written())
.expect("new engine should finish after transcode");
assert_eq!(explicit_progress, default_progress);
assert_eq!(explicit_finish, default_finish);
assert_eq!(
&explicit_output[..explicit_progress.written()],
&default_output[..default_progress.written()]
);
assert_eq!([4, 7], default_output);
}
#[test]
fn test_transcode_decode_engine_lifecycle_rejects_double_finish() {
let mut engine = new_stateless_finish_engine();
let mut reset_output = [];
engine.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 0];
engine
.finish(&mut output, 0)
.expect("first finish should succeed on a stateless decoder");
assert_eq!(
Err(TranscodeDecodeError::Failure(TranscodeFailure::FinishAfterFinish,)),
engine.finish(&mut output, 0),
);
}
#[test]
fn test_transcode_decode_engine_lifecycle_rejects_transcode_after_finish() {
let mut engine = new_stateless_finish_engine();
let mut reset_output = [];
engine.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 0];
engine.finish(&mut output, 0).expect("finish closes the logical stream");
let mut grown = [0_u8; 1];
assert_eq!(
Err(TranscodeDecodeError::Failure(TranscodeFailure::TranscodeAfterFinish,)),
engine.transcode(&[0x10], 0, &mut grown, 0),
);
}
#[test]
fn test_transcode_decode_engine_lifecycle_allows_finish_without_transcode() {
let mut engine = new_stateless_finish_engine();
let mut reset_output = [];
engine.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 0];
let written = engine
.finish(&mut output, 0)
.expect("fresh stateless engine may finalize an empty stream");
assert_eq!(0, written);
}
#[test]
fn test_transcode_decode_engine_lifecycle_allows_finish_retry_after_capacity_failure() {
let mut engine = TranscodeDecodeEngine::<_, _>::new(PrefixCodec, FinishHooks::default());
let mut reset_output = [];
engine.reset(&mut reset_output, 0).expect("initialize stream");
let mut tiny = [0_u8; 0];
let _ = engine
.finish(&mut tiny, 0)
.expect_err("finish should reject insufficient output");
let mut output = [0_u8; 1];
let written = engine
.finish(&mut output, 0)
.expect("retry after capacity failure must succeed");
assert_eq!(1, written);
}
#[test]
fn test_transcode_decode_engine_lifecycle_allows_reuse_after_reset() {
let mut engine = new_stateless_finish_engine();
let mut reset_output = [];
engine.reset(&mut reset_output, 0).expect("initialize stream");
let mut buf = [0_u8; 4];
engine.finish(&mut buf, 0).expect("close the first logical stream");
engine
.reset(&mut buf, 0)
.expect("reset must reopen the engine for a new logical stream");
let progress = engine
.transcode(&[0x10, 0x20], 0, &mut buf, 0)
.expect("transcode should resume after reset");
assert_eq!(2, progress.read());
engine
.finish(&mut buf, 2)
.expect("finish must close the second logical stream");
}
#[test]
fn test_transcode_decode_engine_lifecycle_allows_multiple_resets() {
let mut engine = new_stateless_finish_engine();
let mut buf = [0_u8; 0];
for _ in 0..3 {
engine.reset(&mut buf, 0).expect("reset must always be legal");
}
}
#[test]
fn test_transcode_decode_engine_failed_reset_preserves_finished_state() {
let mut engine = TranscodeDecodeEngine::<_, _>::new(ResetEmittingDecodeCodec, ResetEmittingDecodePassthroughHooks);
let mut reset_output = [0_u8; 1];
engine.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
engine.finish(&mut output, 0).expect("finish closes the logical stream");
let error = engine
.reset(&mut [], 0)
.expect_err("reset should reject insufficient output");
assert_eq!(
codec::TranscodeDecodeError::Failure(codec::TranscodeFailure::insufficient_output(0, 1, 0)),
error
);
assert_eq!(
Err(TranscodeDecodeError::Failure(TranscodeFailure::TranscodeAfterFinish,)),
engine.transcode(&[1_u8], 0, &mut output, 0),
);
}
#[test]
fn test_transcode_decode_engine_failed_finish_poisoned_until_reset() {
let mut engine = TranscodeDecodeEngine::<_, _>::new(FlushFailCodec, FlushMappingHooks);
let mut reset_output = [];
engine.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
let error = engine.finish(&mut output, 0).expect_err("codec finish should fail");
assert_eq!(TranscodeDecodeError::domain_finish(FlushFailError), error);
assert_eq!(
Err(TranscodeDecodeError::Failure(TranscodeFailure::LifecyclePoisoned,)),
engine.finish(&mut output, 0),
);
assert_eq!(
Err(TranscodeDecodeError::Failure(TranscodeFailure::LifecyclePoisoned,)),
engine.transcode(&[1_u8], 0, &mut output, 0),
);
engine
.reset(&mut [], 0)
.expect("successful reset should recover a poisoned engine");
let progress = engine
.transcode(&[1_u8], 0, &mut output, 0)
.expect("transcode should resume after successful reset");
assert_eq!(1, progress.read());
}
#[test]
fn test_transcode_decode_engine_failed_reset_poisoned_until_successful_reset() {
let mut engine = TranscodeDecodeEngine::<_, _>::new(ResetFailCodec::default(), ResetErrorMappingHooks);
let mut output = [0_u8; 1];
let _ = engine
.reset(&mut output, 0)
.expect_err("codec reset should fail after reset execution starts");
assert_eq!(
Err(TranscodeDecodeError::Failure(TranscodeFailure::LifecyclePoisoned,)),
engine.transcode(&[1_u8], 0, &mut output, 0),
);
engine.codec_mut().fail_reset = false;
engine
.reset(&mut output, 0)
.expect("successful reset should recover a poisoned engine");
let progress = engine
.transcode(&[1_u8], 0, &mut output, 0)
.expect("transcode should resume after successful reset");
assert_eq!(1, progress.read());
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct InvalidHookErrorHooks;
impl TranscodeDecodeHooks<PrefixCodec> for InvalidHookErrorHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut PrefixCodec,
error: &PrefixDecodeError,
_consumed: Option<NonZeroUsize>,
context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<PrefixCodec>> {
Err(TranscodeDecodeError::domain_main(*error, context.input_index()))
}
}
#[test]
fn test_transcode_decode_engine_propagates_invalid_decode_hook_errors() {
let mut decoder = TranscodeDecodeEngine::new(PrefixCodec, InvalidHookErrorHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
let error = decoder
.transcode(&[0xff, 1], 0, &mut output, 0)
.expect_err("invalid decode hook errors should propagate");
assert_eq!(
TranscodeDecodeError::domain_main(PrefixDecodeError::Invalid { consumed: 1 }, 0),
error,
);
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct OverflowPlanningDecodeHooks;
impl TranscodeDecodeHooks<PrefixCodec> for OverflowPlanningDecodeHooks {
fn max_transcode_output_len(&self, _codec: &PrefixCodec, _input_len: usize) -> Result<usize, CapacityError> {
Err(CapacityError::OutputLengthOverflow)
}
fn handle_invalid_decode(
&mut self,
_codec: &mut PrefixCodec,
error: &PrefixDecodeError,
_consumed: Option<NonZeroUsize>,
context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<PrefixCodec>> {
Err(TranscodeDecodeError::domain_main(*error, context.input_index()))
}
}
#[test]
fn test_transcode_decode_engine_reports_capacity_failures() {
type Decoder = TranscodeDecodeEngine<PrefixCodec, ReplacingHooks>;
let decoder = Decoder::new(PrefixCodec, ReplacingHooks);
assert_eq!(
Ok(3),
TranscodeDecodeEngine::<PrefixCodec, ReplacingHooks>::max_total_output_len(&decoder, 3,),
);
let overflow_decoder = TranscodeDecodeEngine::<PrefixCodec, OverflowPlanningDecodeHooks>::new(
PrefixCodec,
OverflowPlanningDecodeHooks,
);
assert_eq!(
Err(CapacityError::OutputLengthOverflow),
Transcoder::max_transcode_output_len(&overflow_decoder, 1),
);
assert_eq!(
Err(CapacityError::OutputLengthOverflow),
overflow_decoder.max_total_output_len(1),
);
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct RejectingDecodeHooks;
impl TranscodeDecodeHooks<PrefixCodec> for RejectingDecodeHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut PrefixCodec,
_error: &PrefixDecodeError,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<PrefixCodec>> {
Ok(DecodeInvalidAction::Reject)
}
}
impl TranscodeDecodeHooks<UnknownInvalidCodec> for RejectingDecodeHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut UnknownInvalidCodec,
_error: &PrefixDecodeError,
consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<UnknownInvalidCodec>> {
assert_eq!(None, consumed);
Ok(DecodeInvalidAction::Reject)
}
}
#[test]
fn test_transcode_decode_engine_rejects_invalid_input_via_hooks() {
let mut decoder = TranscodeDecodeEngine::new(PrefixCodec, RejectingDecodeHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let input = [0xff_u8];
let mut output = [0_u8; 1];
let error = decoder
.transcode(&input, 0, &mut output, 0)
.expect_err("reject policy should surface the invalid input");
assert_eq!(
TranscodeDecodeError::domain_main_with_consumed(
PrefixDecodeError::Invalid { consumed: 1 },
0,
Some(nonzero(1)),
),
error,
);
}
#[test]
fn test_transcode_decode_engine_rejects_invalid_unknown_input_via_hooks() {
let mut decoder = TranscodeDecodeEngine::new(UnknownInvalidCodec, RejectingDecodeHooks);
let mut reset_output = [];
decoder.reset(&mut reset_output, 0).expect("initialize stream");
let input = [0xff_u8];
let mut output = [0_u8; 1];
let error = decoder
.transcode(&input, 0, &mut output, 0)
.expect_err("reject policy should surface invalid unknown input");
assert_eq!(
TranscodeDecodeError::domain_main(PrefixDecodeError::Invalid { consumed: 0 }, 0),
error,
);
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct ResetEmittingDecodeCodec;
impl Codec for ResetEmittingDecodeCodec {
type Value = u8;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = Infallible;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_RESET_VALUES: usize = 1;
unsafe fn decode(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
Ok((input[input_index], NonZeroUsize::MIN))
}
unsafe fn encode(
&mut self,
value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
output[output_index] = *value;
Ok(1)
}
unsafe fn decode_reset(&mut self, output: &mut [u8], output_index: usize) -> Result<usize, Self::DecodeError> {
output[output_index] = 0xaa;
Ok(1)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct ResetEmittingDecodePassthroughHooks;
impl TranscodeDecodeHooks<ResetEmittingDecodeCodec> for ResetEmittingDecodePassthroughHooks {
fn handle_invalid_decode(
&mut self,
_codec: &mut ResetEmittingDecodeCodec,
error: &Infallible,
_consumed: Option<NonZeroUsize>,
_context: DecodeContext,
) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<ResetEmittingDecodeCodec>> {
match *error {}
}
}
#[test]
fn test_transcode_decode_engine_max_total_output_len_reports_sum_overflow() {
let decoder = TranscodeDecodeEngine::<ResetEmittingDecodeCodec, ResetEmittingDecodePassthroughHooks>::new(
ResetEmittingDecodeCodec,
ResetEmittingDecodePassthroughHooks,
);
assert_eq!(
Err(CapacityError::OutputLengthOverflow),
decoder.max_total_output_len(usize::MAX),
);
}