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.
42pub struct CodecTranscodeConverter<D, E>
43where
44    D: Codec,
45    E: Codec<Value = D::Value>,
46{
47    /// Common buffered converter engine.
48    engine: TranscodeConvertEngine<D, E, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks>,
49}
50
51impl<D, E> CodecTranscodeConverter<D, E>
52where
53    D: Codec,
54    E: Codec<Value = D::Value>,
55{
56    /// Creates a buffered converter backed by decoder and encoder codecs.
57    ///
58    /// # Parameters
59    ///
60    /// - `decoder`: Low-level codec used to decode source units.
61    /// - `encoder`: Low-level codec used to encode target units.
62    ///
63    /// # Returns
64    ///
65    /// Returns a buffered converter adapter for the supplied codecs.
66    #[inline(always)]
67    #[must_use]
68    pub fn new(decoder: D, encoder: E) -> Self {
69        Self {
70            engine: TranscodeConvertEngine::new(decoder, encoder, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks),
71        }
72    }
73
74    /// Returns a shared reference to the source codec.
75    #[inline(always)]
76    #[must_use]
77    pub fn source_codec(&self) -> &D {
78        self.engine.source_codec()
79    }
80
81    /// Returns a mutable reference to the source codec.
82    ///
83    /// Mutating a codec during an active stream can invalidate that stream's
84    /// assumptions; reset the adapter before continuing with the new codec
85    /// configuration.
86    #[inline(always)]
87    #[must_use]
88    pub fn source_codec_mut(&mut self) -> &mut D {
89        self.engine.source_codec_mut()
90    }
91
92    /// Returns a shared reference to the target codec.
93    #[inline(always)]
94    #[must_use]
95    pub fn target_codec(&self) -> &E {
96        self.engine.target_codec()
97    }
98
99    /// Returns a mutable reference to the target codec.
100    ///
101    /// Mutating a codec during an active stream can invalidate that stream's
102    /// assumptions; reset the adapter before continuing with the new codec
103    /// configuration.
104    #[inline(always)]
105    #[must_use]
106    pub fn target_codec_mut(&mut self) -> &mut E {
107        self.engine.target_codec_mut()
108    }
109
110    /// Consumes the adapter and returns its source and target codecs.
111    ///
112    /// Any buffered lifecycle state, pending value, and internal hooks are
113    /// discarded.
114    #[inline(always)]
115    #[must_use]
116    pub fn into_codecs(self) -> (D, E) {
117        let (source, target, _, _) = self.engine.into_parts();
118        (source, target)
119    }
120
121    /// Returns an upper bound for target units produced from `input_len` units.
122    ///
123    /// This concrete adapter method is available even when `D::Value` does not
124    /// implement [`Default`].
125    ///
126    /// # Parameters
127    ///
128    /// - `input_len`: Source units the caller plans to convert.
129    ///
130    /// # Returns
131    ///
132    /// Returns a conservative upper bound for produced target units.
133    #[must_use = "capacity planning can fail on overflow"]
134    #[inline(always)]
135    pub fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
136        self.engine.max_transcode_output_len(input_len)
137    }
138
139    /// Returns the global maximum target units emitted by finishing internal
140    /// state.
141    ///
142    /// # Returns
143    ///
144    /// Returns a conservative upper bound valid for every reachable converter
145    /// state.
146    #[must_use = "capacity planning can fail on overflow"]
147    #[inline(always)]
148    pub fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
149        self.engine.max_finish_output_len()
150    }
151
152    /// Returns the maximum target units emitted when resetting stream state.
153    ///
154    /// # Returns
155    ///
156    /// Returns a conservative upper bound valid for every reachable converter
157    /// state.
158    ///
159    /// # Errors
160    ///
161    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
162    /// arithmetic overflows.
163    #[must_use = "capacity planning can fail on overflow"]
164    #[inline(always)]
165    pub fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
166        self.engine.max_reset_output_len()
167    }
168
169    /// Clears retained pending output, resets target state, and then encodes
170    /// source-side reset values.
171    ///
172    /// `D::Value: Default` is required so the engine can allocate scratch
173    /// storage for any stream-start values the source decoder emits through
174    /// [`Codec::decode_reset`] before they are
175    /// piped through the target encoder. Stateless decoders never reach the
176    /// allocating path; the bound is consulted only when
177    /// [`Codec::MAX_DECODE_RESET_VALUES`]
178    /// is non-zero.
179    ///
180    /// # Parameters
181    ///
182    /// - `output`: Target unit slice for stream-start output.
183    /// - `output_index`: Absolute target output index where writing starts.
184    ///
185    /// # Returns
186    ///
187    /// Returns the number of target units written while resetting component
188    /// state.
189    ///
190    /// # Errors
191    ///
192    /// Returns a converter error when the output range is invalid or too
193    /// small, or when decoder or encoder reset processing fails.
194    #[inline(always)]
195    pub fn reset(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<usize, TranscodeConvertErrorOf<D, E>>
196    where
197        D::Value: Default,
198    {
199        self.engine.reset(output, output_index)
200    }
201
202    /// Converts source units into target units.
203    ///
204    /// This is the main streaming operation and does not require `D::Value` to
205    /// implement [`Default`].
206    ///
207    /// # Parameters
208    ///
209    /// - `input`: Source unit slice.
210    /// - `input_index`: Absolute source index where conversion starts.
211    /// - `output`: Target unit slice.
212    /// - `output_index`: Absolute target index where writing starts.
213    ///
214    /// # Returns
215    ///
216    /// Returns conversion progress for consumed/produced counters and stop
217    /// reason.
218    ///
219    /// # Errors
220    ///
221    /// Returns converter error when source or target indices are invalid, or
222    /// when decoding/encoding fails under current policy.
223    #[inline(always)]
224    pub fn transcode(
225        &mut self,
226        input: &[D::Unit],
227        input_index: usize,
228        output: &mut [E::Unit],
229        output_index: usize,
230    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
231        self.engine.transcode(input, input_index, output, output_index)
232    }
233
234    /// Converts source units after the caller has established end of input.
235    #[inline(always)]
236    pub fn transcode_eof(
237        &mut self,
238        input: &[D::Unit],
239        input_index: usize,
240        output: &mut [E::Unit],
241        output_index: usize,
242    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
243        self.engine.transcode_eof(input, input_index, output, output_index)
244    }
245
246    /// Finishes internally retained output after EOF.
247    ///
248    /// Finalization delegates to the reusable converter engine. It drains
249    /// retained pending output, encodes source-side decode flush values, and
250    /// then finishes target-side encode hook state.
251    ///
252    /// # Parameters
253    ///
254    /// - `output`: Target unit slice for finalization output.
255    /// - `output_index`: Absolute target output index where writing starts.
256    ///
257    /// # Returns
258    ///
259    /// Returns the number of target units written by finalization.
260    ///
261    /// # Errors
262    ///
263    /// Returns a finish error for pending output that cannot be finalized.
264    #[inline(always)]
265    pub fn finish(
266        &mut self,
267        output: &mut [E::Unit],
268        output_index: usize,
269    ) -> Result<usize, TranscodeConvertErrorOf<D, E>>
270    where
271        D::Value: Default,
272    {
273        self.engine.finish(output, output_index)
274    }
275}
276
277impl<D, E> Transcoder for CodecTranscodeConverter<D, E>
278where
279    D: Codec,
280    E: Codec<Value = D::Value>,
281    D::Value: Default,
282{
283    type Input = D::Unit;
284    type Output = E::Unit;
285    type Error = TranscodeConvertErrorOf<D, E>;
286
287    /// Returns an upper bound for target units produced from `input_len` units.
288    ///
289    /// # Parameters
290    ///
291    /// - `input_len`: Source units the caller plans to convert.
292    ///
293    /// # Returns
294    ///
295    /// Returns a conservative upper bound for produced target units.
296    #[inline(always)]
297    fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
298        CodecTranscodeConverter::max_transcode_output_len(self, input_len)
299    }
300
301    /// Returns the maximum target units emitted by finishing internal state.
302    ///
303    /// # Returns
304    ///
305    /// Returns a conservative upper bound valid for every reachable converter
306    /// state.
307    #[inline(always)]
308    fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
309        CodecTranscodeConverter::max_finish_output_len(self)
310    }
311
312    /// Returns the maximum target units emitted when resetting stream state.
313    ///
314    /// # Returns
315    ///
316    /// Returns a conservative upper bound valid for every reachable converter
317    /// state.
318    ///
319    /// # Errors
320    ///
321    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
322    /// arithmetic overflows.
323    #[inline(always)]
324    fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
325        CodecTranscodeConverter::max_reset_output_len(self)
326    }
327
328    /// Clears retained pending output, resets component state, and emits
329    /// stream-start encode output.
330    ///
331    /// # Parameters
332    ///
333    /// - `output`: Target unit slice for stream-start output.
334    /// - `output_index`: Absolute target output index where writing starts.
335    ///
336    /// # Returns
337    ///
338    /// Returns the number of target units written while resetting component
339    /// state.
340    ///
341    /// # Errors
342    ///
343    /// Returns a converter error when the output range is invalid or too
344    /// small, or when decoder or encoder reset processing fails.
345    #[inline(always)]
346    fn reset(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<usize, TranscodeConvertErrorOf<D, E>> {
347        CodecTranscodeConverter::reset(self, output, output_index)
348    }
349
350    /// Converts source units into target units.
351    ///
352    /// # Parameters
353    ///
354    /// - `input`: Source unit slice.
355    /// - `input_index`: Absolute source index where conversion starts.
356    /// - `output`: Target unit slice.
357    /// - `output_index`: Absolute target index where writing starts.
358    ///
359    /// # Returns
360    ///
361    /// Returns conversion progress for consumed/produced counters and stop
362    /// reason.
363    ///
364    /// # Errors
365    ///
366    /// Returns converter error when source or target indices are invalid, or
367    /// when decoding/encoding fails under current policy.
368    #[inline(always)]
369    fn transcode(
370        &mut self,
371        input: &[D::Unit],
372        input_index: usize,
373        output: &mut [E::Unit],
374        output_index: usize,
375    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
376        CodecTranscodeConverter::transcode(self, input, input_index, output, output_index)
377    }
378
379    /// Transcodes an input segment while marking it as the end of the stream.
380    ///
381    /// This forwarding implementation delegates to the converter's EOF-aware
382    /// operation so buffered decoder state can be finalized before encoding.
383    #[inline(always)]
384    fn transcode_eof(
385        &mut self,
386        input: &[D::Unit],
387        input_index: usize,
388        output: &mut [E::Unit],
389        output_index: usize,
390    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
391        CodecTranscodeConverter::transcode_eof(self, input, input_index, output, output_index)
392    }
393
394    /// Finishes internally retained output after EOF.
395    ///
396    /// # Parameters
397    ///
398    /// - `output`: Target unit slice for finalization output.
399    /// - `output_index`: Absolute target output index where writing starts.
400    ///
401    /// # Returns
402    ///
403    /// Returns the number of target units written by finalization.
404    ///
405    /// # Errors
406    ///
407    /// Returns a finish error for pending output that cannot be finalized.
408    #[inline(always)]
409    fn finish(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<usize, TranscodeConvertErrorOf<D, E>> {
410        CodecTranscodeConverter::finish(self, output, output_index)
411    }
412}
413
414impl<D, E> TranscodeConverter for CodecTranscodeConverter<D, E>
415where
416    D: Codec,
417    E: Codec<Value = D::Value>,
418    D::Value: Default,
419{
420    type DecodeError = D::DecodeError;
421    type EncodeError = E::EncodeError;
422    type Value = D::Value;
423}
424
425impl<D, E> Default for CodecTranscodeConverter<D, E>
426where
427    D: Codec,
428    E: Codec<Value = D::Value>,
429    TranscodeConvertEngine<D, E, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks>: Default,
430{
431    /// Creates a default codec-backed buffered converter.
432    ///
433    /// # Returns
434    ///
435    /// Returns a converter with default codecs and hooks.
436    #[inline(always)]
437    fn default() -> Self {
438        Self {
439            engine: TranscodeConvertEngine::default(),
440        }
441    }
442}
443
444impl<D, E> fmt::Debug for CodecTranscodeConverter<D, E>
445where
446    D: Codec,
447    E: Codec<Value = D::Value>,
448    TranscodeConvertEngine<D, E, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks>: fmt::Debug,
449{
450    /// Formats the wrapped converter engine for debugging.
451    ///
452    /// # Parameters
453    ///
454    /// - `f`: Destination formatter.
455    ///
456    /// # Returns
457    ///
458    /// Returns `fmt::Result` from the formatter.
459    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
460        f.debug_struct("CodecTranscodeConverter")
461            .field("engine", &self.engine)
462            .finish()
463    }
464}