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