qubit-codec 0.17.4

Core codec traits and buffer conversion primitives for Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
// =============================================================================
//    Copyright (c) 2026 Haixing Hu.
//
//    SPDX-License-Identifier: Apache-2.0
//
//    Licensed under the Apache License, Version 2.0.
// =============================================================================
use super::capacity_error::CapacityError;
use super::transcode_failure::TranscodeFailure;
use super::transcode_progress::TranscodeProgress;
use super::transcode_status::TranscodeStatus;

/// Validates one-shot transcode progress and returns completed output length.
///
/// Keeping progress classification independent of the concrete transcoder
/// ensures every implementation receives identical trailing-input and
/// streaming-stop handling.
///
/// # Parameters
///
/// - `progress`: Progress returned by the streaming transcode phase.
/// - `input_len`: Total complete-input length supplied to the one-shot call.
/// - `output_index`: Absolute output index used for the transcode phase.
/// - `output_len`: Total output slice length.
///
/// # Returns
///
/// Returns the number of output units written when progress is complete.
///
/// # Errors
///
/// Returns a trailing-input, incomplete-input, or insufficient-output failure
/// when progress reports a non-complete one-shot result.
///
/// # Panics
///
/// Panics when the transcoder reports progress inconsistent with the supplied
/// input and output bounds.
fn complete_progress_written(
    progress: TranscodeProgress,
    input_len: usize,
    output_index: usize,
    output_len: usize,
) -> Result<usize, TranscodeFailure> {
    assert!(
        progress
            .validate(0, input_len, output_index, output_len.saturating_sub(output_index),)
            .is_ok(),
        "Transcoder::transcode returned invalid progress",
    );
    match progress.status() {
        TranscodeStatus::Complete => Ok(progress.written()),
        TranscodeStatus::NeedOutput { required } => Err(TranscodeFailure::insufficient_output(
            output_index + progress.written(),
            required.get(),
            output_len - output_index - progress.written(),
        )),
        TranscodeStatus::NeedInput { required } => Err(TranscodeFailure::incomplete_input(
            progress.read(),
            required.get(),
            input_len - progress.read(),
        )),
    }
}

/// Adds two independent output-capacity bounds.
///
/// # Parameters
///
/// - `first`: First output-capacity bound.
/// - `second`: Second output-capacity bound.
///
/// # Returns
///
/// Returns the sum of both bounds.
///
/// # Errors
///
/// Returns [`CapacityError::OutputLengthOverflow`] when the sum overflows.
#[inline]
fn add_output_bounds(first: usize, second: usize) -> Result<usize, CapacityError> {
    first.checked_add(second).ok_or(CapacityError::OutputLengthOverflow)
}

/// Adds the reset, transcode, and finish output-capacity bounds.
///
/// # Parameters
///
/// - `reset`: Reset-phase output-capacity bound.
/// - `transcode`: Streaming transcode-phase output-capacity bound.
/// - `finish`: Finish-phase output-capacity bound.
///
/// # Returns
///
/// Returns the complete lifecycle output-capacity bound.
///
/// # Errors
///
/// Returns [`CapacityError::OutputLengthOverflow`] when either addition
/// overflows.
#[inline]
fn sum_output_bounds(reset: usize, transcode: usize, finish: usize) -> Result<usize, CapacityError> {
    let before_finish = add_output_bounds(reset, transcode)?;
    add_output_bounds(before_finish, finish)
}

/// Converts one logical stream of input units into one logical stream of output
/// units.
///
/// `transcode` is the main streaming API. It transforms a provided input
/// segment and writes as much output as available buffer space allows. A
/// successful `transcode` call must stop with exactly one of three meanings:
/// all visible input was consumed (`Complete`), an incomplete tail remains
/// caller-owned (`NeedInput`), or output capacity prevented further progress
/// (`NeedOutput`).
///
/// A transcoder instance has a simple lifecycle:
///
/// 1. A newly created instance is uninitialized; call [`Transcoder::reset`]
///    before the first stream. A reset instance is ready for a new logical
///    stream.
/// 2. Call [`Transcoder::transcode`] zero or more times while input is
///    available.
/// 3. Preserve any tail reported by [`crate::TranscodeStatus::NeedInput`] in
///    the caller-owned input buffer.
/// 4. Call [`Transcoder::finish`] after the caller knows no more input remains
///    and has handled any incomplete tail. Size this final output with
///    [`Transcoder::max_finish_output_len`].
/// 5. After [`Transcoder::finish`] succeeds, call [`Transcoder::reset`] with a
///    buffer sized by [`Transcoder::max_reset_output_len`] before starting
///    another logical stream with the same instance.
///
/// The method is suitable for:
/// - pull-style consumers that call conversion repeatedly as buffers arrive;
/// - bounded output sinks that use `NeedOutput` progress during `transcode`;
/// - stateless and stateful codecs that all return progress-oriented stopping
///   reasons.
///
/// `finish` finalizes retained state only; it does not receive source input and
/// does not reinterpret a tail previously reported by `NeedInput`. For
/// codec-backed decoders, streaming uses
/// [`Codec::decode`](crate::Codec::decode) and an explicit EOF call may use
/// [`Codec::decode_eof`](crate::Codec::decode_eof)
/// through [`Transcoder::transcode_eof`]. If a format needs EOF-aware
/// maximal-munch parsing or must delay whether a prefix is complete until the
/// next chunk or EOF, implement that policy in the codec or override
/// `transcode_eof`.
///
/// `Transcoder` is intentionally independent from any charset
/// semantics:
///
/// - Use `Transcoder` directly for custom, policy-free unit transforms.
/// - Use `Transcoder` when you want to own malformed/unmappable decisions at
///   the call site.
///
/// # Examples
///
/// Streaming byte-to-word decoding:
///
/// ```rust
/// use core::convert::Infallible;
/// use core::num::NonZeroUsize;
///
/// use qubit_codec::CapacityError;
/// use qubit_codec::TranscodeDecodeError;
/// use qubit_codec::TranscodeFailure;
/// use qubit_codec::TranscodeProgress;
/// use qubit_codec::TranscodeStatus;
/// use qubit_codec::Transcoder;
///
/// #[derive(Default)]
/// struct U16BeBytesDecoder;
///
/// impl Transcoder for U16BeBytesDecoder {
///     type Input = u8;
///     type Output = u16;
///     type Error = TranscodeDecodeError<Infallible>;
///
///     fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
///         Ok(input_len / 2)
///     }
///
///     fn reset(
///         &mut self,
///         output: &mut [u16],
///         output_index: usize,
///     ) -> Result<usize, Self::Error> {
///         TranscodeFailure::ensure_output_index(output.len(), output_index)?;
///         Ok(0)
///     }
///
///     fn transcode(
///         &mut self,
///         input: &[u8],
///         input_index: usize,
///         output: &mut [u16],
///         output_index: usize,
///     ) -> Result<TranscodeProgress, Self::Error> {
///         TranscodeFailure::ensure_transcode_indices(
///             input.len(),
///             input_index,
///             output.len(),
///             output_index,
///         )?;
///
///         let mut read = 0;
///         let mut written = 0;
///         while input_index + read + 1 < input.len() {
///             if output_index + written == output.len() {
///                 let status = TranscodeStatus::NeedOutput {
///                     required: NonZeroUsize::MIN,
///                 };
///                 return Ok(TranscodeProgress::new(status, read, written));
///             }
///             let high = input[input_index + read] as u16;
///             let low = input[input_index + read + 1] as u16;
///             output[output_index + written] = (high << 8) | low;
///             read += 2;
///             written += 1;
///         }
///         if input_index + read == input.len() {
///             Ok(TranscodeProgress::complete(read, written))
///         } else {
///             let status = TranscodeStatus::NeedInput {
///                 required: NonZeroUsize::new(2).expect("two is non-zero"),
///             };
///             Ok(TranscodeProgress::new(status, read, written))
///         }
///     }
///
///     fn finish(
///         &mut self,
///         output: &mut [u16],
///         output_index: usize,
///     ) -> Result<usize, Self::Error> {
///         TranscodeFailure::ensure_output_index(output.len(), output_index)?;
///         Ok(0)
///     }
/// }
///
/// let mut transcoder = U16BeBytesDecoder::default();
/// let mut reset_output = [];
/// transcoder
///     .reset(&mut reset_output, 0)
///     .expect("stateless decoder reset cannot fail");
/// let mut output = [0_u16; 1];
/// let progress = transcoder
///     .transcode(&[0x12, 0x34, 0xab, 0xcd], 0, &mut output, 0)
///     .expect("decoding cannot fail");
/// assert_eq!(TranscodeStatus::NeedOutput {
///     required: NonZeroUsize::MIN,
/// }, progress.status());
/// assert_eq!(2, progress.read());
/// assert_eq!(1, progress.written());
/// assert_eq!([0x1234], output);
///
/// let mut output = [0_u16; 2];
/// let progress = transcoder
///     .transcode(&[0x12, 0x34, 0xab], 0, &mut output, 0)
///     .expect("decoding cannot fail");
/// assert_eq!(TranscodeStatus::NeedInput {
///     required: NonZeroUsize::new(2).expect("two is non-zero"),
/// }, progress.status());
/// assert_eq!(2, progress.read());
/// assert_eq!(1, progress.written());
/// assert_eq!([0x1234, 0], output);
///
/// assert!(matches!(
///     transcoder.transcode(&[0x12], 2, &mut output, 0),
///     Err(TranscodeDecodeError::Failure(
///         TranscodeFailure::InvalidInputIndex { .. }
///     )),
/// ));
/// assert!(matches!(
///     transcoder.transcode(&[0x12], 0, &mut output, 3),
///     Err(TranscodeDecodeError::Failure(
///         TranscodeFailure::InvalidOutputIndex { .. }
///     )),
/// ));
/// ```
///
/// The trait is intentionally independent from charset concepts. Implementors
/// use `input_index` and `output_index` as absolute positions in the supplied
/// slices. Returned progress counters are relative counts from those positions.
/// For raw codecs this gives a compact API; higher-level workflows can wrap
/// this trait with their own semantic policies.
pub trait Transcoder {
    /// Input unit type accepted by this transcoder.
    type Input;

    /// Output unit type produced by this transcoder.
    type Output;

    /// Complete error type produced by this transcoder.
    type Error: From<TranscodeFailure>;

    /// Returns an upper bound for output units emitted when resetting stream
    /// state.
    ///
    /// Stateful encoders may need a stream-start sequence, such as a byte
    /// order mark, before the first encoded value. Callers use this bound to
    /// size the output buffer passed to [`Transcoder::reset`].
    ///
    /// The bound may depend on immutable transcoder configuration, but it must
    /// cover reset output from every reachable transient stream state. It must
    /// not shrink merely because the current stream has already been reset or
    /// finished.
    ///
    /// # Returns
    ///
    /// Returns `Ok(bound)` when the upper bound can be represented as `usize`.
    /// Stateless transcoders default to `Ok(0)`.
    ///
    /// # Errors
    ///
    /// Implementations return [`CapacityError::OutputLengthOverflow`] when
    /// capacity arithmetic overflows. The default never fails.
    #[inline]
    fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
        Ok(0)
    }

    /// Returns an upper bound for the output capacity needed to transcode
    /// `input_len` units during the streaming phase.
    ///
    /// This bound excludes stream-start output emitted by
    /// [`Transcoder::reset`] and final output emitted by
    /// [`Transcoder::finish`]. Callers that need a complete one-shot stream
    /// bound should use [`Transcoder::max_total_output_len`].
    ///
    /// The bound covers both [`Transcoder::transcode`] and
    /// [`Transcoder::transcode_eof`], including any conservative capacity
    /// preflight required by those operations. It must provide enough writable
    /// capacity for the implementation to complete its preflight, even when
    /// the actual output would use fewer units.
    ///
    /// The bound may depend on immutable transcoder configuration and
    /// `input_len`, but it must cover the streaming output reachable from every
    /// transient stream state. In particular, it must include the maximum
    /// retained output that may be emitted before or alongside output derived
    /// from the supplied input, even when no output is currently retained.
    ///
    /// # Parameters
    ///
    /// - `input_len`: Number of input units the caller plans to transcode.
    ///
    /// # Returns
    ///
    /// Returns `Ok(bound)` when the upper bound can be represented as `usize`.
    ///
    /// # Errors
    ///
    /// Returns [`CapacityError::OutputLengthOverflow`] when capacity arithmetic
    /// overflows.
    fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError>;

    /// Returns an upper bound for a complete `reset -> transcode_eof -> finish`
    /// stream.
    ///
    /// This is the one-shot bound for [`Transcoder::transcode_complete_into`]
    /// and a convenience sum of [`Transcoder::max_reset_output_len`],
    /// [`Transcoder::max_transcode_output_len`], and
    /// [`Transcoder::max_finish_output_len`]. Each component is independent of
    /// transient stream state, so the sum is valid before a full one-shot
    /// stream regardless of how the previous logical stream ended.
    ///
    /// # Parameters
    ///
    /// - `input_len`: Number of input units in the complete stream.
    ///
    /// # Returns
    ///
    /// Returns `Ok(bound)` when the full-stream upper bound can be represented
    /// as `usize`.
    ///
    /// # Errors
    ///
    /// Returns [`CapacityError::OutputLengthOverflow`] when a component bound
    /// or the sum of lifecycle bounds overflows.
    #[inline]
    fn max_total_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
        let reset = self.max_reset_output_len()?;
        let transcode = self.max_transcode_output_len(input_len)?;
        let finish = self.max_finish_output_len()?;
        sum_output_bounds(reset, transcode, finish)
    }

    /// Returns an upper bound for output units produced by stream finalization.
    ///
    /// The bound may depend on immutable transcoder configuration, but it must
    /// cover final output from every reachable transient stream state. For
    /// example, an encoder that can emit one checksum byte must return at least
    /// `1` before input, while accumulating the checksum, and after finishing a
    /// previous stream. It must not shrink merely because no finish output is
    /// currently pending.
    ///
    /// # Returns
    ///
    /// Returns `Ok(bound)` when the upper bound can be represented as `usize`.
    /// Stateless transcoders default to `Ok(0)`.
    ///
    /// # Errors
    ///
    /// Implementations return [`CapacityError::OutputLengthOverflow`] when
    /// capacity arithmetic overflows. The default never fails.
    #[inline]
    fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
        Ok(0)
    }

    /// Resets stream state and emits stream-start output into `output`.
    ///
    /// This starts a new logical stream while keeping configuration such as
    /// byte order, charset policy, replacement values, and cryptographic keys.
    /// Pending input, pending output, and completed-stream state must be
    /// discarded by stateful implementations. The caller must provide enough
    /// output capacity for [`Transcoder::max_reset_output_len`].
    ///
    /// # Parameters
    ///
    /// - `output`: Complete output unit slice visible to the transcoder.
    /// - `output_index`: Absolute output unit index where writing starts.
    ///
    /// # Returns
    ///
    /// Returns the number of units written while resetting stream state.
    /// Stateless transcoders return `0`. Implementations must not return a
    /// count greater than [`Transcoder::max_reset_output_len`] or the output
    /// units available from `output_index`.
    ///
    /// # Errors
    ///
    /// Returns contract errors (`invalid_output_index`, `insufficient_output`)
    /// when capacity checks fail, or policy errors when reset itself fails.
    fn reset(&mut self, output: &mut [Self::Output], output_index: usize) -> Result<usize, Self::Error>;

    /// Converts available input units into output units.
    ///
    /// This method processes an input segment without closing the logical input
    /// stream. When the current segment ends in a partial value, the transcoder
    /// reports [`crate::TranscodeStatus::NeedInput`] without consuming that
    /// tail. The caller owns input-buffer refill and EOF incomplete-tail
    /// policy.
    ///
    /// Returning [`crate::TranscodeStatus::Complete`] means all input visible
    /// from `input_index` was consumed. Implementations must not return
    /// `Complete` after consuming only a prefix of the supplied input; they
    /// must instead continue processing, report `NeedInput` for an
    /// incomplete tail, or report `NeedOutput` if output capacity prevents
    /// progress. This invariant is checked by
    /// [`crate::TranscodeProgress::validate`].
    ///
    /// # Parameters
    ///
    /// - `input`: Complete input unit slice visible to the transcoder.
    /// - `input_index`: Absolute input unit index where conversion starts.
    /// - `output`: Complete output unit slice visible to the transcoder.
    /// - `output_index`: Absolute output unit index where writing starts.
    ///
    /// # Returns
    ///
    /// Returns progress describing how many units were consumed and produced
    /// and why conversion stopped. Implementations must keep the returned
    /// counters and status fields consistent with the supplied input and
    /// output ranges. For `Complete`, `read` must equal `input.len() -
    /// input_index`. The default one-shot helper rejects incomplete `Complete`
    /// progress and asserts the remaining progress contract in all builds;
    /// streaming I/O drivers may convert validation failures before advancing
    /// unsafe cursors.
    ///
    /// # Errors
    ///
    /// Returns framework failures for invalid indices, insufficient output,
    /// incomplete input, and capacity overflow. Returns domain errors for
    /// semantic conversion failures that the transcoder's policy does not
    /// absorb. An error does not provide general transactional rollback or
    /// complete progress: the transcoder may already have advanced. Callers
    /// must not replay the original input range unless the concrete
    /// transcoder provides an explicit recovery protocol. Reset may begin a
    /// new logical stream but cannot undo output already delivered.
    fn transcode(
        &mut self,
        input: &[Self::Input],
        input_index: usize,
        output: &mut [Self::Output],
        output_index: usize,
    ) -> Result<TranscodeProgress, Self::Error>;

    /// Converts source units after the caller has established end of input.
    ///
    /// The default preserves ordinary streaming behavior. Implementations that
    /// can resolve a trailing prefix using EOF-aware format rules should
    /// override it. If the default still reports
    /// [`TranscodeStatus::NeedInput`], this method returns
    /// [`TranscodeFailure::IncompleteInput`] instead. The default validates
    /// its input and output indices before delegating, then validates returned
    /// progress before calculating the incomplete-input context. A broken
    /// implementation therefore returns [`TranscodeFailure::InvalidProgress`]
    /// rather than causing arithmetic overflow or advancing invalid cursors.
    ///
    /// # Parameters
    ///
    /// - `input`: Complete input slice, including the final source segment.
    /// - `input_index`: Absolute position where final decoding starts.
    /// - `output`: Destination output slice.
    /// - `output_index`: Absolute output position where writing starts.
    ///
    /// # Returns
    ///
    /// Validated complete or output-limited progress. EOF results should be
    /// `Complete`, `NeedOutput`, or an error; the default never returns a
    /// successful `NeedInput` status at EOF.
    ///
    /// # Errors
    ///
    /// Returns invalid-index errors before calling `transcode`, forwards its
    /// errors, and rejects invalid progress or an incomplete final input tail.
    /// It has the same non-transactional error boundary as [`Self::transcode`]:
    /// the transcoder may have advanced, and callers must not replay the
    /// original input range without an explicit recovery protocol. Reset only
    /// begins a new logical stream; it cannot undo output already delivered.
    #[inline]
    fn transcode_eof(
        &mut self,
        input: &[Self::Input],
        input_index: usize,
        output: &mut [Self::Output],
        output_index: usize,
    ) -> Result<TranscodeProgress, Self::Error> {
        TranscodeFailure::ensure_transcode_indices(input.len(), input_index, output.len(), output_index)?;
        let available_input = input.len() - input_index;
        let available_output = output.len() - output_index;
        let progress = self.transcode(input, input_index, output, output_index)?;
        progress
            .validate(input_index, available_input, output_index, available_output)
            .map_err(TranscodeFailure::invalid_progress)?;
        match progress.status() {
            TranscodeStatus::NeedInput { required } => Err(TranscodeFailure::incomplete_input(
                input_index + progress.read(),
                required.get(),
                available_input - progress.read(),
            )
            .into()),
            _ => Ok(progress),
        }
    }

    /// Finishes internally retained output after all input has been supplied.
    ///
    /// `transcode` handles ordinary input consumption. `finish` is called once
    /// after the caller knows no more input remains and has handled any
    /// incomplete input tail reported by `transcode`. It emits final output
    /// derived from internal state, such as reset bytes, checksums, digests, or
    /// trailers. The caller must provide enough output capacity for
    /// [`Transcoder::max_finish_output_len`].
    ///
    /// After `finish` succeeds, the logical stream is closed. Portable callers
    /// should call [`Transcoder::reset`] with a buffer sized by
    /// [`Transcoder::max_reset_output_len`] before passing input for another
    /// logical stream to the same instance.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use core::convert::Infallible;
    /// use core::num::NonZeroUsize;
    ///
    /// use qubit_codec::CapacityError;
    /// use qubit_codec::TranscodeDecodeError;
    /// use qubit_codec::TranscodeFailure;
    /// use qubit_codec::TranscodeProgress;
    /// use qubit_codec::TranscodeStatus;
    /// use qubit_codec::Transcoder;
    ///
    /// #[derive(Default)]
    /// struct ByteCopy;
    ///
    /// impl Transcoder for ByteCopy {
    ///     type Input = u8;
    ///     type Output = u8;
    ///     type Error = TranscodeDecodeError<Infallible>;
    ///
    ///     fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
    ///         Ok(input_len)
    ///     }
    ///
    ///     fn reset(
    ///         &mut self,
    ///         output: &mut [u8],
    ///         output_index: usize,
    ///     ) -> Result<usize, Self::Error> {
    ///         TranscodeFailure::ensure_output_index(output.len(), output_index)?;
    ///         Ok(0)
    ///     }
    ///
    ///     fn transcode(
    ///         &mut self,
    ///         input: &[u8],
    ///         input_index: usize,
    ///         output: &mut [u8],
    ///         output_index: usize,
    ///     ) -> Result<TranscodeProgress, Self::Error> {
    ///         TranscodeFailure::ensure_transcode_indices(
    ///             input.len(),
    ///             input_index,
    ///             output.len(),
    ///             output_index,
    ///         )?;
    ///         let mut read = 0;
    ///         let mut written = 0;
    ///         while input_index + read < input.len() && output_index + written < output.len() {
    ///             output[output_index + written] = input[input_index + read];
    ///             read += 1;
    ///             written += 1;
    ///         }
    ///         if input_index + read == input.len() {
    ///             Ok(TranscodeProgress::complete(read, written))
    ///         } else {
    ///             let status = TranscodeStatus::NeedOutput {
    ///                 required: NonZeroUsize::MIN,
    ///             };
    ///             Ok(TranscodeProgress::new(
    ///                 status,
    ///                 read,
    ///                 written,
    ///             ))
    ///         }
    ///     }
    ///
    ///     fn finish(
    ///         &mut self,
    ///         output: &mut [u8],
    ///         output_index: usize,
    ///     ) -> Result<usize, Self::Error> {
    ///         TranscodeFailure::ensure_output_index(output.len(), output_index)?;
    ///         Ok(0)
    ///     }
    /// }
    ///
    /// let mut transcoder = ByteCopy::default();
    /// let mut reset_output = [];
    /// transcoder
    ///     .reset(&mut reset_output, 0)
    ///     .expect("stateless transcoder reset cannot fail");
    /// let mut output = [1_u8; 1];
    /// let progress = transcoder
    ///     .transcode(&[7], 0, &mut output, 0)
    ///     .expect("writer consumes one unit");
    /// assert_eq!(TranscodeStatus::Complete, progress.status());
    ///
    /// let error = transcoder.transcode(&[7], 2, &mut output, 0).unwrap_err();
    /// assert!(matches!(
    ///     error.failure_ref(),
    ///     Some(TranscodeFailure::InvalidInputIndex { index: 2, input_len: 1 })
    /// ));
    /// let error = transcoder.transcode(&[7], 0, &mut output, 2).unwrap_err();
    /// assert!(matches!(
    ///     error.failure_ref(),
    ///     Some(TranscodeFailure::InvalidOutputIndex { index: 2, output_len: 1 })
    /// ));
    /// assert_eq!([7], output);
    ///
    /// let written = transcoder
    ///     .finish(&mut output, 1)
    ///     .expect("finish does not emit final state for no-op transcoders");
    /// assert_eq!(0, written);
    /// ```
    ///
    /// # Parameters
    ///
    /// - `output`: Complete output unit slice visible to the transcoder.
    /// - `output_index`: Absolute output unit index where writing starts.
    ///
    /// # Returns
    ///
    /// Returns the number of units written during finalization. Stateless
    /// transcoders return `0`. Implementations must not return a count greater
    /// than [`Transcoder::max_finish_output_len`] or the output units available
    /// from `output_index`.
    ///
    /// # Errors
    ///
    /// Returns contract errors (`invalid_output_index`, `insufficient_output`)
    /// when capacity checks fail, or policy errors when finish itself
    /// fails.
    fn finish(&mut self, output: &mut [Self::Output], output_index: usize) -> Result<usize, Self::Error>;

    /// Runs a complete one-shot `reset -> transcode -> finish` stream.
    ///
    /// The `input` slice is treated as complete input at EOF, and output is
    /// written from the beginning of `output`. Callers that need to operate on
    /// a range inside a larger buffer should slice the input or output
    /// before calling this method.
    ///
    /// Before invoking any lifecycle method, this method queries the
    /// state-independent reset, transcode, and finish bounds and requires
    /// `output` to provide at least the complete upper bound returned by
    /// [`Transcoder::max_total_output_len`] for `input.len()` units.
    ///
    /// # Parameters
    ///
    /// - `input`: Complete input unit slice.
    /// - `output`: Complete output unit slice where the stream starts at index
    ///   `0`.
    ///
    /// # Returns
    ///
    /// Returns the number of output units written to `output`.
    ///
    /// # Errors
    ///
    /// Returns framework errors when the output buffer is too small, when
    /// capacity arithmetic overflows, or when the complete input ends with
    /// an incomplete value. Capacity-bound overflow and insufficient
    /// complete-stream capacity are reported before `reset`, `transcode`, or
    /// `finish` is called, so those preflight failures do not write to
    /// `output` or advance transient stream state through lifecycle methods.
    /// Once reset begins, domain errors, incomplete input, runtime
    /// backpressure caused by an invalid bound implementation, or contract
    /// violations may leave partial output and advanced state; this method
    /// does not provide general transactional rollback.
    ///
    /// # Panics
    ///
    /// Panics when `reset` or `finish` reports writing beyond its declared
    /// bound or the available output, when an overridden `transcode_eof`
    /// returns invalid progress, or when a returned write count cannot be
    /// added to the output cursor. The default `transcode_eof` converts
    /// invalid `transcode` progress into [`TranscodeFailure::InvalidProgress`].
    fn transcode_complete_into(
        &mut self,
        input: &[Self::Input],
        output: &mut [Self::Output],
    ) -> Result<usize, Self::Error> {
        let reset_required = self.max_reset_output_len().map_err(TranscodeFailure::from)?;
        let transcode_required = self
            .max_transcode_output_len(input.len())
            .map_err(TranscodeFailure::from)?;
        let finish_required = self.max_finish_output_len().map_err(TranscodeFailure::from)?;
        let total_required =
            sum_output_bounds(reset_required, transcode_required, finish_required).map_err(TranscodeFailure::from)?;
        TranscodeFailure::ensure_output_capacity(output.len(), 0, total_required)?;

        let reset_written = self.reset(output, 0)?;
        assert!(
            reset_written <= reset_required,
            "Transcoder::reset wrote beyond its bound",
        );
        assert!(
            reset_written <= output.len(),
            "Transcoder::reset wrote beyond available output",
        );
        let mut output_cursor = reset_written;

        let progress = self.transcode_eof(input, 0, output, output_cursor)?;
        let transcode_written = complete_progress_written(progress, input.len(), output_cursor, output.len())?;
        output_cursor = output_cursor
            .checked_add(transcode_written)
            .expect("Transcoder::transcode write count overflowed the output cursor");

        let finish_available = output.len() - output_cursor;
        let finish_written = self.finish(output, output_cursor)?;
        assert!(
            finish_written <= finish_required,
            "Transcoder::finish wrote beyond its bound",
        );
        assert!(
            finish_written <= finish_available,
            "Transcoder::finish wrote beyond available output",
        );
        output_cursor = output_cursor
            .checked_add(finish_written)
            .expect("Transcoder::finish write count overflowed the output cursor");
        Ok(output_cursor)
    }
}