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}