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