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