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}