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}