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 #[inline(always)]
380 fn transcode_eof(
381 &mut self,
382 input: &[D::Unit],
383 input_index: usize,
384 output: &mut [E::Unit],
385 output_index: usize,
386 ) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
387 CodecTranscodeConverter::transcode_eof(self, input, input_index, output, output_index)
388 }
389
390 /// Finishes internally retained output after EOF.
391 ///
392 /// # Parameters
393 ///
394 /// - `output`: Target unit slice for finalization output.
395 /// - `output_index`: Absolute target output index where writing starts.
396 ///
397 /// # Returns
398 ///
399 /// Returns the number of target units written by finalization.
400 ///
401 /// # Errors
402 ///
403 /// Returns a finish error for pending output that cannot be finalized.
404 #[inline(always)]
405 fn finish(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<usize, TranscodeConvertErrorOf<D, E>> {
406 CodecTranscodeConverter::finish(self, output, output_index)
407 }
408}
409
410impl<D, E> TranscodeConverter for CodecTranscodeConverter<D, E>
411where
412 D: Codec,
413 E: Codec<Value = D::Value>,
414 D::Value: Default,
415{
416 type DecodeError = D::DecodeError;
417 type EncodeError = E::EncodeError;
418 type Value = D::Value;
419}
420
421impl<D, E> Default for CodecTranscodeConverter<D, E>
422where
423 D: Codec,
424 E: Codec<Value = D::Value>,
425 TranscodeConvertEngine<D, E, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks>: Default,
426{
427 /// Creates a default codec-backed buffered converter.
428 ///
429 /// # Returns
430 ///
431 /// Returns a converter with default codecs and hooks.
432 #[inline(always)]
433 fn default() -> Self {
434 Self {
435 engine: TranscodeConvertEngine::default(),
436 }
437 }
438}
439
440impl<D, E> fmt::Debug for CodecTranscodeConverter<D, E>
441where
442 D: Codec,
443 E: Codec<Value = D::Value>,
444 TranscodeConvertEngine<D, E, CodecTranscodeDecodeHooks, CodecTranscodeEncodeHooks>: fmt::Debug,
445{
446 /// Formats the wrapped converter engine for debugging.
447 ///
448 /// # Parameters
449 ///
450 /// - `f`: Destination formatter.
451 ///
452 /// # Returns
453 ///
454 /// Returns `fmt::Result` from the formatter.
455 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456 f.debug_struct("CodecTranscodeConverter")
457 .field("engine", &self.engine)
458 .finish()
459 }
460}