Skip to main content

qubit_codec/transcode/adapter/
codec_transcode_decoder.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 decoder adapter backed by a low-level codec.
9
10use super::CodecTranscodeDecodeHooks;
11use crate::{
12    CapacityError,
13    Codec,
14    CodecDecodeError,
15    TranscodeDecodeEngine,
16    TranscodeDecodeEngineError,
17    TranscodeDecoder,
18    TranscodeError,
19    TranscodeProgress,
20    Transcoder,
21};
22
23/// Decodes encoded units into caller-provided value buffers by using a
24/// [`Codec`].
25///
26/// `CodecTranscodeDecoder` is a policy-free bridge from the low-level unchecked
27/// [`Codec`] contract to [`Transcoder`] and [`TranscodeDecoder`]. It
28/// leaves incomplete input tails in the caller-provided input slice; callers
29/// own input-buffer refill and EOF incomplete-tail policy.
30///
31/// Because [`Transcoder::finish`] receives no source input, this adapter is
32/// intended for codecs whose decode boundary is locally decidable from the
33/// visible prefix plus codec state. Formats that require EOF-aware
34/// maximal-munch parsing or need to reinterpret a pending prefix at EOF should
35/// provide a custom streaming decoder or a value-level facade.
36///
37/// # Type Parameters
38///
39/// - `C`: Low-level codec used to decode values.
40#[derive(Debug, Default)]
41pub struct CodecTranscodeDecoder<C> {
42    /// Common buffered decoding engine.
43    engine: TranscodeDecodeEngine<C, CodecTranscodeDecodeHooks>,
44}
45
46impl<C> CodecTranscodeDecoder<C>
47where
48    C: Codec,
49{
50    /// Creates a buffered decoder backed by `codec`.
51    ///
52    /// # Parameters
53    ///
54    /// - `codec`: Low-level codec used to decode values.
55    ///
56    /// # Returns
57    ///
58    /// Returns a buffered decoder adapter for the supplied codec.
59    #[inline]
60    #[must_use]
61    pub fn new(codec: C) -> Self {
62        Self {
63            engine: TranscodeDecodeEngine::new(
64                codec,
65                CodecTranscodeDecodeHooks,
66            ),
67        }
68    }
69}
70
71impl<C> Transcoder<C::Unit, C::Value> for CodecTranscodeDecoder<C>
72where
73    C: Codec,
74{
75    type Error = CodecDecodeError<C::DecodeError>;
76
77    /// Returns an upper bound for decoded values produced from `input_len`
78    /// units.
79    ///
80    /// # Parameters
81    ///
82    /// - `input_len`: Source units the caller plans to decode.
83    ///
84    /// # Returns
85    ///
86    /// Returns a conservative upper bound for decoded values.
87    #[inline(always)]
88    fn max_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
89        self.engine.max_output_len(input_len)
90    }
91
92    /// Returns the maximum values emitted by finishing internal state.
93    ///
94    /// # Returns
95    ///
96    /// Returns the number of values that may still be emitted by finishing
97    /// state.
98    #[inline(always)]
99    fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
100        self.engine.max_finish_output_len()
101    }
102
103    /// Returns the maximum values emitted when resetting internal state.
104    #[inline(always)]
105    fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
106        self.engine.max_reset_output_len()
107    }
108
109    /// Runs before-reset cleanup for decoder state.
110    #[inline(always)]
111    fn reset(
112        &mut self,
113        output: &mut [C::Value],
114        output_index: usize,
115    ) -> Result<usize, TranscodeError<Self::Error>> {
116        self.engine
117            .reset(output, output_index)
118            .map_err(|error| error.map_domain(flatten_decode_engine_error))
119    }
120
121    /// Decodes source units into logical values.
122    ///
123    /// # Parameters
124    ///
125    /// - `input`: Source unit slice.
126    /// - `input_index`: Absolute source index where decoding starts.
127    /// - `output`: Destination value slice.
128    /// - `output_index`: Absolute output value index where writing starts.
129    ///
130    /// # Returns
131    ///
132    /// Returns conversion progress for consumed and written counters.
133    ///
134    /// # Errors
135    ///
136    /// Returns a decode error when indices are invalid or when conversion fails
137    /// under hook policy.
138    #[inline(always)]
139    fn transcode(
140        &mut self,
141        input: &[C::Unit],
142        input_index: usize,
143        output: &mut [C::Value],
144        output_index: usize,
145    ) -> Result<TranscodeProgress, TranscodeError<Self::Error>> {
146        self.engine
147            .transcode(input, input_index, output, output_index)
148            .map_err(|error| error.map_domain(flatten_decode_engine_error))
149    }
150
151    /// Finishes internally retained output after EOF.
152    ///
153    /// # Parameters
154    ///
155    /// - `output`: Destination value slice for final retained values.
156    /// - `output_index`: Absolute output value index where writing starts.
157    ///
158    /// # Returns
159    ///
160    /// Returns the number of values written by finalization.
161    ///
162    /// # Errors
163    ///
164    /// Returns a finish error if finalization cannot complete.
165    #[inline(always)]
166    fn finish(
167        &mut self,
168        output: &mut [C::Value],
169        output_index: usize,
170    ) -> Result<usize, TranscodeError<Self::Error>> {
171        self.engine
172            .finish(output, output_index)
173            .map_err(|error| error.map_domain(flatten_decode_engine_error))
174    }
175}
176
177impl<C> TranscodeDecoder<C::Unit, C::Value> for CodecTranscodeDecoder<C> where
178    C: Codec
179{
180}
181
182#[inline(always)]
183fn flatten_decode_engine_error<E>(
184    error: TranscodeDecodeEngineError<E, CodecDecodeError<E>>,
185) -> CodecDecodeError<E> {
186    match error {
187        TranscodeDecodeEngineError::Codec(error)
188        | TranscodeDecodeEngineError::Hook(error) => error,
189    }
190}