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}