Skip to main content

qubit_codec/transcode/adapter/
codec_transcode_converter.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// =============================================================================
8//! Buffered converter adapter backed by two low-level codecs.
9
10use core::fmt;
11
12use super::super::engine::TranscodeConvertEngine;
13use super::CodecTranscodeDecodeHooks;
14use super::CodecTranscodeEncodeHooks;
15use crate::CapacityError;
16use crate::Codec;
17use crate::TranscodeConvertErrorOf;
18use crate::TranscodeConverter;
19use crate::TranscodeProgress;
20use crate::Transcoder;
21
22/// Converts source units to target units through a decoded value by using
23/// codecs.
24///
25/// The converter decodes one source value with the decoder codec, then encodes
26/// that value with the encoder codec. If the current output buffer cannot hold
27/// the encoded value, the already decoded value is retained by the common
28/// converter engine and must be drained before more source input is consumed.
29/// Incomplete source tails are left in the caller-provided input slice; callers
30/// own input-buffer refill and EOF incomplete-tail policy.
31///
32/// Because finalization receives no source input, the source codec should have
33/// locally decidable decode boundaries for the default converter bridge. Source
34/// formats that require EOF-aware maximal-munch parsing or delayed boundary
35/// decisions should implement that source-side policy in a custom transcoder or
36/// facade before conversion.
37///
38/// # Type Parameters
39///
40/// - `D`: Low-level codec used to decode source units.
41/// - `E`: Low-level codec used to encode target units.
42///
43/// # Examples
44///
45/// A byte-preserving codec can be adapted to the buffered streaming interface.
46///
47/// ```
48/// use core::convert::Infallible;
49/// use core::num::NonZeroUsize;
50///
51/// use qubit_codec::Codec;
52/// use qubit_codec::CodecTranscodeConverter;
53/// use qubit_codec::DecodeFailure;
54///
55/// # struct ByteCodec;
56/// # impl Codec for ByteCodec {
57/// #     type Value = u8;
58/// #     type Unit = u8;
59/// #     type DecodeError = Infallible;
60/// #     type EncodeError = Infallible;
61/// #     const MIN_UNITS_PER_VALUE: usize = 1;
62/// #     const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
63/// #     const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
64/// #     unsafe fn decode(&mut self, input: &[u8], index: usize)
65/// #         -> Result<(u8, NonZeroUsize), DecodeFailure<Infallible>> {
66/// #         Ok((input[index], NonZeroUsize::MIN))
67/// #     }
68/// #     unsafe fn encode(&mut self, value: &u8, output: &mut [u8], index: usize)
69/// #         -> Result<usize, Infallible> {
70/// #         output[index] = *value;
71/// #         Ok(1)
72/// #     }
73/// # }
74/// let mut converter = CodecTranscodeConverter::new(ByteCodec, ByteCodec);
75/// let mut output = [0_u8; 2];
76/// converter.reset(&mut output, 0).unwrap();
77/// converter.transcode(&[7, 9], 0, &mut output, 0).unwrap();
78/// assert_eq!(output, [7, 9]);
79/// ```
80pub struct CodecTranscodeConverter<D, E>
81where
82    D: Codec,
83    E: Codec<Value = D::Value>,
84{
85    /// Common buffered converter engine.
86    engine: TranscodeConvertEngine<D, E, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks>,
87}
88
89impl<D, E> CodecTranscodeConverter<D, E>
90where
91    D: Codec,
92    E: Codec<Value = D::Value>,
93{
94    /// Creates a buffered converter backed by decoder and encoder codecs.
95    ///
96    /// # Parameters
97    ///
98    /// - `decoder`: Low-level codec used to decode source units.
99    /// - `encoder`: Low-level codec used to encode target units.
100    ///
101    /// # Returns
102    ///
103    /// Returns a buffered converter adapter for the supplied codecs.
104    #[inline]
105    #[must_use]
106    pub fn new(decoder: D, encoder: E) -> Self {
107        Self {
108            engine: TranscodeConvertEngine::new(decoder, encoder, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks),
109        }
110    }
111
112    /// Returns a shared reference to the source codec.
113    ///
114    /// # Returns
115    ///
116    /// Borrows the source codec without allocating or changing stream state.
117    #[inline]
118    #[must_use]
119    pub fn source_codec(&self) -> &D {
120        self.engine.source_codec()
121    }
122
123    /// Returns a mutable reference to the source codec.
124    ///
125    /// Mutating a codec during an active stream can invalidate that stream's
126    /// assumptions; reset the adapter before continuing with the new codec
127    /// configuration.
128    ///
129    /// # Returns
130    ///
131    /// Exclusively borrows the codec while the adapter remains borrowed.
132    #[inline]
133    #[must_use]
134    pub fn source_codec_mut(&mut self) -> &mut D {
135        self.engine.source_codec_mut()
136    }
137
138    /// Returns a shared reference to the target codec.
139    ///
140    /// # Returns
141    ///
142    /// Borrows the target codec without allocating or changing stream state.
143    #[inline]
144    #[must_use]
145    pub fn target_codec(&self) -> &E {
146        self.engine.target_codec()
147    }
148
149    /// Returns a mutable reference to the target codec.
150    ///
151    /// Mutating a codec during an active stream can invalidate that stream's
152    /// assumptions; reset the adapter before continuing with the new codec
153    /// configuration.
154    ///
155    /// # Returns
156    ///
157    /// Exclusively borrows the codec while the adapter remains borrowed.
158    #[inline]
159    #[must_use]
160    pub fn target_codec_mut(&mut self) -> &mut E {
161        self.engine.target_codec_mut()
162    }
163
164    /// Consumes the adapter and returns its source and target codecs.
165    ///
166    /// Any buffered lifecycle state, pending value, and internal hooks are
167    /// discarded.
168    ///
169    /// # Returns
170    ///
171    /// Returns the owned source codec followed by the owned target codec.
172    #[inline]
173    #[must_use]
174    pub fn into_codecs(self) -> (D, E) {
175        let (source, target, _, _) = self.engine.into_parts();
176        (source, target)
177    }
178
179    /// Returns an upper bound for target units produced from `input_len` units.
180    ///
181    /// This concrete adapter method is available even when `D::Value` does not
182    /// implement [`Default`].
183    ///
184    /// # Parameters
185    ///
186    /// - `input_len`: Source units the caller plans to convert.
187    ///
188    /// # Returns
189    ///
190    /// Returns a conservative upper bound for produced target units.
191    ///
192    /// # Errors
193    ///
194    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
195    /// arithmetic overflows.
196    #[inline]
197    pub fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
198        self.engine.max_transcode_output_len(input_len)
199    }
200
201    /// Returns the global maximum target units emitted by finishing internal
202    /// state.
203    ///
204    /// # Returns
205    ///
206    /// Returns a conservative upper bound valid for every reachable converter
207    /// state.
208    ///
209    /// # Errors
210    ///
211    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
212    /// arithmetic overflows.
213    #[inline]
214    pub fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
215        self.engine.max_finish_output_len()
216    }
217
218    /// Returns the maximum target units emitted when resetting stream state.
219    ///
220    /// # Returns
221    ///
222    /// Returns a conservative upper bound valid for every reachable converter
223    /// state.
224    ///
225    /// # Errors
226    ///
227    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
228    /// arithmetic overflows.
229    #[inline]
230    pub fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
231        self.engine.max_reset_output_len()
232    }
233
234    /// Clears retained pending output, resets target state, and then encodes
235    /// source-side reset values.
236    ///
237    /// `D::Value: Default` is required so the engine can allocate scratch
238    /// storage for any stream-start values the source decoder emits through
239    /// [`Codec::decode_reset`] before they are
240    /// piped through the target encoder. Stateless decoders never reach the
241    /// allocating path; the bound is consulted only when
242    /// [`Codec::MAX_DECODE_RESET_VALUES`]
243    /// is non-zero.
244    ///
245    /// # Parameters
246    ///
247    /// - `output`: Target unit slice for stream-start output.
248    /// - `output_index`: Absolute target output index where writing starts.
249    ///
250    /// # Returns
251    ///
252    /// Returns the number of target units written while resetting component
253    /// state.
254    ///
255    /// # Errors
256    ///
257    /// Returns a converter error when the output range is invalid or too
258    /// small, or when decoder or encoder reset processing fails.
259    pub fn reset(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<usize, TranscodeConvertErrorOf<D, E>>
260    where
261        D::Value: Default,
262    {
263        self.engine.reset(output, output_index)
264    }
265
266    /// Converts source units into target units.
267    ///
268    /// This is the main streaming operation and does not require `D::Value` to
269    /// implement [`Default`].
270    ///
271    /// # Parameters
272    ///
273    /// - `input`: Source unit slice.
274    /// - `input_index`: Absolute source index where conversion starts.
275    /// - `output`: Target unit slice.
276    /// - `output_index`: Absolute target index where writing starts.
277    ///
278    /// # Returns
279    ///
280    /// Returns conversion progress for consumed/produced counters and stop
281    /// reason.
282    ///
283    /// # Errors
284    ///
285    /// Returns converter error when source or target indices are invalid, or
286    /// when decoding/encoding fails under current policy.
287    pub fn transcode(
288        &mut self,
289        input: &[D::Unit],
290        input_index: usize,
291        output: &mut [E::Unit],
292        output_index: usize,
293    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
294        self.engine.transcode(input, input_index, output, output_index)
295    }
296
297    /// Converts source units after the caller has established end of input.
298    ///
299    /// # Parameters
300    ///
301    /// - `input`: Final source unit slice; the caller must not supply more
302    ///   input later.
303    /// - `input_index`: Absolute index at which unread source units begin.
304    /// - `output`: Target unit slice receiving converted values.
305    /// - `output_index`: Absolute index at which target output begins.
306    ///
307    /// # Returns
308    ///
309    /// Returns consumed and produced counts with the conversion stop reason.
310    ///
311    /// # Errors
312    ///
313    /// Returns a converter error for invalid indices, invalid or incomplete
314    /// final source input, or a decoder/encoder failure under the current
315    /// policy.
316    pub fn transcode_eof(
317        &mut self,
318        input: &[D::Unit],
319        input_index: usize,
320        output: &mut [E::Unit],
321        output_index: usize,
322    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
323        self.engine.transcode_eof(input, input_index, output, output_index)
324    }
325
326    /// Finishes internally retained output after EOF.
327    ///
328    /// Finalization delegates to the reusable converter engine. It drains
329    /// retained pending output, encodes source-side decode flush values, and
330    /// then finishes target-side encode hook state.
331    ///
332    /// # Parameters
333    ///
334    /// - `output`: Target unit slice for finalization output.
335    /// - `output_index`: Absolute target output index where writing starts.
336    ///
337    /// # Returns
338    ///
339    /// Returns the number of target units written by finalization.
340    ///
341    /// # Errors
342    ///
343    /// Returns a converter error for invalid output indices, insufficient
344    /// output capacity, or failures while draining pending values or
345    /// finishing either codec.
346    pub fn finish(
347        &mut self,
348        output: &mut [E::Unit],
349        output_index: usize,
350    ) -> Result<usize, TranscodeConvertErrorOf<D, E>>
351    where
352        D::Value: Default,
353    {
354        self.engine.finish(output, output_index)
355    }
356}
357
358impl<D, E> Transcoder for CodecTranscodeConverter<D, E>
359where
360    D: Codec,
361    E: Codec<Value = D::Value>,
362    D::Value: Default,
363{
364    /// Source units accepted by the decoder codec.
365    type Input = D::Unit;
366    /// Target units produced by the encoder codec.
367    type Output = E::Unit;
368    /// Conversion failures retaining decoder, encoder, and pending-value
369    /// context.
370    type Error = TranscodeConvertErrorOf<D, E>;
371
372    /// Returns an upper bound for target units produced from `input_len` units.
373    ///
374    /// # Parameters
375    ///
376    /// - `input_len`: Source units the caller plans to convert.
377    ///
378    /// # Returns
379    ///
380    /// Returns a conservative upper bound for produced target units.
381    ///
382    /// # Errors
383    ///
384    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
385    /// arithmetic overflows.
386    #[inline]
387    fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
388        CodecTranscodeConverter::max_transcode_output_len(self, input_len)
389    }
390
391    /// Returns the maximum target units emitted by finishing internal state.
392    ///
393    /// # Returns
394    ///
395    /// Returns a conservative upper bound valid for every reachable converter
396    /// state.
397    ///
398    /// # Errors
399    ///
400    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
401    /// arithmetic overflows.
402    #[inline]
403    fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
404        CodecTranscodeConverter::max_finish_output_len(self)
405    }
406
407    /// Returns the maximum target units emitted when resetting stream state.
408    ///
409    /// # Returns
410    ///
411    /// Returns a conservative upper bound valid for every reachable converter
412    /// state.
413    ///
414    /// # Errors
415    ///
416    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
417    /// arithmetic overflows.
418    #[inline]
419    fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
420        CodecTranscodeConverter::max_reset_output_len(self)
421    }
422
423    /// Clears retained pending output, resets component state, and emits
424    /// stream-start encode output.
425    ///
426    /// # Parameters
427    ///
428    /// - `output`: Target unit slice for stream-start output.
429    /// - `output_index`: Absolute target output index where writing starts.
430    ///
431    /// # Returns
432    ///
433    /// Returns the number of target units written while resetting component
434    /// state.
435    ///
436    /// # Errors
437    ///
438    /// Returns a converter error when the output range is invalid or too
439    /// small, or when decoder or encoder reset processing fails.
440    fn reset(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<usize, TranscodeConvertErrorOf<D, E>> {
441        CodecTranscodeConverter::reset(self, output, output_index)
442    }
443
444    /// Converts source units into target units.
445    ///
446    /// # Parameters
447    ///
448    /// - `input`: Source unit slice.
449    /// - `input_index`: Absolute source index where conversion starts.
450    /// - `output`: Target unit slice.
451    /// - `output_index`: Absolute target index where writing starts.
452    ///
453    /// # Returns
454    ///
455    /// Returns conversion progress for consumed/produced counters and stop
456    /// reason.
457    ///
458    /// # Errors
459    ///
460    /// Returns converter error when source or target indices are invalid, or
461    /// when decoding/encoding fails under current policy.
462    fn transcode(
463        &mut self,
464        input: &[D::Unit],
465        input_index: usize,
466        output: &mut [E::Unit],
467        output_index: usize,
468    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
469        CodecTranscodeConverter::transcode(self, input, input_index, output, output_index)
470    }
471
472    /// Transcodes an input segment while marking it as the end of the stream.
473    ///
474    /// This forwarding implementation delegates to the converter's EOF-aware
475    /// operation so buffered decoder state can be finalized before encoding.
476    ///
477    /// # Parameters
478    ///
479    /// - `input`: Final source unit slice; the caller must not supply more
480    ///   input later.
481    /// - `input_index`: Absolute index at which unread source units begin.
482    /// - `output`: Target unit slice receiving converted values.
483    /// - `output_index`: Absolute index at which target output begins.
484    ///
485    /// # Returns
486    ///
487    /// Returns consumed and produced counts with the conversion stop reason.
488    ///
489    /// # Errors
490    ///
491    /// Returns a converter error for invalid indices, invalid or incomplete
492    /// final source input, or a decoder/encoder failure under the current
493    /// policy.
494    fn transcode_eof(
495        &mut self,
496        input: &[D::Unit],
497        input_index: usize,
498        output: &mut [E::Unit],
499        output_index: usize,
500    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
501        CodecTranscodeConverter::transcode_eof(self, input, input_index, output, output_index)
502    }
503
504    /// Finishes internally retained output after EOF.
505    ///
506    /// # Parameters
507    ///
508    /// - `output`: Target unit slice for finalization output.
509    /// - `output_index`: Absolute target output index where writing starts.
510    ///
511    /// # Returns
512    ///
513    /// Returns the number of target units written by finalization.
514    ///
515    /// # Errors
516    ///
517    /// Returns a converter error for invalid output indices, insufficient
518    /// output capacity, or failures while draining pending values or
519    /// finishing either codec.
520    fn finish(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<usize, TranscodeConvertErrorOf<D, E>> {
521        CodecTranscodeConverter::finish(self, output, output_index)
522    }
523}
524
525impl<D, E> TranscodeConverter for CodecTranscodeConverter<D, E>
526where
527    D: Codec,
528    E: Codec<Value = D::Value>,
529    D::Value: Default,
530{
531    /// Domain error reported by the source codec.
532    type DecodeError = D::DecodeError;
533    /// Domain error reported by the target codec.
534    type EncodeError = E::EncodeError;
535    /// Decoded value transferred from source codec to target codec.
536    type Value = D::Value;
537}
538
539impl<D, E> Default for CodecTranscodeConverter<D, E>
540where
541    D: Codec,
542    E: Codec<Value = D::Value>,
543    TranscodeConvertEngine<D, E, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks>: Default,
544{
545    /// Creates a default codec-backed buffered converter.
546    ///
547    /// # Returns
548    ///
549    /// Returns a converter with default codecs and hooks.
550    #[inline]
551    fn default() -> Self {
552        Self {
553            engine: TranscodeConvertEngine::default(),
554        }
555    }
556}
557
558impl<D, E> fmt::Debug for CodecTranscodeConverter<D, E>
559where
560    D: Codec,
561    E: Codec<Value = D::Value>,
562    TranscodeConvertEngine<D, E, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks>: fmt::Debug,
563{
564    /// Formats the wrapped converter engine for debugging.
565    ///
566    /// # Parameters
567    ///
568    /// - `f`: Destination formatter.
569    ///
570    /// # Returns
571    ///
572    /// Returns `fmt::Result` from the formatter.
573    ///
574    /// # Errors
575    ///
576    /// Returns the formatter error if writing the debug representation fails.
577    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
578        f.debug_struct("CodecTranscodeConverter")
579            .field("engine", &self.engine)
580            .finish()
581    }
582}