qubit_codec/transcode/engine/transcode_decode_engine.rs
1// =============================================================================
2// Copyright (c) 2026 Haixing Hu.
3//
4// SPDX-License-Identifier: Apache-2.0
5//
6// Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Reusable buffered decoder engine.
9
10use core::num::NonZeroUsize;
11
12use super::super::internal::{
13 applied_decode_invalid_action::AppliedDecodeInvalidAction,
14 decode_state::DecodeState,
15 lifecycle_guard::LifecycleGuard,
16};
17use super::{
18 DecodeContext,
19 DecodeInvalidAction,
20 DecodeOutcome,
21 TranscodeDecodeHooks,
22};
23use crate::codec::assert_unit_bounds;
24use crate::{
25 CapacityError,
26 Codec,
27 DecodeFailure,
28 TranscodeDecodeError,
29 TranscodeDecodeErrorOf,
30 TranscodeDecoder,
31 TranscodeFailure,
32 TranscodeProgress,
33 Transcoder,
34};
35
36/// Reusable buffered decoding engine for codec-backed decoders.
37///
38/// The engine owns the low-level codec and hook object. It keeps the common
39/// buffered decoding loop private: input-index validation, output-capacity
40/// checks, calls to [`Codec::decode`], hook dispatch, and
41/// [`crate::TranscodeStatus`] reporting. Incomplete input tails are left in the
42/// caller-provided input slice; callers own input-buffer refill.
43///
44/// Use this type to build a streaming decoder over a one-value [`Codec`]. The
45/// engine decodes into a caller-provided output slice and returns
46/// [`TranscodeProgress`] instead of allocating. On success it writes decoded
47/// values directly to output. On codec errors it delegates to
48/// [`crate::engine::TranscodeDecodeHooks`], allowing a policy to skip invalid
49/// units, emit a replacement value, or fail.
50///
51/// The engine stops before reading an incomplete value when fewer than
52/// [`Codec::MIN_UNITS_PER_VALUE`] units are available. For variable-width
53/// codecs, the codec may still return an incomplete decode error after that
54/// minimum is satisfied; the engine converts that failure directly into
55/// [`crate::TranscodeStatus::NeedInput`].
56///
57/// For strict decoding that wraps codec errors, use
58/// [`crate::CodecTranscodeDecoder`]. Use `TranscodeDecodeEngine` directly when
59/// invalid input should be repaired, skipped, counted, or otherwise handled by
60/// policy.
61///
62/// # Example
63///
64/// ```rust
65/// use core::num::NonZeroUsize;
66/// use qubit_codec::{
67/// Codec,
68/// TranscodeDecodeErrorOf,
69/// DecodeFailure,
70/// TranscodeStatus,
71/// };
72/// use qubit_codec::engine::{
73/// DecodeContext,
74/// DecodeInvalidAction,
75/// TranscodeDecodeEngine,
76/// TranscodeDecodeHooks,
77/// };
78///
79/// #[derive(Clone, Copy)]
80/// struct ByteCodec;
81///
82/// #[derive(Clone, Copy, Debug, Eq, PartialEq)]
83/// enum ByteDecodeError {
84/// Malformed { consumed: NonZeroUsize },
85/// }
86///
87/// impl Codec for ByteCodec {
88/// type Value = u8;
89/// type Unit = u8;
90/// type DecodeError = ByteDecodeError;
91/// type EncodeError = core::convert::Infallible;
92///
93/// const MIN_UNITS_PER_VALUE: usize = 1;
94/// const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
95/// const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
96///
97/// unsafe fn decode(
98/// &mut self,
99/// input: &[u8],
100/// index: usize,
101/// ) -> Result<(u8, NonZeroUsize), DecodeFailure<Self::DecodeError>> {
102/// match input[index] {
103/// 0xff => Err(DecodeFailure::invalid(
104/// ByteDecodeError::Malformed {
105/// consumed: NonZeroUsize::MIN,
106/// },
107/// NonZeroUsize::MIN,
108/// )),
109/// value => Ok((value, NonZeroUsize::MIN)),
110/// }
111/// }
112///
113/// unsafe fn encode(
114/// &mut self,
115/// value: &u8,
116/// output: &mut [u8],
117/// index: usize,
118/// ) -> Result<usize, Self::EncodeError> {
119/// output[index] = *value;
120/// Ok(1)
121/// }
122/// }
123///
124/// struct ReplacementHooks;
125///
126/// impl TranscodeDecodeHooks<ByteCodec> for ReplacementHooks {
127/// fn handle_invalid_decode(
128/// &mut self,
129/// _codec: &mut ByteCodec,
130/// error: &ByteDecodeError,
131/// consumed: Option<NonZeroUsize>,
132/// _context: DecodeContext,
133/// ) -> Result<DecodeInvalidAction<u8>, TranscodeDecodeErrorOf<ByteCodec>> {
134/// match error {
135/// ByteDecodeError::Malformed { .. } => {
136/// Ok(DecodeInvalidAction::Emit {
137/// value: b'?',
138/// consumed: consumed.expect("codec reported malformed width"),
139/// })
140/// }
141/// }
142/// }
143/// }
144///
145/// let mut engine = TranscodeDecodeEngine::<_, _>::new(ByteCodec, ReplacementHooks);
146/// let input = [b'a', 0xff, b'b'];
147/// let mut output = [0_u8; 3];
148/// let mut reset_output = [];
149/// engine.reset(&mut reset_output, 0)?;
150///
151/// let progress = engine.transcode(&input, 0, &mut output, 0)?;
152/// match progress.status() {
153/// TranscodeStatus::Complete => assert_eq!(&output[..progress.written()], b"a?b"),
154/// TranscodeStatus::NeedInput { .. } => {
155/// // Keep `input[input_index + progress.read()..]`, append more source
156/// // units, and resume.
157/// }
158/// TranscodeStatus::NeedOutput { .. } => {
159/// // Drain `output[..progress.written()]`, then resume with more output
160/// // room.
161/// }
162/// }
163/// # Ok::<(), qubit_codec::TranscodeDecodeError<ByteDecodeError>>(())
164/// ```
165///
166/// # Type Parameters
167///
168/// - `C`: Low-level codec used by the engine.
169/// - `H`: Policy hook object used by the engine.
170#[derive(Debug, Default)]
171pub struct TranscodeDecodeEngine<C, H> {
172 /// Low-level codec used for one-value decoding.
173 pub(super) codec: C,
174 /// Policy hooks used for decode failures.
175 pub(super) hooks: H,
176 /// Guard for the `reset → transcode* → finish` lifecycle in every build
177 /// profile.
178 lifecycle: LifecycleGuard,
179}
180
181/// Adds two independent decoded-value capacity bounds.
182fn add_decode_output_bounds(
183 first: usize,
184 second: usize,
185) -> Result<usize, CapacityError> {
186 first
187 .checked_add(second)
188 .ok_or(CapacityError::OutputLengthOverflow)
189}
190
191/// Adds reset, transcode, and finish decoded-value capacity bounds.
192fn sum_decode_output_bounds(
193 reset: usize,
194 transcode: usize,
195 finish: usize,
196) -> Result<usize, CapacityError> {
197 let before_finish = add_decode_output_bounds(reset, transcode)?;
198 add_decode_output_bounds(before_finish, finish)
199}
200
201impl<C, H> TranscodeDecodeEngine<C, H>
202where
203 C: Codec,
204 H: TranscodeDecodeHooks<C>,
205{
206 /// Creates a buffered decoder engine.
207 ///
208 /// # Parameters
209 ///
210 /// - `codec`: Low-level codec used for one-value decoding.
211 /// - `hooks`: Policy hooks used for decode failures.
212 ///
213 /// # Returns
214 ///
215 /// Returns a buffered decoder engine.
216 ///
217 /// # Compile-Time Checks
218 ///
219 /// Fails to compile when the supplied codec declares a zero decode unit
220 /// bound or when [`Codec::MIN_UNITS_PER_VALUE`] exceeds
221 /// [`Codec::MAX_DECODE_UNITS_PER_VALUE`].
222 #[inline]
223 #[must_use]
224 pub fn new(codec: C, hooks: H) -> Self {
225 assert_unit_bounds::<C>();
226 Self {
227 codec,
228 hooks,
229 lifecycle: LifecycleGuard::new(),
230 }
231 }
232
233 /// Returns the wrapped low-level codec.
234 ///
235 /// # Returns
236 ///
237 /// Returns a shared reference to the codec owned by this engine.
238 #[inline(always)]
239 #[must_use]
240 pub const fn codec(&self) -> &C {
241 &self.codec
242 }
243
244 /// Returns the wrapped low-level codec mutably.
245 ///
246 /// # Returns
247 ///
248 /// Returns a mutable reference to the codec owned by this engine.
249 #[inline(always)]
250 #[must_use]
251 pub fn codec_mut(&mut self) -> &mut C {
252 &mut self.codec
253 }
254
255 /// Returns the decode hooks used by this engine.
256 ///
257 /// # Returns
258 ///
259 /// Returns a shared reference to the hook object owned by this engine.
260 #[inline(always)]
261 #[must_use]
262 pub const fn hooks(&self) -> &H {
263 &self.hooks
264 }
265
266 /// Returns the decode hooks mutably.
267 ///
268 /// # Returns
269 ///
270 /// Returns a mutable reference to the hook object owned by this engine.
271 #[inline(always)]
272 #[must_use]
273 pub fn hooks_mut(&mut self) -> &mut H {
274 &mut self.hooks
275 }
276
277 /// Consumes the engine and returns its codec and hooks.
278 ///
279 /// Any lifecycle state owned by the engine is discarded.
280 ///
281 /// # Returns
282 ///
283 /// Returns the wrapped codec followed by the decode hooks.
284 #[inline(always)]
285 #[must_use]
286 pub fn into_parts(self) -> (C, H) {
287 let Self { codec, hooks, .. } = self;
288 (codec, hooks)
289 }
290
291 /// Returns an upper bound for decoded values produced from `input_len`
292 /// units.
293 ///
294 /// This bound covers only the streaming decode phase. It is delegated to
295 /// [`TranscodeDecodeHooks::max_transcode_output_len`], so it includes hook
296 /// policy and is valid for every reachable transient codec and hook state.
297 /// Downstream decoders must use this engine-level API for capacity planning
298 /// instead of recomputing the bound from [`Codec`] constants.
299 ///
300 /// # Parameters
301 ///
302 /// - `input_len`: Number of source units the caller plans to decode.
303 ///
304 /// # Returns
305 ///
306 /// Returns a conservative upper bound, or a capacity error on arithmetic
307 /// overflow.
308 #[inline(always)]
309 #[must_use = "capacity planning can fail on overflow"]
310 pub fn max_transcode_output_len(
311 &self,
312 input_len: usize,
313 ) -> Result<usize, CapacityError> {
314 self.hooks.max_transcode_output_len(&self.codec, input_len)
315 }
316
317 /// Returns the global maximum values emitted by finishing codec state and
318 /// finishing hook-owned state.
319 ///
320 /// # Returns
321 ///
322 /// Returns the sum of [`Codec::MAX_DECODE_FINISH_VALUES`] and the
323 /// hook-provided final-output bound. The codec finish portion covers values
324 /// written by [`Codec::decode_finish`]; hook implementations must not
325 /// include that portion in
326 /// [`TranscodeDecodeHooks::max_finish_output_len`]. Both component bounds
327 /// cover every reachable transient state.
328 #[inline(always)]
329 #[must_use = "capacity planning can fail on overflow"]
330 pub fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
331 add_decode_output_bounds(
332 C::MAX_DECODE_FINISH_VALUES,
333 self.hooks.max_finish_output_len(&self.codec),
334 )
335 }
336
337 /// Returns the global maximum values emitted when resetting stream state.
338 ///
339 /// Returns [`Codec::MAX_DECODE_RESET_VALUES`] for the wrapped codec.
340 /// Stateless decoders always return `0`.
341 #[inline(always)]
342 #[must_use = "capacity planning can fail on overflow"]
343 pub fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
344 Ok(C::MAX_DECODE_RESET_VALUES)
345 }
346
347 /// Returns the maximum values needed by a complete one-shot decode stream.
348 ///
349 /// The returned bound covers reset output, the streaming decode phase for
350 /// `input_len` units, and finish output. Its components are global across
351 /// transient state. Higher-level complete decode helpers should use this
352 /// engine-level bound instead of recomputing capacity from [`Codec`]
353 /// constants, because hook policy may change streaming or finish output.
354 ///
355 /// # Parameters
356 ///
357 /// - `input_len`: Number of source units in the complete stream.
358 ///
359 /// # Returns
360 ///
361 /// Returns the complete-stream output bound, or a capacity error on
362 /// arithmetic overflow.
363 #[inline]
364 #[must_use = "capacity planning can fail on overflow"]
365 pub fn max_total_output_len(
366 &self,
367 input_len: usize,
368 ) -> Result<usize, CapacityError> {
369 let transcode = self.max_transcode_output_len(input_len)?;
370 let finish = self.max_finish_output_len()?;
371 sum_decode_output_bounds(C::MAX_DECODE_RESET_VALUES, transcode, finish)
372 }
373
374 /// Resets codec decode state, runs reset hooks, and emits stream-start
375 /// values.
376 ///
377 /// The sequence is: validate capacity → run `reset_hooks` → call
378 /// [`Codec::decode_reset`]. Stateless decoders (`MAX_DECODE_RESET_VALUES
379 /// == 0`) write nothing and return `Ok(0)`.
380 ///
381 /// # Parameters
382 ///
383 /// - `output`: Complete output value slice visible to the decoder.
384 /// - `output_index`: Absolute output value index where writing starts.
385 ///
386 /// # Returns
387 ///
388 /// Returns the number of reset values written.
389 ///
390 /// # Errors
391 ///
392 /// Returns framework errors when the caller provides invalid or
393 /// insufficient output capacity. Returns domain errors when codec reset
394 /// or hook reset handling fails. Capacity and index failures occur before
395 /// any reset state is changed. Once reset execution starts, an error
396 /// poisons the engine until a later reset succeeds.
397 pub fn reset(
398 &mut self,
399 output: &mut [C::Value],
400 output_index: usize,
401 ) -> Result<usize, TranscodeDecodeErrorOf<C>> {
402 let required = C::MAX_DECODE_RESET_VALUES;
403 TranscodeFailure::ensure_output_capacity(
404 output.len(),
405 output_index,
406 required,
407 )?;
408 self.lifecycle.on_reset_start();
409 self.hooks.reset_hooks(&mut self.codec);
410 let written = unsafe {
411 // SAFETY: The capacity check above reserves the codec's declared
412 // reset-output bound at `output_index`.
413 self.codec.decode_reset(output, output_index)
414 }
415 .map_err(TranscodeDecodeError::domain_reset)?;
416 assert!(
417 written <= required,
418 "Codec::decode_reset wrote beyond its reset bound",
419 );
420 self.lifecycle.on_reset_success();
421 Ok(written)
422 }
423
424 /// Decodes source units into caller-provided output values.
425 ///
426 /// # Parameters
427 ///
428 /// - `input`: Complete input unit slice visible to the decoder.
429 /// - `input_index`: Absolute input unit index where decoding starts.
430 /// - `output`: Complete output value slice visible to the decoder.
431 /// - `output_index`: Absolute output value index where writing starts.
432 ///
433 /// # Returns
434 ///
435 /// Returns progress describing input units consumed, output values written,
436 /// and why decoding stopped.
437 ///
438 /// # Errors
439 ///
440 /// Returns hook errors when `input_index` is outside `input`, when
441 /// `output_index` is outside `output`, or when a concrete policy hook
442 /// rejects a value. Returns
443 /// [`TranscodeFailure::TranscodeBeforeReset`] when the engine has not
444 /// completed its first reset. Returns
445 /// [`TranscodeFailure::TranscodeAfterFinish`] when the logical stream was
446 /// already finished and has not been reset, or
447 /// [`TranscodeFailure::LifecyclePoisoned`] when an earlier reset or finish
448 /// failed after execution started.
449 pub fn transcode(
450 &mut self,
451 input: &[C::Unit],
452 input_index: usize,
453 output: &mut [C::Value],
454 output_index: usize,
455 ) -> Result<TranscodeProgress, TranscodeDecodeErrorOf<C>> {
456 self.transcode_with_eof(input, input_index, output, output_index, false)
457 }
458
459 /// Decodes source units after the caller has established end of input.
460 ///
461 /// This follows the normal streaming contract, except that codec attempts
462 /// use [`Codec::decode_eof`]. A codec may therefore resolve a trailing
463 /// prefix that would remain incomplete in an open stream.
464 #[inline(always)]
465 pub fn transcode_eof(
466 &mut self,
467 input: &[C::Unit],
468 input_index: usize,
469 output: &mut [C::Value],
470 output_index: usize,
471 ) -> Result<TranscodeProgress, TranscodeDecodeErrorOf<C>> {
472 self.transcode_with_eof(input, input_index, output, output_index, true)
473 }
474
475 fn transcode_with_eof(
476 &mut self,
477 input: &[C::Unit],
478 input_index: usize,
479 output: &mut [C::Value],
480 output_index: usize,
481 end_of_input: bool,
482 ) -> Result<TranscodeProgress, TranscodeDecodeErrorOf<C>> {
483 self.lifecycle.on_transcode()?;
484 TranscodeFailure::ensure_transcode_indices(
485 input.len(),
486 input_index,
487 output.len(),
488 output_index,
489 )?;
490
491 let min_units = NonZeroUsize::new(C::MIN_UNITS_PER_VALUE)
492 .expect("Codec::MIN_UNITS_PER_VALUE is non-zero");
493 let min_units_len = min_units.get();
494 let mut state =
495 DecodeState::new(input, input_index, output, output_index);
496 while state.has_input() {
497 let context = state.context();
498 let available = context.available();
499 if available < min_units_len {
500 return Ok(state.need_input_progress_with(min_units));
501 }
502 if state.needs_output() {
503 return Ok(state.need_output_progress());
504 }
505 let output_index = state.output_cursor();
506 let output = state.output_mut();
507 let (outcome, _) = self.decode_one(
508 input,
509 context,
510 end_of_input,
511 |value, _input_index| {
512 // SAFETY: `needs_output()` returned false, so the output
513 // cursor points at a writable initialized slot.
514 unsafe {
515 *output.get_unchecked_mut(output_index) = value;
516 }
517 },
518 )?;
519 if let Some(progress) = state.apply_decode_outcome(outcome) {
520 return Ok(progress);
521 }
522 }
523
524 Ok(state.complete_progress())
525 }
526
527 /// Finishes codec and hook-owned output after EOF.
528 ///
529 /// Finalization first finishes decode-side codec state through
530 /// [`Codec::decode_finish`], then lets hook implementations finish their
531 /// own retained state. The caller must provide enough output capacity for
532 /// [`TranscodeDecodeEngine::max_finish_output_len`], which includes both
533 /// the codec finish bound and the hook-owned finish bound.
534 ///
535 /// # Parameters
536 ///
537 /// - `output`: Complete output value slice visible to the decoder.
538 /// - `output_index`: Absolute output value index where writing starts.
539 ///
540 /// # Returns
541 ///
542 /// Returns the number of values written by finalization.
543 ///
544 /// # Errors
545 ///
546 /// Returns framework errors when the caller provides invalid or
547 /// insufficient output capacity. Returns domain errors when codec finish or
548 /// hook finalization fails. Returns
549 /// [`TranscodeFailure::FinishBeforeReset`] when the engine has not
550 /// completed its first reset. Returns
551 /// [`TranscodeFailure::FinishAfterFinish`] when the logical stream was
552 /// already finished and has not been reset, or
553 /// [`TranscodeFailure::LifecyclePoisoned`] when an earlier reset or finish
554 /// failed after execution started. Capacity and index failures occur before
555 /// finish execution and remain retryable; later failures poison the engine
556 /// until reset succeeds.
557 ///
558 /// # Panics
559 ///
560 /// Panics when the codec finish writes beyond
561 /// [`Codec::MAX_DECODE_FINISH_VALUES`] or when the combined codec and hook
562 /// finalization writes beyond
563 /// [`TranscodeDecodeEngine::max_finish_output_len`].
564 pub fn finish(
565 &mut self,
566 output: &mut [C::Value],
567 output_index: usize,
568 ) -> Result<usize, TranscodeDecodeErrorOf<C>> {
569 self.lifecycle.on_finish_attempt()?;
570 let required = self.max_finish_output_len()?;
571 TranscodeFailure::ensure_output_capacity(
572 output.len(),
573 output_index,
574 required,
575 )?;
576 self.lifecycle.on_finish_start();
577 let finished =
578 unsafe { self.codec.decode_finish(output, output_index) }
579 .map_err(TranscodeDecodeError::domain_finish)?;
580 assert!(
581 finished <= C::MAX_DECODE_FINISH_VALUES,
582 "Codec::decode_finish wrote beyond its finish bound",
583 );
584 let written = self.hooks.finish_hooks(
585 &mut self.codec,
586 output,
587 output_index + finished,
588 )?;
589 assert!(
590 finished + written <= required,
591 "TranscodeDecodeEngine hook wrote beyond its finish bound",
592 );
593 self.lifecycle.on_finish_success();
594 Ok(finished + written)
595 }
596
597 /// Runs a complete one-shot `reset -> transcode -> finish` decode stream.
598 ///
599 /// The complete input is supplied as `input`, and output starts at index
600 /// `0` in `output`. Callers that need subranges should slice their
601 /// buffers before calling this method. Downstream one-shot decoder
602 /// helpers should call this engine method instead of reproducing the
603 /// reset, transcode, and finish sequence themselves.
604 ///
605 /// # Parameters
606 ///
607 /// - `input`: Complete source unit slice.
608 /// - `output`: Output value slice for the whole decoded stream.
609 ///
610 /// # Returns
611 ///
612 /// Returns the number of output values written.
613 ///
614 /// # Errors
615 ///
616 /// Returns framework errors for insufficient output, capacity overflow, or
617 /// an incomplete EOF tail, and domain errors from reset, decode, or
618 /// finish.
619 #[inline(always)]
620 pub fn transcode_complete_into(
621 &mut self,
622 input: &[C::Unit],
623 output: &mut [C::Value],
624 ) -> Result<usize, TranscodeDecodeErrorOf<C>> {
625 <Self as Transcoder>::transcode_complete_into(self, input, output)
626 }
627
628 /// Decodes one source value attempt and delivers emitted values.
629 ///
630 /// # Parameters
631 ///
632 /// - `input`: Complete input unit slice visible to the caller.
633 /// - `context`: Decode context describing the current source and output
634 /// cursors.
635 /// - `consume`: Callback invoked exactly once when this attempt emits a
636 /// logical value.
637 ///
638 /// # Type Parameters
639 ///
640 /// - `R`: Value returned by the consumer when a decoded value is emitted.
641 /// - `F`: Consumer callback type.
642 ///
643 /// # Returns
644 ///
645 /// Returns the decode outcome and the consumer result when a value was
646 /// emitted.
647 ///
648 /// # Errors
649 ///
650 /// Returns hook errors when the decode policy rejects the input.
651 ///
652 /// # Panics
653 ///
654 /// Panics when the codec reports consumption beyond the available input,
655 /// an incomplete-input requirement beyond
656 /// [`Codec::MAX_DECODE_UNITS_PER_VALUE`], or hooks return an action that
657 /// consumes beyond the available input.
658 pub(crate) fn decode_one<R, F>(
659 &mut self,
660 input: &[C::Unit],
661 context: DecodeContext,
662 end_of_input: bool,
663 consume: F,
664 ) -> Result<(DecodeOutcome, Option<R>), TranscodeDecodeErrorOf<C>>
665 where
666 F: FnOnce(C::Value, usize) -> R,
667 {
668 debug_assert!(
669 context.available() >= C::MIN_UNITS_PER_VALUE,
670 "decode_one requires at least Codec::MIN_UNITS_PER_VALUE input units",
671 );
672
673 // SAFETY: The context reports at least `MIN_UNITS_PER_VALUE` source
674 // units available from `context.input_index()`.
675 let result = unsafe {
676 if end_of_input {
677 self.codec.decode_eof(input, context.input_index())
678 } else {
679 self.codec.decode(input, context.input_index())
680 }
681 };
682 match result {
683 Ok((value, consumed)) => {
684 assert!(
685 consumed.get() <= context.available(),
686 "Codec::decode consumed beyond available input",
687 );
688 assert!(
689 consumed.get() <= C::MAX_DECODE_UNITS_PER_VALUE,
690 "Codec::decode consumed beyond Codec::MAX_DECODE_UNITS_PER_VALUE",
691 );
692 let consumed_value = consume(value, context.input_index());
693 Ok((
694 DecodeOutcome::emitted(consumed, NonZeroUsize::MIN),
695 Some(consumed_value),
696 ))
697 }
698 Err(DecodeFailure::Incomplete {
699 source,
700 required_total,
701 }) => {
702 assert!(
703 required_total.get() > context.available(),
704 "Codec::decode incomplete required_total must exceed available input",
705 );
706 assert!(
707 required_total.get() <= C::MAX_DECODE_UNITS_PER_VALUE,
708 "Codec::decode incomplete required_total exceeded Codec::MAX_DECODE_UNITS_PER_VALUE",
709 );
710 if end_of_input {
711 match source {
712 Some(source) => Err(TranscodeDecodeError::domain_main(
713 source,
714 context.input_index(),
715 )),
716 None => Err(TranscodeFailure::incomplete_input(
717 context.input_index(),
718 required_total.get(),
719 context.available(),
720 )
721 .into()),
722 }
723 } else {
724 Ok((DecodeOutcome::need_input(required_total), None))
725 }
726 }
727 Err(DecodeFailure::Invalid { source, consumed }) => {
728 let action = match self.hooks.handle_invalid_decode(
729 &mut self.codec,
730 &source,
731 consumed,
732 context,
733 )? {
734 DecodeInvalidAction::Reject => {
735 return Err(
736 TranscodeDecodeError::domain_main_with_consumed(
737 source,
738 context.input_index(),
739 consumed,
740 ),
741 );
742 }
743 DecodeInvalidAction::Skip { consumed } => {
744 AppliedDecodeInvalidAction::Skip { consumed }
745 }
746 DecodeInvalidAction::Emit { value, consumed } => {
747 AppliedDecodeInvalidAction::Emit { value, consumed }
748 }
749 };
750 Ok(Self::apply_invalid_decode_action(action, context, consume))
751 }
752 }
753 }
754
755 /// Applies a hook-selected invalid-decode action after reject handling.
756 fn apply_invalid_decode_action<R, F>(
757 action: AppliedDecodeInvalidAction<C::Value>,
758 context: DecodeContext,
759 consume: F,
760 ) -> (DecodeOutcome, Option<R>)
761 where
762 F: FnOnce(C::Value, usize) -> R,
763 {
764 match action {
765 AppliedDecodeInvalidAction::Skip { consumed } => {
766 let read = DecodeInvalidAction::<C::Value>::bound_consumed(
767 consumed,
768 context.available(),
769 );
770 (DecodeOutcome::skipped(read), None)
771 }
772 AppliedDecodeInvalidAction::Emit { value, consumed } => {
773 let read = DecodeInvalidAction::<C::Value>::bound_consumed(
774 consumed,
775 context.available(),
776 );
777 let consumed_value = consume(value, context.input_index());
778 (
779 DecodeOutcome::emitted(read, NonZeroUsize::MIN),
780 Some(consumed_value),
781 )
782 }
783 }
784 }
785}
786
787impl<C, H> Transcoder for TranscodeDecodeEngine<C, H>
788where
789 C: Codec,
790 H: TranscodeDecodeHooks<C>,
791{
792 type Input = C::Unit;
793 type Output = C::Value;
794 type Error = TranscodeDecodeErrorOf<C>;
795
796 /// Returns an upper bound for decoded values produced from `input_len`
797 /// units.
798 #[inline(always)]
799 fn max_transcode_output_len(
800 &self,
801 input_len: usize,
802 ) -> Result<usize, CapacityError> {
803 TranscodeDecodeEngine::max_transcode_output_len(self, input_len)
804 }
805
806 /// Returns an upper bound for values produced by finishing codec and hook
807 /// state.
808 #[inline(always)]
809 fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
810 TranscodeDecodeEngine::max_finish_output_len(self)
811 }
812
813 /// Returns an upper bound for values emitted when resetting stream state.
814 #[inline(always)]
815 fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
816 TranscodeDecodeEngine::max_reset_output_len(self)
817 }
818
819 /// Runs hook-owned cleanup before a logical decoder reset.
820 #[inline(always)]
821 fn reset(
822 &mut self,
823 output: &mut [C::Value],
824 output_index: usize,
825 ) -> Result<usize, TranscodeDecodeErrorOf<C>> {
826 TranscodeDecodeEngine::reset(self, output, output_index)
827 }
828
829 /// Decodes source units into logical values.
830 #[inline(always)]
831 fn transcode(
832 &mut self,
833 input: &[C::Unit],
834 input_index: usize,
835 output: &mut [C::Value],
836 output_index: usize,
837 ) -> Result<TranscodeProgress, TranscodeDecodeErrorOf<C>> {
838 TranscodeDecodeEngine::transcode(
839 self,
840 input,
841 input_index,
842 output,
843 output_index,
844 )
845 }
846
847 /// Decodes source units after end of input is known.
848 #[inline(always)]
849 fn transcode_eof(
850 &mut self,
851 input: &[C::Unit],
852 input_index: usize,
853 output: &mut [C::Value],
854 output_index: usize,
855 ) -> Result<TranscodeProgress, TranscodeDecodeErrorOf<C>> {
856 TranscodeDecodeEngine::transcode_eof(
857 self,
858 input,
859 input_index,
860 output,
861 output_index,
862 )
863 }
864
865 /// Finishes internally retained output after EOF.
866 #[inline(always)]
867 fn finish(
868 &mut self,
869 output: &mut [C::Value],
870 output_index: usize,
871 ) -> Result<usize, TranscodeDecodeErrorOf<C>> {
872 TranscodeDecodeEngine::finish(self, output, output_index)
873 }
874}
875
876impl<C, H> TranscodeDecoder for TranscodeDecodeEngine<C, H>
877where
878 C: Codec,
879 H: TranscodeDecodeHooks<C>,
880{
881 type DecodeError = C::DecodeError;
882}