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