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/// # Example: streaming byte-to-word decoder
155///
156/// ```rust
157/// use core::num::NonZeroUsize;
158/// use qubit_codec::{
159/// TranscodeDecodeError,
160/// TranscodeProgress,
161/// TranscodeStatus,
162/// Transcoder,
163/// };
164///
165/// #[derive(Default)]
166/// struct U16BeBytesDecoder;
167///
168/// impl Transcoder for U16BeBytesDecoder {
169/// type Input = u8;
170/// type Output = u16;
171/// type Error = TranscodeDecodeError<core::convert::Infallible>;
172///
173/// fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, qubit_codec::CapacityError> {
174/// Ok(input_len / 2)
175/// }
176///
177/// fn reset(
178/// &mut self,
179/// output: &mut [u16],
180/// output_index: usize,
181/// ) -> Result<usize, Self::Error> {
182/// qubit_codec::TranscodeFailure::ensure_output_index(output.len(), output_index)?;
183/// Ok(0)
184/// }
185///
186/// fn transcode(
187/// &mut self,
188/// input: &[u8],
189/// input_index: usize,
190/// output: &mut [u16],
191/// output_index: usize,
192/// ) -> Result<TranscodeProgress, Self::Error> {
193/// qubit_codec::TranscodeFailure::ensure_transcode_indices(
194/// input.len(),
195/// input_index,
196/// output.len(),
197/// output_index,
198/// )?;
199///
200/// let mut read = 0;
201/// let mut written = 0;
202/// while input_index + read + 1 < input.len() {
203/// if output_index + written == output.len() {
204/// let status = TranscodeStatus::NeedOutput {
205/// required: NonZeroUsize::MIN,
206/// };
207/// return Ok(TranscodeProgress::new(status, read, written));
208/// }
209/// let high = input[input_index + read] as u16;
210/// let low = input[input_index + read + 1] as u16;
211/// output[output_index + written] = (high << 8) | low;
212/// read += 2;
213/// written += 1;
214/// }
215/// if input_index + read == input.len() {
216/// Ok(TranscodeProgress::complete(read, written))
217/// } else {
218/// let status = TranscodeStatus::NeedInput {
219/// required: NonZeroUsize::new(2).expect("two is non-zero"),
220/// };
221/// Ok(TranscodeProgress::new(status, read, written))
222/// }
223/// }
224///
225/// fn finish(
226/// &mut self,
227/// output: &mut [u16],
228/// output_index: usize,
229/// ) -> Result<usize, Self::Error> {
230/// qubit_codec::TranscodeFailure::ensure_output_index(output.len(), output_index)?;
231/// Ok(0)
232/// }
233/// }
234///
235/// let mut transcoder = U16BeBytesDecoder;
236/// let mut reset_output = [];
237/// transcoder
238/// .reset(&mut reset_output, 0)
239/// .expect("stateless decoder reset cannot fail");
240/// let mut output = [0_u16; 1];
241/// let progress = transcoder
242/// .transcode(&[0x12, 0x34, 0xab, 0xcd], 0, &mut output, 0)
243/// .expect("decoding cannot fail");
244/// assert_eq!(TranscodeStatus::NeedOutput {
245/// required: NonZeroUsize::MIN,
246/// }, progress.status());
247/// assert_eq!(2, progress.read());
248/// assert_eq!(1, progress.written());
249/// assert_eq!([0x1234], output);
250///
251/// let mut output = [0_u16; 2];
252/// let progress = transcoder
253/// .transcode(&[0x12, 0x34, 0xab], 0, &mut output, 0)
254/// .expect("decoding cannot fail");
255/// assert_eq!(TranscodeStatus::NeedInput {
256/// required: NonZeroUsize::new(2).expect("two is non-zero"),
257/// }, progress.status());
258/// assert_eq!(2, progress.read());
259/// assert_eq!(1, progress.written());
260/// assert_eq!([0x1234, 0], output);
261///
262/// assert!(matches!(
263/// transcoder.transcode(&[0x12], 2, &mut output, 0),
264/// Err(TranscodeDecodeError::Failure(
265/// qubit_codec::TranscodeFailure::InvalidInputIndex { .. }
266/// )),
267/// ));
268/// assert!(matches!(
269/// transcoder.transcode(&[0x12], 0, &mut output, 3),
270/// Err(TranscodeDecodeError::Failure(
271/// qubit_codec::TranscodeFailure::InvalidOutputIndex { .. }
272/// )),
273/// ));
274/// ```
275///
276/// The trait is intentionally independent from charset concepts. Implementors
277/// use `input_index` and `output_index` as absolute positions in the supplied
278/// slices. Returned progress counters are relative counts from those positions.
279/// For raw codecs this gives a compact API; higher-level workflows can wrap
280/// this trait with their own semantic policies.
281pub trait Transcoder {
282 /// Input unit type accepted by this transcoder.
283 type Input;
284
285 /// Output unit type produced by this transcoder.
286 type Output;
287
288 /// Complete error type produced by this transcoder.
289 type Error: From<TranscodeFailure>;
290
291 /// Returns an upper bound for output units emitted when resetting stream
292 /// state.
293 ///
294 /// Stateful encoders may need a stream-start sequence, such as a byte
295 /// order mark, before the first encoded value. Callers use this bound to
296 /// size the output buffer passed to [`Transcoder::reset`].
297 ///
298 /// The bound may depend on immutable transcoder configuration, but it must
299 /// cover reset output from every reachable transient stream state. It must
300 /// not shrink merely because the current stream has already been reset or
301 /// finished.
302 ///
303 /// # Returns
304 ///
305 /// Returns `Ok(bound)` when the upper bound can be represented as `usize`.
306 /// Returns [`CapacityError::OutputLengthOverflow`] when capacity arithmetic
307 /// overflows. Stateless transcoders default to `Ok(0)`.
308 #[must_use = "capacity planning can fail on overflow"]
309 #[inline(always)]
310 fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
311 Ok(0)
312 }
313
314 /// Returns an upper bound for output units produced from `input_len` units
315 /// during the streaming transcode phase.
316 ///
317 /// This bound excludes stream-start output emitted by
318 /// [`Transcoder::reset`] and final output emitted by
319 /// [`Transcoder::finish`]. Callers that need a complete one-shot stream
320 /// bound should use [`Transcoder::max_total_output_len`].
321 ///
322 /// The bound may depend on immutable transcoder configuration and
323 /// `input_len`, but it must cover the streaming output reachable from every
324 /// transient stream state. In particular, it must include the maximum
325 /// retained output that may be emitted before or alongside output derived
326 /// from the supplied input, even when no output is currently retained.
327 ///
328 /// # Parameters
329 ///
330 /// - `input_len`: Number of input units the caller plans to transcode.
331 ///
332 /// # Returns
333 ///
334 /// Returns `Ok(bound)` when the upper bound can be represented as `usize`.
335 /// Returns [`CapacityError::OutputLengthOverflow`] when capacity arithmetic
336 /// overflows.
337 #[must_use = "capacity planning can fail on overflow"]
338 fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError>;
339
340 /// Returns an upper bound for a complete `reset -> transcode -> finish`
341 /// stream.
342 ///
343 /// This is a convenience sum of [`Transcoder::max_reset_output_len`],
344 /// [`Transcoder::max_transcode_output_len`], and
345 /// [`Transcoder::max_finish_output_len`]. Each component is independent of
346 /// transient stream state, so the sum is valid before a full one-shot
347 /// stream regardless of how the previous logical stream ended.
348 ///
349 /// # Parameters
350 ///
351 /// - `input_len`: Number of input units in the complete stream.
352 ///
353 /// # Returns
354 ///
355 /// Returns `Ok(bound)` when the full-stream upper bound can be represented
356 /// as `usize`. Returns [`CapacityError::OutputLengthOverflow`] when
357 /// capacity arithmetic overflows.
358 #[must_use = "capacity planning can fail on overflow"]
359 #[inline]
360 fn max_total_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
361 let reset = self.max_reset_output_len()?;
362 let transcode = self.max_transcode_output_len(input_len)?;
363 let finish = self.max_finish_output_len()?;
364 sum_output_bounds(reset, transcode, finish)
365 }
366
367 /// Returns an upper bound for output units produced by stream finalization.
368 ///
369 /// The bound may depend on immutable transcoder configuration, but it must
370 /// cover final output from every reachable transient stream state. For
371 /// example, an encoder that can emit one checksum byte must return at least
372 /// `1` before input, while accumulating the checksum, and after finishing a
373 /// previous stream. It must not shrink merely because no finish output is
374 /// currently pending.
375 ///
376 /// # Returns
377 ///
378 /// Returns `Ok(bound)` when the upper bound can be represented as `usize`.
379 /// Returns [`CapacityError::OutputLengthOverflow`] when capacity arithmetic
380 /// overflows. Stateless transcoders default to `Ok(0)`.
381 #[must_use = "capacity planning can fail on overflow"]
382 #[inline(always)]
383 fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
384 Ok(0)
385 }
386
387 /// Resets stream state and emits stream-start output into `output`.
388 ///
389 /// This starts a new logical stream while keeping configuration such as
390 /// byte order, charset policy, replacement values, and cryptographic keys.
391 /// Pending input, pending output, and completed-stream state must be
392 /// discarded by stateful implementations. The caller must provide enough
393 /// output capacity for [`Transcoder::max_reset_output_len`].
394 ///
395 /// # Parameters
396 ///
397 /// - `output`: Complete output unit slice visible to the transcoder.
398 /// - `output_index`: Absolute output unit index where writing starts.
399 ///
400 /// # Returns
401 ///
402 /// Returns the number of units written while resetting stream state.
403 /// Stateless transcoders return `0`. Implementations must not return a
404 /// count greater than [`Transcoder::max_reset_output_len`] or the output
405 /// units available from `output_index`.
406 ///
407 /// # Errors
408 ///
409 /// Returns contract errors (`invalid_output_index`, `insufficient_output`)
410 /// when capacity checks fail, or policy errors when reset itself fails.
411 fn reset(&mut self, output: &mut [Self::Output], output_index: usize) -> Result<usize, Self::Error>;
412
413 /// Converts available input units into output units.
414 ///
415 /// This method processes an input segment without closing the logical input
416 /// stream. When the current segment ends in a partial value, the transcoder
417 /// reports [`crate::TranscodeStatus::NeedInput`] without consuming that
418 /// tail. The caller owns input-buffer refill and EOF incomplete-tail
419 /// policy.
420 ///
421 /// Returning [`crate::TranscodeStatus::Complete`] means all input visible
422 /// from `input_index` was consumed. Implementations must not return
423 /// `Complete` after consuming only a prefix of the supplied input; they
424 /// must instead continue processing, report `NeedInput` for an
425 /// incomplete tail, or report `NeedOutput` if output capacity prevents
426 /// progress. This invariant is checked by
427 /// [`crate::TranscodeProgress::validate`].
428 ///
429 /// # Parameters
430 ///
431 /// - `input`: Complete input unit slice visible to the transcoder.
432 /// - `input_index`: Absolute input unit index where conversion starts.
433 /// - `output`: Complete output unit slice visible to the transcoder.
434 /// - `output_index`: Absolute output unit index where writing starts.
435 ///
436 /// # Returns
437 ///
438 /// Returns progress describing how many units were consumed and produced
439 /// and why conversion stopped. Implementations must keep the returned
440 /// counters and status fields consistent with the supplied input and
441 /// output ranges. For `Complete`, `read` must equal `input.len() -
442 /// input_index`. The default one-shot helper rejects incomplete `Complete`
443 /// progress and asserts the remaining progress contract in all builds;
444 /// streaming I/O drivers may convert validation failures before advancing
445 /// unsafe cursors.
446 ///
447 /// # Errors
448 ///
449 /// Returns framework failures for invalid indices, insufficient output,
450 /// incomplete input, and capacity overflow. Returns domain errors for
451 /// semantic conversion failures that the transcoder's policy does not
452 /// absorb.
453 fn transcode(
454 &mut self,
455 input: &[Self::Input],
456 input_index: usize,
457 output: &mut [Self::Output],
458 output_index: usize,
459 ) -> Result<TranscodeProgress, Self::Error>;
460
461 /// Converts source units after the caller has established end of input.
462 ///
463 /// The default preserves ordinary streaming behavior. Implementations that
464 /// can resolve a trailing prefix using EOF-aware format rules should
465 /// override it. If the default still reports
466 /// [`TranscodeStatus::NeedInput`], this method returns
467 /// [`TranscodeFailure::IncompleteInput`] instead.
468 #[inline]
469 fn transcode_eof(
470 &mut self,
471 input: &[Self::Input],
472 input_index: usize,
473 output: &mut [Self::Output],
474 output_index: usize,
475 ) -> Result<TranscodeProgress, Self::Error> {
476 let progress = self.transcode(input, input_index, output, output_index)?;
477 match progress.status() {
478 TranscodeStatus::NeedInput { required } => Err(TranscodeFailure::incomplete_input(
479 input_index + progress.read(),
480 required.get(),
481 input.len() - input_index - progress.read(),
482 )
483 .into()),
484 _ => Ok(progress),
485 }
486 }
487
488 /// Finishes internally retained output after all input has been supplied.
489 ///
490 /// `transcode` handles ordinary input consumption. `finish` is called once
491 /// after the caller knows no more input remains and has handled any
492 /// incomplete input tail reported by `transcode`. It emits final output
493 /// derived from internal state, such as reset bytes, checksums, digests, or
494 /// trailers. The caller must provide enough output capacity for
495 /// [`Transcoder::max_finish_output_len`].
496 ///
497 /// After `finish` succeeds, the logical stream is closed. Portable callers
498 /// should call [`Transcoder::reset`] with a buffer sized by
499 /// [`Transcoder::max_reset_output_len`] before passing input for another
500 /// logical stream to the same instance.
501 ///
502 /// # Example
503 ///
504 /// ```rust
505 /// use core::num::NonZeroUsize;
506 /// use qubit_codec::{
507 /// TranscodeDecodeError,
508 /// Transcoder,
509 /// TranscodeStatus,
510 /// };
511 ///
512 /// #[derive(Default)]
513 /// struct ByteCopy;
514 ///
515 /// impl Transcoder for ByteCopy {
516 /// type Input = u8;
517 /// type Output = u8;
518 /// type Error = TranscodeDecodeError<core::convert::Infallible>;
519 ///
520 /// fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, qubit_codec::CapacityError> {
521 /// Ok(input_len)
522 /// }
523 ///
524 /// fn reset(
525 /// &mut self,
526 /// output: &mut [u8],
527 /// output_index: usize,
528 /// ) -> Result<usize, Self::Error> {
529 /// qubit_codec::TranscodeFailure::ensure_output_index(output.len(), output_index)?;
530 /// Ok(0)
531 /// }
532 ///
533 /// fn transcode(
534 /// &mut self,
535 /// input: &[u8],
536 /// input_index: usize,
537 /// output: &mut [u8],
538 /// output_index: usize,
539 /// ) -> Result<qubit_codec::TranscodeProgress, Self::Error> {
540 /// let mut read = 0;
541 /// let mut written = 0;
542 /// while input_index + read < input.len() && output_index + written < output.len() {
543 /// output[output_index + written] = input[input_index + read];
544 /// read += 1;
545 /// written += 1;
546 /// }
547 /// if input_index + read == input.len() {
548 /// Ok(qubit_codec::TranscodeProgress::complete(read, written))
549 /// } else {
550 /// let status = qubit_codec::TranscodeStatus::NeedOutput {
551 /// required: NonZeroUsize::MIN,
552 /// };
553 /// Ok(qubit_codec::TranscodeProgress::new(
554 /// status,
555 /// read,
556 /// written,
557 /// ))
558 /// }
559 /// }
560 ///
561 /// fn finish(
562 /// &mut self,
563 /// output: &mut [u8],
564 /// output_index: usize,
565 /// ) -> Result<usize, Self::Error> {
566 /// qubit_codec::TranscodeFailure::ensure_output_index(output.len(), output_index)?;
567 /// Ok(0)
568 /// }
569 /// }
570 ///
571 /// let mut transcoder = ByteCopy;
572 /// let mut reset_output = [];
573 /// transcoder
574 /// .reset(&mut reset_output, 0)
575 /// .expect("stateless transcoder reset cannot fail");
576 /// let mut output = [1_u8; 1];
577 /// let progress = transcoder
578 /// .transcode(&[7], 0, &mut output, 0)
579 /// .expect("writer consumes one unit");
580 /// assert_eq!(TranscodeStatus::Complete, progress.status());
581 ///
582 /// let written = transcoder
583 /// .finish(&mut output, 1)
584 /// .expect("finish does not emit final state for no-op transcoders");
585 /// assert_eq!(0, written);
586 /// ```
587 ///
588 /// # Parameters
589 ///
590 /// - `output`: Complete output unit slice visible to the transcoder.
591 /// - `output_index`: Absolute output unit index where writing starts.
592 ///
593 /// # Returns
594 ///
595 /// Returns the number of units written during finalization. Stateless
596 /// transcoders return `0`. Implementations must not return a count greater
597 /// than [`Transcoder::max_finish_output_len`] or the output units available
598 /// from `output_index`.
599 ///
600 /// # Errors
601 ///
602 /// Returns contract errors (`invalid_output_index`, `insufficient_output`)
603 /// when capacity checks fail, or policy errors when finish itself
604 /// fails.
605 fn finish(&mut self, output: &mut [Self::Output], output_index: usize) -> Result<usize, Self::Error>;
606
607 /// Runs a complete one-shot `reset -> transcode -> finish` stream.
608 ///
609 /// The `input` slice is treated as complete input at EOF, and output is
610 /// written from the beginning of `output`. Callers that need to operate on
611 /// a range inside a larger buffer should slice the input or output
612 /// before calling this method.
613 ///
614 /// Before invoking any lifecycle method, this method queries the
615 /// state-independent reset, transcode, and finish bounds and requires
616 /// `output` to provide at least the complete upper bound returned by
617 /// [`Transcoder::max_total_output_len`] for `input.len()` units.
618 ///
619 /// # Parameters
620 ///
621 /// - `input`: Complete input unit slice.
622 /// - `output`: Complete output unit slice where the stream starts at index
623 /// `0`.
624 ///
625 /// # Returns
626 ///
627 /// Returns the number of output units written to `output`.
628 ///
629 /// # Errors
630 ///
631 /// Returns framework errors when the output buffer is too small, when
632 /// capacity arithmetic overflows, or when the complete input ends with
633 /// an incomplete value. Capacity-bound overflow and insufficient
634 /// complete-stream capacity are reported before `reset`, `transcode`, or
635 /// `finish` is called, so those preflight failures do not write to
636 /// `output` or advance transient stream state through lifecycle methods.
637 /// Once reset begins, domain errors, incomplete input, runtime
638 /// backpressure caused by an invalid bound implementation, or contract
639 /// violations may leave partial output and advanced state; this method
640 /// does not provide general transactional rollback.
641 ///
642 /// # Panics
643 ///
644 /// Panics when `reset` or `finish` reports writing beyond its declared
645 /// bound or the available output, when `transcode` returns invalid
646 /// progress, or when a returned write count cannot be added to the output
647 /// cursor.
648 fn transcode_complete_into(
649 &mut self,
650 input: &[Self::Input],
651 output: &mut [Self::Output],
652 ) -> Result<usize, Self::Error> {
653 let reset_required = self.max_reset_output_len().map_err(TranscodeFailure::from)?;
654 let transcode_required = self
655 .max_transcode_output_len(input.len())
656 .map_err(TranscodeFailure::from)?;
657 let finish_required = self.max_finish_output_len().map_err(TranscodeFailure::from)?;
658 let total_required =
659 sum_output_bounds(reset_required, transcode_required, finish_required).map_err(TranscodeFailure::from)?;
660 TranscodeFailure::ensure_output_capacity(output.len(), 0, total_required)?;
661
662 let reset_written = self.reset(output, 0)?;
663 assert!(
664 reset_written <= reset_required,
665 "Transcoder::reset wrote beyond its bound",
666 );
667 assert!(
668 reset_written <= output.len(),
669 "Transcoder::reset wrote beyond available output",
670 );
671 let mut output_cursor = reset_written;
672
673 let progress = self.transcode_eof(input, 0, output, output_cursor)?;
674 let transcode_written = complete_progress_written(progress, input.len(), output_cursor, output.len())?;
675 output_cursor = output_cursor
676 .checked_add(transcode_written)
677 .expect("Transcoder::transcode write count overflowed the output cursor");
678
679 let finish_available = output.len() - output_cursor;
680 let finish_written = self.finish(output, output_cursor)?;
681 assert!(
682 finish_written <= finish_required,
683 "Transcoder::finish wrote beyond its bound",
684 );
685 assert!(
686 finish_written <= finish_available,
687 "Transcoder::finish wrote beyond available output",
688 );
689 output_cursor = output_cursor
690 .checked_add(finish_written)
691 .expect("Transcoder::finish write count overflowed the output cursor");
692 Ok(output_cursor)
693 }
694}