Skip to main content

qubit_codec/transcode/
transcoder.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// =============================================================================
8use super::capacity_error::CapacityError;
9use super::transcode_failure::TranscodeFailure;
10use super::transcode_progress::TranscodeProgress;
11use super::transcode_status::TranscodeStatus;
12
13/// Validates one-shot transcode progress and returns completed output length.
14///
15/// Keeping progress classification independent of the concrete transcoder
16/// ensures every implementation receives identical trailing-input and
17/// streaming-stop handling.
18///
19/// # Parameters
20///
21/// - `progress`: Progress returned by the streaming transcode phase.
22/// - `input_len`: Total complete-input length supplied to the one-shot call.
23/// - `output_index`: Absolute output index used for the transcode phase.
24/// - `output_len`: Total output slice length.
25///
26/// # Returns
27///
28/// Returns the number of output units written when progress is complete.
29///
30/// # Errors
31///
32/// Returns a trailing-input, incomplete-input, or insufficient-output failure
33/// when progress reports a non-complete one-shot result.
34///
35/// # Panics
36///
37/// Panics when the transcoder reports progress inconsistent with the supplied
38/// input and output bounds.
39fn complete_progress_written(
40    progress: TranscodeProgress,
41    input_len: usize,
42    output_index: usize,
43    output_len: usize,
44) -> Result<usize, TranscodeFailure> {
45    assert!(
46        progress
47            .validate(0, input_len, output_index, output_len.saturating_sub(output_index),)
48            .is_ok(),
49        "Transcoder::transcode returned invalid progress",
50    );
51    match progress.status() {
52        TranscodeStatus::Complete => Ok(progress.written()),
53        TranscodeStatus::NeedOutput { required } => Err(TranscodeFailure::insufficient_output(
54            output_index + progress.written(),
55            required.get(),
56            output_len - output_index - progress.written(),
57        )),
58        TranscodeStatus::NeedInput { required } => Err(TranscodeFailure::incomplete_input(
59            progress.read(),
60            required.get(),
61            input_len - progress.read(),
62        )),
63    }
64}
65
66/// Adds two independent output-capacity bounds.
67///
68/// # Parameters
69///
70/// - `first`: First output-capacity bound.
71/// - `second`: Second output-capacity bound.
72///
73/// # Returns
74///
75/// Returns the sum of both bounds.
76///
77/// # Errors
78///
79/// Returns [`CapacityError::OutputLengthOverflow`] when the sum overflows.
80#[inline]
81fn add_output_bounds(first: usize, second: usize) -> Result<usize, CapacityError> {
82    first.checked_add(second).ok_or(CapacityError::OutputLengthOverflow)
83}
84
85/// Adds the reset, transcode, and finish output-capacity bounds.
86///
87/// # Parameters
88///
89/// - `reset`: Reset-phase output-capacity bound.
90/// - `transcode`: Streaming transcode-phase output-capacity bound.
91/// - `finish`: Finish-phase output-capacity bound.
92///
93/// # Returns
94///
95/// Returns the complete lifecycle output-capacity bound.
96///
97/// # Errors
98///
99/// Returns [`CapacityError::OutputLengthOverflow`] when either addition
100/// overflows.
101#[inline]
102fn sum_output_bounds(reset: usize, transcode: usize, finish: usize) -> Result<usize, CapacityError> {
103    let before_finish = add_output_bounds(reset, transcode)?;
104    add_output_bounds(before_finish, finish)
105}
106
107/// Converts one logical stream of input units into one logical stream of output
108/// units.
109///
110/// `transcode` is the main streaming API. It transforms a provided input
111/// segment and writes as much output as available buffer space allows. A
112/// successful `transcode` call must stop with exactly one of three meanings:
113/// all visible input was consumed (`Complete`), an incomplete tail remains
114/// caller-owned (`NeedInput`), or output capacity prevented further progress
115/// (`NeedOutput`).
116///
117/// A transcoder instance has a simple lifecycle:
118///
119/// 1. A newly created instance is uninitialized; call [`Transcoder::reset`]
120///    before the first stream. A reset instance is ready for a new logical
121///    stream.
122/// 2. Call [`Transcoder::transcode`] zero or more times while input is
123///    available.
124/// 3. Preserve any tail reported by [`crate::TranscodeStatus::NeedInput`] in
125///    the caller-owned input buffer.
126/// 4. Call [`Transcoder::finish`] after the caller knows no more input remains
127///    and has handled any incomplete tail. Size this final output with
128///    [`Transcoder::max_finish_output_len`].
129/// 5. After [`Transcoder::finish`] succeeds, call [`Transcoder::reset`] with a
130///    buffer sized by [`Transcoder::max_reset_output_len`] before starting
131///    another logical stream with the same instance.
132///
133/// The method is suitable for:
134/// - pull-style consumers that call conversion repeatedly as buffers arrive;
135/// - bounded output sinks that use `NeedOutput` progress during `transcode`;
136/// - stateless and stateful codecs that all return progress-oriented stopping
137///   reasons.
138///
139/// `finish` finalizes retained state only; it does not receive source input and
140/// does not reinterpret a tail previously reported by `NeedInput`. For
141/// codec-backed decoders, streaming uses
142/// [`Codec::decode`](crate::Codec::decode) and an explicit EOF call may use
143/// [`Codec::decode_eof`](crate::Codec::decode_eof)
144/// through [`Transcoder::transcode_eof`]. If a format needs EOF-aware
145/// maximal-munch parsing or must delay whether a prefix is complete until the
146/// next chunk or EOF, implement that policy in the codec or override
147/// `transcode_eof`.
148///
149/// `Transcoder` is intentionally independent from any charset
150/// semantics:
151///
152/// - Use `Transcoder` directly for custom, policy-free unit transforms.
153/// - Use `Transcoder` when you want to own malformed/unmappable decisions at
154///   the call site.
155///
156/// # Examples
157///
158/// Streaming byte-to-word decoding:
159///
160/// ```rust
161/// use core::convert::Infallible;
162/// use core::num::NonZeroUsize;
163///
164/// use qubit_codec::CapacityError;
165/// use qubit_codec::TranscodeDecodeError;
166/// use qubit_codec::TranscodeFailure;
167/// use qubit_codec::TranscodeProgress;
168/// use qubit_codec::TranscodeStatus;
169/// use qubit_codec::Transcoder;
170///
171/// #[derive(Default)]
172/// struct U16BeBytesDecoder;
173///
174/// impl Transcoder for U16BeBytesDecoder {
175///     type Input = u8;
176///     type Output = u16;
177///     type Error = TranscodeDecodeError<Infallible>;
178///
179///     fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
180///         Ok(input_len / 2)
181///     }
182///
183///     fn reset(
184///         &mut self,
185///         output: &mut [u16],
186///         output_index: usize,
187///     ) -> Result<usize, Self::Error> {
188///         TranscodeFailure::ensure_output_index(output.len(), output_index)?;
189///         Ok(0)
190///     }
191///
192///     fn transcode(
193///         &mut self,
194///         input: &[u8],
195///         input_index: usize,
196///         output: &mut [u16],
197///         output_index: usize,
198///     ) -> Result<TranscodeProgress, Self::Error> {
199///         TranscodeFailure::ensure_transcode_indices(
200///             input.len(),
201///             input_index,
202///             output.len(),
203///             output_index,
204///         )?;
205///
206///         let mut read = 0;
207///         let mut written = 0;
208///         while input_index + read + 1 < input.len() {
209///             if output_index + written == output.len() {
210///                 let status = TranscodeStatus::NeedOutput {
211///                     required: NonZeroUsize::MIN,
212///                 };
213///                 return Ok(TranscodeProgress::new(status, read, written));
214///             }
215///             let high = input[input_index + read] as u16;
216///             let low = input[input_index + read + 1] as u16;
217///             output[output_index + written] = (high << 8) | low;
218///             read += 2;
219///             written += 1;
220///         }
221///         if input_index + read == input.len() {
222///             Ok(TranscodeProgress::complete(read, written))
223///         } else {
224///             let status = TranscodeStatus::NeedInput {
225///                 required: NonZeroUsize::new(2).expect("two is non-zero"),
226///             };
227///             Ok(TranscodeProgress::new(status, read, written))
228///         }
229///     }
230///
231///     fn finish(
232///         &mut self,
233///         output: &mut [u16],
234///         output_index: usize,
235///     ) -> Result<usize, Self::Error> {
236///         TranscodeFailure::ensure_output_index(output.len(), output_index)?;
237///         Ok(0)
238///     }
239/// }
240///
241/// let mut transcoder = U16BeBytesDecoder::default();
242/// let mut reset_output = [];
243/// transcoder
244///     .reset(&mut reset_output, 0)
245///     .expect("stateless decoder reset cannot fail");
246/// let mut output = [0_u16; 1];
247/// let progress = transcoder
248///     .transcode(&[0x12, 0x34, 0xab, 0xcd], 0, &mut output, 0)
249///     .expect("decoding cannot fail");
250/// assert_eq!(TranscodeStatus::NeedOutput {
251///     required: NonZeroUsize::MIN,
252/// }, progress.status());
253/// assert_eq!(2, progress.read());
254/// assert_eq!(1, progress.written());
255/// assert_eq!([0x1234], output);
256///
257/// let mut output = [0_u16; 2];
258/// let progress = transcoder
259///     .transcode(&[0x12, 0x34, 0xab], 0, &mut output, 0)
260///     .expect("decoding cannot fail");
261/// assert_eq!(TranscodeStatus::NeedInput {
262///     required: NonZeroUsize::new(2).expect("two is non-zero"),
263/// }, progress.status());
264/// assert_eq!(2, progress.read());
265/// assert_eq!(1, progress.written());
266/// assert_eq!([0x1234, 0], output);
267///
268/// assert!(matches!(
269///     transcoder.transcode(&[0x12], 2, &mut output, 0),
270///     Err(TranscodeDecodeError::Failure(
271///         TranscodeFailure::InvalidInputIndex { .. }
272///     )),
273/// ));
274/// assert!(matches!(
275///     transcoder.transcode(&[0x12], 0, &mut output, 3),
276///     Err(TranscodeDecodeError::Failure(
277///         TranscodeFailure::InvalidOutputIndex { .. }
278///     )),
279/// ));
280/// ```
281///
282/// The trait is intentionally independent from charset concepts. Implementors
283/// use `input_index` and `output_index` as absolute positions in the supplied
284/// slices. Returned progress counters are relative counts from those positions.
285/// For raw codecs this gives a compact API; higher-level workflows can wrap
286/// this trait with their own semantic policies.
287pub trait Transcoder {
288    /// Input unit type accepted by this transcoder.
289    type Input;
290
291    /// Output unit type produced by this transcoder.
292    type Output;
293
294    /// Complete error type produced by this transcoder.
295    type Error: From<TranscodeFailure>;
296
297    /// Returns an upper bound for output units emitted when resetting stream
298    /// state.
299    ///
300    /// Stateful encoders may need a stream-start sequence, such as a byte
301    /// order mark, before the first encoded value. Callers use this bound to
302    /// size the output buffer passed to [`Transcoder::reset`].
303    ///
304    /// The bound may depend on immutable transcoder configuration, but it must
305    /// cover reset output from every reachable transient stream state. It must
306    /// not shrink merely because the current stream has already been reset or
307    /// finished.
308    ///
309    /// # Returns
310    ///
311    /// Returns `Ok(bound)` when the upper bound can be represented as `usize`.
312    /// Stateless transcoders default to `Ok(0)`.
313    ///
314    /// # Errors
315    ///
316    /// Implementations return [`CapacityError::OutputLengthOverflow`] when
317    /// capacity arithmetic overflows. The default never fails.
318    #[inline]
319    fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
320        Ok(0)
321    }
322
323    /// Returns an upper bound for the output capacity needed to transcode
324    /// `input_len` units during the streaming phase.
325    ///
326    /// This bound excludes stream-start output emitted by
327    /// [`Transcoder::reset`] and final output emitted by
328    /// [`Transcoder::finish`]. Callers that need a complete one-shot stream
329    /// bound should use [`Transcoder::max_total_output_len`].
330    ///
331    /// The bound covers both [`Transcoder::transcode`] and
332    /// [`Transcoder::transcode_eof`], including any conservative capacity
333    /// preflight required by those operations. It must provide enough writable
334    /// capacity for the implementation to complete its preflight, even when
335    /// the actual output would use fewer units.
336    ///
337    /// The bound may depend on immutable transcoder configuration and
338    /// `input_len`, but it must cover the streaming output reachable from every
339    /// transient stream state. In particular, it must include the maximum
340    /// retained output that may be emitted before or alongside output derived
341    /// from the supplied input, even when no output is currently retained.
342    ///
343    /// # Parameters
344    ///
345    /// - `input_len`: Number of input units the caller plans to transcode.
346    ///
347    /// # Returns
348    ///
349    /// Returns `Ok(bound)` when the upper bound can be represented as `usize`.
350    ///
351    /// # Errors
352    ///
353    /// Returns [`CapacityError::OutputLengthOverflow`] when capacity arithmetic
354    /// overflows.
355    fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError>;
356
357    /// Returns an upper bound for a complete `reset -> transcode_eof -> finish`
358    /// stream.
359    ///
360    /// This is the one-shot bound for [`Transcoder::transcode_complete_into`]
361    /// and a convenience sum of [`Transcoder::max_reset_output_len`],
362    /// [`Transcoder::max_transcode_output_len`], and
363    /// [`Transcoder::max_finish_output_len`]. Each component is independent of
364    /// transient stream state, so the sum is valid before a full one-shot
365    /// stream regardless of how the previous logical stream ended.
366    ///
367    /// # Parameters
368    ///
369    /// - `input_len`: Number of input units in the complete stream.
370    ///
371    /// # Returns
372    ///
373    /// Returns `Ok(bound)` when the full-stream upper bound can be represented
374    /// as `usize`.
375    ///
376    /// # Errors
377    ///
378    /// Returns [`CapacityError::OutputLengthOverflow`] when a component bound
379    /// or the sum of lifecycle bounds overflows.
380    #[inline]
381    fn max_total_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
382        let reset = self.max_reset_output_len()?;
383        let transcode = self.max_transcode_output_len(input_len)?;
384        let finish = self.max_finish_output_len()?;
385        sum_output_bounds(reset, transcode, finish)
386    }
387
388    /// Returns an upper bound for output units produced by stream finalization.
389    ///
390    /// The bound may depend on immutable transcoder configuration, but it must
391    /// cover final output from every reachable transient stream state. For
392    /// example, an encoder that can emit one checksum byte must return at least
393    /// `1` before input, while accumulating the checksum, and after finishing a
394    /// previous stream. It must not shrink merely because no finish output is
395    /// currently pending.
396    ///
397    /// # Returns
398    ///
399    /// Returns `Ok(bound)` when the upper bound can be represented as `usize`.
400    /// Stateless transcoders default to `Ok(0)`.
401    ///
402    /// # Errors
403    ///
404    /// Implementations return [`CapacityError::OutputLengthOverflow`] when
405    /// capacity arithmetic overflows. The default never fails.
406    #[inline]
407    fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
408        Ok(0)
409    }
410
411    /// Resets stream state and emits stream-start output into `output`.
412    ///
413    /// This starts a new logical stream while keeping configuration such as
414    /// byte order, charset policy, replacement values, and cryptographic keys.
415    /// Pending input, pending output, and completed-stream state must be
416    /// discarded by stateful implementations. The caller must provide enough
417    /// output capacity for [`Transcoder::max_reset_output_len`].
418    ///
419    /// # Parameters
420    ///
421    /// - `output`: Complete output unit slice visible to the transcoder.
422    /// - `output_index`: Absolute output unit index where writing starts.
423    ///
424    /// # Returns
425    ///
426    /// Returns the number of units written while resetting stream state.
427    /// Stateless transcoders return `0`. Implementations must not return a
428    /// count greater than [`Transcoder::max_reset_output_len`] or the output
429    /// units available from `output_index`.
430    ///
431    /// # Errors
432    ///
433    /// Returns contract errors (`invalid_output_index`, `insufficient_output`)
434    /// when capacity checks fail, or policy errors when reset itself fails.
435    fn reset(&mut self, output: &mut [Self::Output], output_index: usize) -> Result<usize, Self::Error>;
436
437    /// Converts available input units into output units.
438    ///
439    /// This method processes an input segment without closing the logical input
440    /// stream. When the current segment ends in a partial value, the transcoder
441    /// reports [`crate::TranscodeStatus::NeedInput`] without consuming that
442    /// tail. The caller owns input-buffer refill and EOF incomplete-tail
443    /// policy.
444    ///
445    /// Returning [`crate::TranscodeStatus::Complete`] means all input visible
446    /// from `input_index` was consumed. Implementations must not return
447    /// `Complete` after consuming only a prefix of the supplied input; they
448    /// must instead continue processing, report `NeedInput` for an
449    /// incomplete tail, or report `NeedOutput` if output capacity prevents
450    /// progress. This invariant is checked by
451    /// [`crate::TranscodeProgress::validate`].
452    ///
453    /// # Parameters
454    ///
455    /// - `input`: Complete input unit slice visible to the transcoder.
456    /// - `input_index`: Absolute input unit index where conversion starts.
457    /// - `output`: Complete output unit slice visible to the transcoder.
458    /// - `output_index`: Absolute output unit index where writing starts.
459    ///
460    /// # Returns
461    ///
462    /// Returns progress describing how many units were consumed and produced
463    /// and why conversion stopped. Implementations must keep the returned
464    /// counters and status fields consistent with the supplied input and
465    /// output ranges. For `Complete`, `read` must equal `input.len() -
466    /// input_index`. The default one-shot helper rejects incomplete `Complete`
467    /// progress and asserts the remaining progress contract in all builds;
468    /// streaming I/O drivers may convert validation failures before advancing
469    /// unsafe cursors.
470    ///
471    /// # Errors
472    ///
473    /// Returns framework failures for invalid indices, insufficient output,
474    /// incomplete input, and capacity overflow. Returns domain errors for
475    /// semantic conversion failures that the transcoder's policy does not
476    /// absorb. An error does not provide general transactional rollback or
477    /// complete progress: the transcoder may already have advanced. Callers
478    /// must not replay the original input range unless the concrete
479    /// transcoder provides an explicit recovery protocol. Reset may begin a
480    /// new logical stream but cannot undo output already delivered.
481    fn transcode(
482        &mut self,
483        input: &[Self::Input],
484        input_index: usize,
485        output: &mut [Self::Output],
486        output_index: usize,
487    ) -> Result<TranscodeProgress, Self::Error>;
488
489    /// Converts source units after the caller has established end of input.
490    ///
491    /// The default preserves ordinary streaming behavior. Implementations that
492    /// can resolve a trailing prefix using EOF-aware format rules should
493    /// override it. If the default still reports
494    /// [`TranscodeStatus::NeedInput`], this method returns
495    /// [`TranscodeFailure::IncompleteInput`] instead. The default validates
496    /// its input and output indices before delegating, then validates returned
497    /// progress before calculating the incomplete-input context. A broken
498    /// implementation therefore returns [`TranscodeFailure::InvalidProgress`]
499    /// rather than causing arithmetic overflow or advancing invalid cursors.
500    ///
501    /// # Parameters
502    ///
503    /// - `input`: Complete input slice, including the final source segment.
504    /// - `input_index`: Absolute position where final decoding starts.
505    /// - `output`: Destination output slice.
506    /// - `output_index`: Absolute output position where writing starts.
507    ///
508    /// # Returns
509    ///
510    /// Validated complete or output-limited progress. EOF results should be
511    /// `Complete`, `NeedOutput`, or an error; the default never returns a
512    /// successful `NeedInput` status at EOF.
513    ///
514    /// # Errors
515    ///
516    /// Returns invalid-index errors before calling `transcode`, forwards its
517    /// errors, and rejects invalid progress or an incomplete final input tail.
518    /// It has the same non-transactional error boundary as [`Self::transcode`]:
519    /// the transcoder may have advanced, and callers must not replay the
520    /// original input range without an explicit recovery protocol. Reset only
521    /// begins a new logical stream; it cannot undo output already delivered.
522    #[inline]
523    fn transcode_eof(
524        &mut self,
525        input: &[Self::Input],
526        input_index: usize,
527        output: &mut [Self::Output],
528        output_index: usize,
529    ) -> Result<TranscodeProgress, Self::Error> {
530        TranscodeFailure::ensure_transcode_indices(input.len(), input_index, output.len(), output_index)?;
531        let available_input = input.len() - input_index;
532        let available_output = output.len() - output_index;
533        let progress = self.transcode(input, input_index, output, output_index)?;
534        progress
535            .validate(input_index, available_input, output_index, available_output)
536            .map_err(TranscodeFailure::invalid_progress)?;
537        match progress.status() {
538            TranscodeStatus::NeedInput { required } => Err(TranscodeFailure::incomplete_input(
539                input_index + progress.read(),
540                required.get(),
541                available_input - progress.read(),
542            )
543            .into()),
544            _ => Ok(progress),
545        }
546    }
547
548    /// Finishes internally retained output after all input has been supplied.
549    ///
550    /// `transcode` handles ordinary input consumption. `finish` is called once
551    /// after the caller knows no more input remains and has handled any
552    /// incomplete input tail reported by `transcode`. It emits final output
553    /// derived from internal state, such as reset bytes, checksums, digests, or
554    /// trailers. The caller must provide enough output capacity for
555    /// [`Transcoder::max_finish_output_len`].
556    ///
557    /// After `finish` succeeds, the logical stream is closed. Portable callers
558    /// should call [`Transcoder::reset`] with a buffer sized by
559    /// [`Transcoder::max_reset_output_len`] before passing input for another
560    /// logical stream to the same instance.
561    ///
562    /// # Examples
563    ///
564    /// ```rust
565    /// use core::convert::Infallible;
566    /// use core::num::NonZeroUsize;
567    ///
568    /// use qubit_codec::CapacityError;
569    /// use qubit_codec::TranscodeDecodeError;
570    /// use qubit_codec::TranscodeFailure;
571    /// use qubit_codec::TranscodeProgress;
572    /// use qubit_codec::TranscodeStatus;
573    /// use qubit_codec::Transcoder;
574    ///
575    /// #[derive(Default)]
576    /// struct ByteCopy;
577    ///
578    /// impl Transcoder for ByteCopy {
579    ///     type Input = u8;
580    ///     type Output = u8;
581    ///     type Error = TranscodeDecodeError<Infallible>;
582    ///
583    ///     fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
584    ///         Ok(input_len)
585    ///     }
586    ///
587    ///     fn reset(
588    ///         &mut self,
589    ///         output: &mut [u8],
590    ///         output_index: usize,
591    ///     ) -> Result<usize, Self::Error> {
592    ///         TranscodeFailure::ensure_output_index(output.len(), output_index)?;
593    ///         Ok(0)
594    ///     }
595    ///
596    ///     fn transcode(
597    ///         &mut self,
598    ///         input: &[u8],
599    ///         input_index: usize,
600    ///         output: &mut [u8],
601    ///         output_index: usize,
602    ///     ) -> Result<TranscodeProgress, Self::Error> {
603    ///         TranscodeFailure::ensure_transcode_indices(
604    ///             input.len(),
605    ///             input_index,
606    ///             output.len(),
607    ///             output_index,
608    ///         )?;
609    ///         let mut read = 0;
610    ///         let mut written = 0;
611    ///         while input_index + read < input.len() && output_index + written < output.len() {
612    ///             output[output_index + written] = input[input_index + read];
613    ///             read += 1;
614    ///             written += 1;
615    ///         }
616    ///         if input_index + read == input.len() {
617    ///             Ok(TranscodeProgress::complete(read, written))
618    ///         } else {
619    ///             let status = TranscodeStatus::NeedOutput {
620    ///                 required: NonZeroUsize::MIN,
621    ///             };
622    ///             Ok(TranscodeProgress::new(
623    ///                 status,
624    ///                 read,
625    ///                 written,
626    ///             ))
627    ///         }
628    ///     }
629    ///
630    ///     fn finish(
631    ///         &mut self,
632    ///         output: &mut [u8],
633    ///         output_index: usize,
634    ///     ) -> Result<usize, Self::Error> {
635    ///         TranscodeFailure::ensure_output_index(output.len(), output_index)?;
636    ///         Ok(0)
637    ///     }
638    /// }
639    ///
640    /// let mut transcoder = ByteCopy::default();
641    /// let mut reset_output = [];
642    /// transcoder
643    ///     .reset(&mut reset_output, 0)
644    ///     .expect("stateless transcoder reset cannot fail");
645    /// let mut output = [1_u8; 1];
646    /// let progress = transcoder
647    ///     .transcode(&[7], 0, &mut output, 0)
648    ///     .expect("writer consumes one unit");
649    /// assert_eq!(TranscodeStatus::Complete, progress.status());
650    ///
651    /// let error = transcoder.transcode(&[7], 2, &mut output, 0).unwrap_err();
652    /// assert!(matches!(
653    ///     error.failure_ref(),
654    ///     Some(TranscodeFailure::InvalidInputIndex { index: 2, input_len: 1 })
655    /// ));
656    /// let error = transcoder.transcode(&[7], 0, &mut output, 2).unwrap_err();
657    /// assert!(matches!(
658    ///     error.failure_ref(),
659    ///     Some(TranscodeFailure::InvalidOutputIndex { index: 2, output_len: 1 })
660    /// ));
661    /// assert_eq!([7], output);
662    ///
663    /// let written = transcoder
664    ///     .finish(&mut output, 1)
665    ///     .expect("finish does not emit final state for no-op transcoders");
666    /// assert_eq!(0, written);
667    /// ```
668    ///
669    /// # Parameters
670    ///
671    /// - `output`: Complete output unit slice visible to the transcoder.
672    /// - `output_index`: Absolute output unit index where writing starts.
673    ///
674    /// # Returns
675    ///
676    /// Returns the number of units written during finalization. Stateless
677    /// transcoders return `0`. Implementations must not return a count greater
678    /// than [`Transcoder::max_finish_output_len`] or the output units available
679    /// from `output_index`.
680    ///
681    /// # Errors
682    ///
683    /// Returns contract errors (`invalid_output_index`, `insufficient_output`)
684    /// when capacity checks fail, or policy errors when finish itself
685    /// fails.
686    fn finish(&mut self, output: &mut [Self::Output], output_index: usize) -> Result<usize, Self::Error>;
687
688    /// Runs a complete one-shot `reset -> transcode -> finish` stream.
689    ///
690    /// The `input` slice is treated as complete input at EOF, and output is
691    /// written from the beginning of `output`. Callers that need to operate on
692    /// a range inside a larger buffer should slice the input or output
693    /// before calling this method.
694    ///
695    /// Before invoking any lifecycle method, this method queries the
696    /// state-independent reset, transcode, and finish bounds and requires
697    /// `output` to provide at least the complete upper bound returned by
698    /// [`Transcoder::max_total_output_len`] for `input.len()` units.
699    ///
700    /// # Parameters
701    ///
702    /// - `input`: Complete input unit slice.
703    /// - `output`: Complete output unit slice where the stream starts at index
704    ///   `0`.
705    ///
706    /// # Returns
707    ///
708    /// Returns the number of output units written to `output`.
709    ///
710    /// # Errors
711    ///
712    /// Returns framework errors when the output buffer is too small, when
713    /// capacity arithmetic overflows, or when the complete input ends with
714    /// an incomplete value. Capacity-bound overflow and insufficient
715    /// complete-stream capacity are reported before `reset`, `transcode`, or
716    /// `finish` is called, so those preflight failures do not write to
717    /// `output` or advance transient stream state through lifecycle methods.
718    /// Once reset begins, domain errors, incomplete input, runtime
719    /// backpressure caused by an invalid bound implementation, or contract
720    /// violations may leave partial output and advanced state; this method
721    /// does not provide general transactional rollback.
722    ///
723    /// # Panics
724    ///
725    /// Panics when `reset` or `finish` reports writing beyond its declared
726    /// bound or the available output, when an overridden `transcode_eof`
727    /// returns invalid progress, or when a returned write count cannot be
728    /// added to the output cursor. The default `transcode_eof` converts
729    /// invalid `transcode` progress into [`TranscodeFailure::InvalidProgress`].
730    fn transcode_complete_into(
731        &mut self,
732        input: &[Self::Input],
733        output: &mut [Self::Output],
734    ) -> Result<usize, Self::Error> {
735        let reset_required = self.max_reset_output_len().map_err(TranscodeFailure::from)?;
736        let transcode_required = self
737            .max_transcode_output_len(input.len())
738            .map_err(TranscodeFailure::from)?;
739        let finish_required = self.max_finish_output_len().map_err(TranscodeFailure::from)?;
740        let total_required =
741            sum_output_bounds(reset_required, transcode_required, finish_required).map_err(TranscodeFailure::from)?;
742        TranscodeFailure::ensure_output_capacity(output.len(), 0, total_required)?;
743
744        let reset_written = self.reset(output, 0)?;
745        assert!(
746            reset_written <= reset_required,
747            "Transcoder::reset wrote beyond its bound",
748        );
749        assert!(
750            reset_written <= output.len(),
751            "Transcoder::reset wrote beyond available output",
752        );
753        let mut output_cursor = reset_written;
754
755        let progress = self.transcode_eof(input, 0, output, output_cursor)?;
756        let transcode_written = complete_progress_written(progress, input.len(), output_cursor, output.len())?;
757        output_cursor = output_cursor
758            .checked_add(transcode_written)
759            .expect("Transcoder::transcode write count overflowed the output cursor");
760
761        let finish_available = output.len() - output_cursor;
762        let finish_written = self.finish(output, output_cursor)?;
763        assert!(
764            finish_written <= finish_required,
765            "Transcoder::finish wrote beyond its bound",
766        );
767        assert!(
768            finish_written <= finish_available,
769            "Transcoder::finish wrote beyond available output",
770        );
771        output_cursor = output_cursor
772            .checked_add(finish_written)
773            .expect("Transcoder::finish write count overflowed the output cursor");
774        Ok(output_cursor)
775    }
776}