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    #[must_use = "capacity planning can fail on overflow"]
197    #[inline]
198    pub fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
199        self.engine.max_transcode_output_len(input_len)
200    }
201
202    /// Returns the global maximum target units emitted by finishing internal
203    /// state.
204    ///
205    /// # Returns
206    ///
207    /// Returns a conservative upper bound valid for every reachable converter
208    /// state.
209    ///
210    /// # Errors
211    ///
212    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
213    /// arithmetic overflows.
214    #[must_use = "capacity planning can fail on overflow"]
215    #[inline]
216    pub fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
217        self.engine.max_finish_output_len()
218    }
219
220    /// Returns the maximum target units emitted when resetting stream state.
221    ///
222    /// # Returns
223    ///
224    /// Returns a conservative upper bound valid for every reachable converter
225    /// state.
226    ///
227    /// # Errors
228    ///
229    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
230    /// arithmetic overflows.
231    #[must_use = "capacity planning can fail on overflow"]
232    #[inline]
233    pub fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
234        self.engine.max_reset_output_len()
235    }
236
237    /// Clears retained pending output, resets target state, and then encodes
238    /// source-side reset values.
239    ///
240    /// `D::Value: Default` is required so the engine can allocate scratch
241    /// storage for any stream-start values the source decoder emits through
242    /// [`Codec::decode_reset`] before they are
243    /// piped through the target encoder. Stateless decoders never reach the
244    /// allocating path; the bound is consulted only when
245    /// [`Codec::MAX_DECODE_RESET_VALUES`]
246    /// is non-zero.
247    ///
248    /// # Parameters
249    ///
250    /// - `output`: Target unit slice for stream-start output.
251    /// - `output_index`: Absolute target output index where writing starts.
252    ///
253    /// # Returns
254    ///
255    /// Returns the number of target units written while resetting component
256    /// state.
257    ///
258    /// # Errors
259    ///
260    /// Returns a converter error when the output range is invalid or too
261    /// small, or when decoder or encoder reset processing fails.
262    pub fn reset(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<usize, TranscodeConvertErrorOf<D, E>>
263    where
264        D::Value: Default,
265    {
266        self.engine.reset(output, output_index)
267    }
268
269    /// Converts source units into target units.
270    ///
271    /// This is the main streaming operation and does not require `D::Value` to
272    /// implement [`Default`].
273    ///
274    /// # Parameters
275    ///
276    /// - `input`: Source unit slice.
277    /// - `input_index`: Absolute source index where conversion starts.
278    /// - `output`: Target unit slice.
279    /// - `output_index`: Absolute target index where writing starts.
280    ///
281    /// # Returns
282    ///
283    /// Returns conversion progress for consumed/produced counters and stop
284    /// reason.
285    ///
286    /// # Errors
287    ///
288    /// Returns converter error when source or target indices are invalid, or
289    /// when decoding/encoding fails under current policy.
290    pub fn transcode(
291        &mut self,
292        input: &[D::Unit],
293        input_index: usize,
294        output: &mut [E::Unit],
295        output_index: usize,
296    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
297        self.engine.transcode(input, input_index, output, output_index)
298    }
299
300    /// Converts source units after the caller has established end of input.
301    ///
302    /// # Parameters
303    ///
304    /// - `input`: Final source unit slice; the caller must not supply more
305    ///   input later.
306    /// - `input_index`: Absolute index at which unread source units begin.
307    /// - `output`: Target unit slice receiving converted values.
308    /// - `output_index`: Absolute index at which target output begins.
309    ///
310    /// # Returns
311    ///
312    /// Returns consumed and produced counts with the conversion stop reason.
313    ///
314    /// # Errors
315    ///
316    /// Returns a converter error for invalid indices, invalid or incomplete
317    /// final source input, or a decoder/encoder failure under the current
318    /// policy.
319    pub fn transcode_eof(
320        &mut self,
321        input: &[D::Unit],
322        input_index: usize,
323        output: &mut [E::Unit],
324        output_index: usize,
325    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
326        self.engine.transcode_eof(input, input_index, output, output_index)
327    }
328
329    /// Finishes internally retained output after EOF.
330    ///
331    /// Finalization delegates to the reusable converter engine. It drains
332    /// retained pending output, encodes source-side decode flush values, and
333    /// then finishes target-side encode hook state.
334    ///
335    /// # Parameters
336    ///
337    /// - `output`: Target unit slice for finalization output.
338    /// - `output_index`: Absolute target output index where writing starts.
339    ///
340    /// # Returns
341    ///
342    /// Returns the number of target units written by finalization.
343    ///
344    /// # Errors
345    ///
346    /// Returns a converter error for invalid output indices, insufficient
347    /// output capacity, or failures while draining pending values or
348    /// finishing either codec.
349    pub fn finish(
350        &mut self,
351        output: &mut [E::Unit],
352        output_index: usize,
353    ) -> Result<usize, TranscodeConvertErrorOf<D, E>>
354    where
355        D::Value: Default,
356    {
357        self.engine.finish(output, output_index)
358    }
359}
360
361impl<D, E> Transcoder for CodecTranscodeConverter<D, E>
362where
363    D: Codec,
364    E: Codec<Value = D::Value>,
365    D::Value: Default,
366{
367    /// Source units accepted by the decoder codec.
368    type Input = D::Unit;
369    /// Target units produced by the encoder codec.
370    type Output = E::Unit;
371    /// Conversion failures retaining decoder, encoder, and pending-value
372    /// context.
373    type Error = TranscodeConvertErrorOf<D, E>;
374
375    /// Returns an upper bound for target units produced from `input_len` units.
376    ///
377    /// # Parameters
378    ///
379    /// - `input_len`: Source units the caller plans to convert.
380    ///
381    /// # Returns
382    ///
383    /// Returns a conservative upper bound for produced target units.
384    ///
385    /// # Errors
386    ///
387    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
388    /// arithmetic overflows.
389    #[inline]
390    fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
391        CodecTranscodeConverter::max_transcode_output_len(self, input_len)
392    }
393
394    /// Returns the maximum target units emitted by finishing internal state.
395    ///
396    /// # Returns
397    ///
398    /// Returns a conservative upper bound valid for every reachable converter
399    /// state.
400    ///
401    /// # Errors
402    ///
403    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
404    /// arithmetic overflows.
405    #[inline]
406    fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
407        CodecTranscodeConverter::max_finish_output_len(self)
408    }
409
410    /// Returns the maximum target units emitted when resetting stream state.
411    ///
412    /// # Returns
413    ///
414    /// Returns a conservative upper bound valid for every reachable converter
415    /// state.
416    ///
417    /// # Errors
418    ///
419    /// Returns [`CapacityError::OutputLengthOverflow`] when component-bound
420    /// arithmetic overflows.
421    #[inline]
422    fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
423        CodecTranscodeConverter::max_reset_output_len(self)
424    }
425
426    /// Clears retained pending output, resets component state, and emits
427    /// stream-start encode output.
428    ///
429    /// # Parameters
430    ///
431    /// - `output`: Target unit slice for stream-start output.
432    /// - `output_index`: Absolute target output index where writing starts.
433    ///
434    /// # Returns
435    ///
436    /// Returns the number of target units written while resetting component
437    /// state.
438    ///
439    /// # Errors
440    ///
441    /// Returns a converter error when the output range is invalid or too
442    /// small, or when decoder or encoder reset processing fails.
443    fn reset(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<usize, TranscodeConvertErrorOf<D, E>> {
444        CodecTranscodeConverter::reset(self, output, output_index)
445    }
446
447    /// Converts source units into target units.
448    ///
449    /// # Parameters
450    ///
451    /// - `input`: Source unit slice.
452    /// - `input_index`: Absolute source index where conversion starts.
453    /// - `output`: Target unit slice.
454    /// - `output_index`: Absolute target index where writing starts.
455    ///
456    /// # Returns
457    ///
458    /// Returns conversion progress for consumed/produced counters and stop
459    /// reason.
460    ///
461    /// # Errors
462    ///
463    /// Returns converter error when source or target indices are invalid, or
464    /// when decoding/encoding fails under current policy.
465    fn transcode(
466        &mut self,
467        input: &[D::Unit],
468        input_index: usize,
469        output: &mut [E::Unit],
470        output_index: usize,
471    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
472        CodecTranscodeConverter::transcode(self, input, input_index, output, output_index)
473    }
474
475    /// Transcodes an input segment while marking it as the end of the stream.
476    ///
477    /// This forwarding implementation delegates to the converter's EOF-aware
478    /// operation so buffered decoder state can be finalized before encoding.
479    ///
480    /// # Parameters
481    ///
482    /// - `input`: Final source unit slice; the caller must not supply more
483    ///   input later.
484    /// - `input_index`: Absolute index at which unread source units begin.
485    /// - `output`: Target unit slice receiving converted values.
486    /// - `output_index`: Absolute index at which target output begins.
487    ///
488    /// # Returns
489    ///
490    /// Returns consumed and produced counts with the conversion stop reason.
491    ///
492    /// # Errors
493    ///
494    /// Returns a converter error for invalid indices, invalid or incomplete
495    /// final source input, or a decoder/encoder failure under the current
496    /// policy.
497    fn transcode_eof(
498        &mut self,
499        input: &[D::Unit],
500        input_index: usize,
501        output: &mut [E::Unit],
502        output_index: usize,
503    ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
504        CodecTranscodeConverter::transcode_eof(self, input, input_index, output, output_index)
505    }
506
507    /// Finishes internally retained output after EOF.
508    ///
509    /// # Parameters
510    ///
511    /// - `output`: Target unit slice for finalization output.
512    /// - `output_index`: Absolute target output index where writing starts.
513    ///
514    /// # Returns
515    ///
516    /// Returns the number of target units written by finalization.
517    ///
518    /// # Errors
519    ///
520    /// Returns a converter error for invalid output indices, insufficient
521    /// output capacity, or failures while draining pending values or
522    /// finishing either codec.
523    fn finish(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<usize, TranscodeConvertErrorOf<D, E>> {
524        CodecTranscodeConverter::finish(self, output, output_index)
525    }
526}
527
528impl<D, E> TranscodeConverter for CodecTranscodeConverter<D, E>
529where
530    D: Codec,
531    E: Codec<Value = D::Value>,
532    D::Value: Default,
533{
534    /// Domain error reported by the source codec.
535    type DecodeError = D::DecodeError;
536    /// Domain error reported by the target codec.
537    type EncodeError = E::EncodeError;
538    /// Decoded value transferred from source codec to target codec.
539    type Value = D::Value;
540}
541
542impl<D, E> Default for CodecTranscodeConverter<D, E>
543where
544    D: Codec,
545    E: Codec<Value = D::Value>,
546    TranscodeConvertEngine<D, E, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks>: Default,
547{
548    /// Creates a default codec-backed buffered converter.
549    ///
550    /// # Returns
551    ///
552    /// Returns a converter with default codecs and hooks.
553    #[inline]
554    fn default() -> Self {
555        Self {
556            engine: TranscodeConvertEngine::default(),
557        }
558    }
559}
560
561impl<D, E> fmt::Debug for CodecTranscodeConverter<D, E>
562where
563    D: Codec,
564    E: Codec<Value = D::Value>,
565    TranscodeConvertEngine<D, E, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks>: fmt::Debug,
566{
567    /// Formats the wrapped converter engine for debugging.
568    ///
569    /// # Parameters
570    ///
571    /// - `f`: Destination formatter.
572    ///
573    /// # Returns
574    ///
575    /// Returns `fmt::Result` from the formatter.
576    ///
577    /// # Errors
578    ///
579    /// Returns the formatter error if writing the debug representation fails.
580    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
581        f.debug_struct("CodecTranscodeConverter")
582            .field("engine", &self.engine)
583            .finish()
584    }
585}