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
// =============================================================================
//    Copyright (c) 2026 Haixing Hu.
//
//    SPDX-License-Identifier: Apache-2.0
//
//    Licensed under the Apache License, Version 2.0.
// =============================================================================
//! Buffered converter adapter backed by two low-level codecs.

use core::fmt;

use super::super::engine::TranscodeConvertEngine;
use super::CodecTranscodeDecodeHooks;
use super::CodecTranscodeEncodeHooks;
use crate::CapacityError;
use crate::Codec;
use crate::TranscodeConvertErrorOf;
use crate::TranscodeConverter;
use crate::TranscodeProgress;
use crate::Transcoder;

/// Converts source units to target units through a decoded value by using
/// codecs.
///
/// The converter decodes one source value with the decoder codec, then encodes
/// that value with the encoder codec. If the current output buffer cannot hold
/// the encoded value, the already decoded value is retained by the common
/// converter engine and must be drained before more source input is consumed.
/// Incomplete source tails are left in the caller-provided input slice while
/// the stream is open; callers own input-buffer refill. After upstream EOF,
/// call [`Transcoder::transcode_eof`] to let the source codec apply
/// [`Codec::decode_eof`] and the configured incomplete-input policy to the
/// remaining tail, then call [`Transcoder::finish`] to finalize the stream.
/// This supports EOF-aware maximal-munch decoding when the codec expresses it
/// through `decode_eof`. A custom transcoder or facade is needed only when the
/// source framing requires behavior outside that codec EOF contract.
///
/// # Type Parameters
///
/// - `D`: Low-level codec used to decode source units.
/// - `E`: Low-level codec used to encode target units.
///
/// # Examples
///
/// A byte-preserving codec can be adapted to the buffered streaming interface.
///
/// ```
/// use core::convert::Infallible;
/// use core::num::NonZeroUsize;
///
/// use qubit_codec::Codec;
/// use qubit_codec::CodecTranscodeConverter;
/// use qubit_codec::DecodeFailure;
///
/// # struct ByteCodec;
/// # impl Codec for ByteCodec {
/// #     type Value = u8;
/// #     type Unit = u8;
/// #     type DecodeError = Infallible;
/// #     type EncodeError = Infallible;
/// #     const MIN_UNITS_PER_VALUE: usize = 1;
/// #     const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
/// #     const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
/// #     unsafe fn decode(&mut self, input: &[u8], index: usize)
/// #         -> Result<(u8, NonZeroUsize), DecodeFailure<Infallible>> {
/// #         Ok((input[index], NonZeroUsize::MIN))
/// #     }
/// #     unsafe fn encode(&mut self, value: &u8, output: &mut [u8], index: usize)
/// #         -> Result<usize, Infallible> {
/// #         output[index] = *value;
/// #         Ok(1)
/// #     }
/// # }
/// let mut converter = CodecTranscodeConverter::new(ByteCodec, ByteCodec);
/// let mut output = [0_u8; 2];
/// converter.reset(&mut output, 0).unwrap();
/// converter.transcode(&[7, 9], 0, &mut output, 0).unwrap();
/// assert_eq!(output, [7, 9]);
/// ```
#[must_use]
pub struct CodecTranscodeConverter<D, E>
where
    D: Codec,
    E: Codec<Value = D::Value>,
{
    /// Common buffered converter engine.
    engine: TranscodeConvertEngine<D, E, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks>,
}

impl<D, E> CodecTranscodeConverter<D, E>
where
    D: Codec,
    E: Codec<Value = D::Value>,
{
    /// Creates a buffered converter backed by decoder and encoder codecs.
    ///
    /// # Parameters
    ///
    /// - `decoder`: Low-level codec used to decode source units.
    /// - `encoder`: Low-level codec used to encode target units.
    ///
    /// # Returns
    ///
    /// Returns a buffered converter adapter for the supplied codecs.
    #[inline]
    pub fn new(decoder: D, encoder: E) -> Self {
        Self {
            engine: TranscodeConvertEngine::new(decoder, encoder, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks),
        }
    }

    /// Returns a shared reference to the source codec.
    ///
    /// # Returns
    ///
    /// Borrows the source codec without allocating or changing stream state.
    #[inline]
    #[must_use]
    pub fn source_codec(&self) -> &D {
        self.engine.source_codec()
    }

    /// Returns a mutable reference to the source codec.
    ///
    /// Mutating a codec during an active stream can invalidate that stream's
    /// assumptions; reset the adapter before continuing with the new codec
    /// configuration.
    ///
    /// # Returns
    ///
    /// Exclusively borrows the codec while the adapter remains borrowed.
    #[inline]
    #[must_use]
    pub fn source_codec_mut(&mut self) -> &mut D {
        self.engine.source_codec_mut()
    }

    /// Returns a shared reference to the target codec.
    ///
    /// # Returns
    ///
    /// Borrows the target codec without allocating or changing stream state.
    #[inline]
    #[must_use]
    pub fn target_codec(&self) -> &E {
        self.engine.target_codec()
    }

    /// Returns a mutable reference to the target codec.
    ///
    /// Mutating a codec during an active stream can invalidate that stream's
    /// assumptions; reset the adapter before continuing with the new codec
    /// configuration.
    ///
    /// # Returns
    ///
    /// Exclusively borrows the codec while the adapter remains borrowed.
    #[inline]
    #[must_use]
    pub fn target_codec_mut(&mut self) -> &mut E {
        self.engine.target_codec_mut()
    }

    /// Consumes the adapter and returns its source and target codecs.
    ///
    /// Any buffered lifecycle state, pending value, and internal hooks are
    /// discarded.
    ///
    /// # Returns
    ///
    /// Returns the owned source codec followed by the owned target codec.
    #[inline]
    #[must_use]
    pub fn into_codecs(self) -> (D, E) {
        let (source, target, _, _) = self.engine.into_parts();
        (source, target)
    }

    /// Returns an upper bound for target units produced from `input_len` units.
    ///
    /// This concrete adapter method is available even when `D::Value` does not
    /// implement [`Default`].
    ///
    /// # Parameters
    ///
    /// - `input_len`: Source units the caller plans to convert.
    ///
    /// # Returns
    ///
    /// Returns a conservative upper bound for produced target units.
    ///
    /// # Errors
    ///
    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
    /// arithmetic overflows.
    #[inline]
    pub fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
        self.engine.max_transcode_output_len(input_len)
    }

    /// Returns the global maximum target units emitted by finishing internal
    /// state.
    ///
    /// # Returns
    ///
    /// Returns a conservative upper bound valid for every reachable converter
    /// state.
    ///
    /// # Errors
    ///
    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
    /// arithmetic overflows.
    #[inline]
    pub fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
        self.engine.max_finish_output_len()
    }

    /// Returns the maximum target units emitted when resetting stream state.
    ///
    /// # Returns
    ///
    /// Returns a conservative upper bound valid for every reachable converter
    /// state.
    ///
    /// # Errors
    ///
    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
    /// arithmetic overflows.
    #[inline]
    pub fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
        self.engine.max_reset_output_len()
    }

    /// Clears retained pending output, resets target state, and then encodes
    /// source-side reset values.
    ///
    /// `D::Value: Default` is required so the engine can allocate scratch
    /// storage for any stream-start values the source decoder emits through
    /// [`Codec::decode_reset`] before they are
    /// piped through the target encoder. Stateless decoders never reach the
    /// allocating path; the bound is consulted only when
    /// [`Codec::MAX_DECODE_RESET_VALUES`]
    /// is non-zero.
    ///
    /// # Parameters
    ///
    /// - `output`: Target unit slice for stream-start output.
    /// - `output_index`: Absolute target output index where writing starts.
    ///
    /// # Returns
    ///
    /// Returns the number of target units written while resetting component
    /// state.
    ///
    /// # Errors
    ///
    /// Returns a converter error when the output range is invalid or too
    /// small, or when decoder or encoder reset processing fails.
    pub fn reset(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<usize, TranscodeConvertErrorOf<D, E>>
    where
        D::Value: Default,
    {
        self.engine.reset(output, output_index)
    }

    /// Converts source units into target units.
    ///
    /// This method's signature does not require `D::Value: Default`. Starting
    /// and finishing a usable stream through the current reset/finish APIs
    /// still requires that bound. Removing the lifecycle scratch bound is
    /// deferred.
    ///
    /// # Parameters
    ///
    /// - `input`: Source unit slice.
    /// - `input_index`: Absolute source index where conversion starts.
    /// - `output`: Target unit slice.
    /// - `output_index`: Absolute target index where writing starts.
    ///
    /// # Returns
    ///
    /// Returns conversion progress for consumed/produced counters and stop
    /// reason.
    ///
    /// # Errors
    ///
    /// Returns converter error when source or target indices are invalid, or
    /// when decoding/encoding fails under current policy.
    pub fn transcode(
        &mut self,
        input: &[D::Unit],
        input_index: usize,
        output: &mut [E::Unit],
        output_index: usize,
    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
        self.engine.transcode(input, input_index, output, output_index)
    }

    /// Converts source units after the caller has established end of input.
    ///
    /// # Parameters
    ///
    /// - `input`: Final source unit slice; the caller must not supply more
    ///   input later.
    /// - `input_index`: Absolute index at which unread source units begin.
    /// - `output`: Target unit slice receiving converted values.
    /// - `output_index`: Absolute index at which target output begins.
    ///
    /// # Returns
    ///
    /// Returns consumed and produced counts with the conversion stop reason.
    ///
    /// # Errors
    ///
    /// Returns a converter error for invalid indices, invalid or incomplete
    /// final source input, or a decoder/encoder failure under the current
    /// policy.
    pub fn transcode_eof(
        &mut self,
        input: &[D::Unit],
        input_index: usize,
        output: &mut [E::Unit],
        output_index: usize,
    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
        self.engine.transcode_eof(input, input_index, output, output_index)
    }

    /// Finishes internally retained output after EOF.
    ///
    /// Finalization delegates to the reusable converter engine. It drains
    /// retained pending output, encodes source-side decode flush values, and
    /// then finishes target-side encode hook state.
    ///
    /// # Parameters
    ///
    /// - `output`: Target unit slice for finalization output.
    /// - `output_index`: Absolute target output index where writing starts.
    ///
    /// # Returns
    ///
    /// Returns the number of target units written by finalization.
    ///
    /// # Errors
    ///
    /// Returns a converter error for invalid output indices, insufficient
    /// output capacity, or failures while draining pending values or
    /// finishing either codec.
    pub fn finish(
        &mut self,
        output: &mut [E::Unit],
        output_index: usize,
    ) -> Result<usize, TranscodeConvertErrorOf<D, E>>
    where
        D::Value: Default,
    {
        self.engine.finish(output, output_index)
    }
}

impl<D, E> Transcoder for CodecTranscodeConverter<D, E>
where
    D: Codec,
    E: Codec<Value = D::Value>,
    D::Value: Default,
{
    /// Source units accepted by the decoder codec.
    type Input = D::Unit;
    /// Target units produced by the encoder codec.
    type Output = E::Unit;
    /// Conversion failures retaining decoder, encoder, and pending-value
    /// context.
    type Error = TranscodeConvertErrorOf<D, E>;

    /// Returns an upper bound for target units produced from `input_len` units.
    ///
    /// # Parameters
    ///
    /// - `input_len`: Source units the caller plans to convert.
    ///
    /// # Returns
    ///
    /// Returns a conservative upper bound for produced target units.
    ///
    /// # Errors
    ///
    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
    /// arithmetic overflows.
    #[inline]
    fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
        CodecTranscodeConverter::max_transcode_output_len(self, input_len)
    }

    /// Returns the maximum target units emitted by finishing internal state.
    ///
    /// # Returns
    ///
    /// Returns a conservative upper bound valid for every reachable converter
    /// state.
    ///
    /// # Errors
    ///
    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
    /// arithmetic overflows.
    #[inline]
    fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
        CodecTranscodeConverter::max_finish_output_len(self)
    }

    /// Returns the maximum target units emitted when resetting stream state.
    ///
    /// # Returns
    ///
    /// Returns a conservative upper bound valid for every reachable converter
    /// state.
    ///
    /// # Errors
    ///
    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
    /// arithmetic overflows.
    #[inline]
    fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
        CodecTranscodeConverter::max_reset_output_len(self)
    }

    /// Clears retained pending output, resets component state, and emits
    /// stream-start encode output.
    ///
    /// # Parameters
    ///
    /// - `output`: Target unit slice for stream-start output.
    /// - `output_index`: Absolute target output index where writing starts.
    ///
    /// # Returns
    ///
    /// Returns the number of target units written while resetting component
    /// state.
    ///
    /// # Errors
    ///
    /// Returns a converter error when the output range is invalid or too
    /// small, or when decoder or encoder reset processing fails.
    fn reset(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<usize, TranscodeConvertErrorOf<D, E>> {
        CodecTranscodeConverter::reset(self, output, output_index)
    }

    /// Converts source units into target units.
    ///
    /// # Parameters
    ///
    /// - `input`: Source unit slice.
    /// - `input_index`: Absolute source index where conversion starts.
    /// - `output`: Target unit slice.
    /// - `output_index`: Absolute target index where writing starts.
    ///
    /// # Returns
    ///
    /// Returns conversion progress for consumed/produced counters and stop
    /// reason.
    ///
    /// # Errors
    ///
    /// Returns converter error when source or target indices are invalid, or
    /// when decoding/encoding fails under current policy.
    fn transcode(
        &mut self,
        input: &[D::Unit],
        input_index: usize,
        output: &mut [E::Unit],
        output_index: usize,
    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
        CodecTranscodeConverter::transcode(self, input, input_index, output, output_index)
    }

    /// Transcodes an input segment while marking it as the end of the stream.
    ///
    /// This forwarding implementation delegates to the converter's EOF-aware
    /// operation so buffered decoder state can be finalized before encoding.
    ///
    /// # Parameters
    ///
    /// - `input`: Final source unit slice; the caller must not supply more
    ///   input later.
    /// - `input_index`: Absolute index at which unread source units begin.
    /// - `output`: Target unit slice receiving converted values.
    /// - `output_index`: Absolute index at which target output begins.
    ///
    /// # Returns
    ///
    /// Returns consumed and produced counts with the conversion stop reason.
    ///
    /// # Errors
    ///
    /// Returns a converter error for invalid indices, invalid or incomplete
    /// final source input, or a decoder/encoder failure under the current
    /// policy.
    fn transcode_eof(
        &mut self,
        input: &[D::Unit],
        input_index: usize,
        output: &mut [E::Unit],
        output_index: usize,
    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
        CodecTranscodeConverter::transcode_eof(self, input, input_index, output, output_index)
    }

    /// Finishes internally retained output after EOF.
    ///
    /// # Parameters
    ///
    /// - `output`: Target unit slice for finalization output.
    /// - `output_index`: Absolute target output index where writing starts.
    ///
    /// # Returns
    ///
    /// Returns the number of target units written by finalization.
    ///
    /// # Errors
    ///
    /// Returns a converter error for invalid output indices, insufficient
    /// output capacity, or failures while draining pending values or
    /// finishing either codec.
    fn finish(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<usize, TranscodeConvertErrorOf<D, E>> {
        CodecTranscodeConverter::finish(self, output, output_index)
    }
}

impl<D, E> TranscodeConverter for CodecTranscodeConverter<D, E>
where
    D: Codec,
    E: Codec<Value = D::Value>,
    D::Value: Default,
{
    /// Domain error reported by the source codec.
    type DecodeError = D::DecodeError;
    /// Domain error reported by the target codec.
    type EncodeError = E::EncodeError;
    /// Decoded value transferred from source codec to target codec.
    type Value = D::Value;
}

impl<D, E> Default for CodecTranscodeConverter<D, E>
where
    D: Codec,
    E: Codec<Value = D::Value>,
    TranscodeConvertEngine<D, E, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks>: Default,
{
    /// Creates a default codec-backed buffered converter.
    ///
    /// # Returns
    ///
    /// Returns a converter with default codecs and hooks.
    #[inline]
    fn default() -> Self {
        Self {
            engine: TranscodeConvertEngine::default(),
        }
    }
}

impl<D, E> fmt::Debug for CodecTranscodeConverter<D, E>
where
    D: Codec,
    E: Codec<Value = D::Value>,
    TranscodeConvertEngine<D, E, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks>: fmt::Debug,
{
    /// Formats the wrapped converter engine for debugging.
    ///
    /// # Parameters
    ///
    /// - `f`: Destination formatter.
    ///
    /// # Returns
    ///
    /// Returns `fmt::Result` from the formatter.
    ///
    /// # Errors
    ///
    /// Returns the formatter error if writing the debug representation fails.
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("CodecTranscodeConverter")
            .field("engine", &self.engine)
            .finish()
    }
}