qubit_codec/transcode/adapter/codec_transcode_encoder.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 encoder adapter backed by a low-level codec.
9
10use super::CodecTranscodeEncodeHooks;
11use crate::{
12 CapacityError,
13 Codec,
14 CodecEncodeError,
15 TranscodeEncodeEngine,
16 TranscodeEncodeEngineError,
17 TranscodeEncoder,
18 TranscodeError,
19 TranscodeProgress,
20 Transcoder,
21};
22
23/// Encodes values into caller-provided output units by using a [`Codec`].
24///
25/// `CodecTranscodeEncoder` is the default bridge from the low-level unchecked
26/// [`Codec`] contract to the buffered [`Transcoder`] and
27/// [`TranscodeEncoder`] contracts. It encodes complete values only; when the
28/// remaining output capacity is smaller than `codec.encode_len(value)`, it
29/// stops before consuming that input value and reports
30/// [`crate::TranscodeStatus::NeedOutput`].
31///
32/// # Type Parameters
33///
34/// - `C`: Low-level codec used to encode values.
35#[derive(Debug)]
36pub struct CodecTranscodeEncoder<C> {
37 /// Common buffered encoding engine.
38 engine: TranscodeEncodeEngine<C, CodecTranscodeEncodeHooks>,
39}
40
41impl<C> CodecTranscodeEncoder<C>
42where
43 C: Codec,
44{
45 /// Creates a buffered encoder backed by `codec`.
46 ///
47 /// # Parameters
48 ///
49 /// - `codec`: Low-level codec used to encode values.
50 ///
51 /// # Returns
52 ///
53 /// Returns a buffered encoder adapter for the supplied codec.
54 #[inline]
55 #[must_use]
56 pub fn new(codec: C) -> Self {
57 Self {
58 engine: TranscodeEncodeEngine::new(
59 codec,
60 CodecTranscodeEncodeHooks,
61 ),
62 }
63 }
64}
65
66impl<C> Transcoder<C::Value, C::Unit> for CodecTranscodeEncoder<C>
67where
68 C: Codec,
69{
70 type Error = CodecEncodeError<C::EncodeError>;
71
72 /// Gets the maximum number of output units needed for `input_len`
73 /// values.
74 ///
75 /// # Parameters
76 ///
77 /// - `input_len`: Logical input values the caller plans to encode.
78 ///
79 /// # Returns
80 ///
81 /// a conservative upper bound for output units.
82 #[inline(always)]
83 fn max_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
84 self.engine.max_output_len(input_len)
85 }
86
87 /// Gets the maximum units emitted when resetting internal state.
88 ///
89 /// # Returns
90 ///
91 /// the maximum units emitted when resetting internal state.
92 #[inline(always)]
93 fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
94 self.engine.max_reset_output_len()
95 }
96
97 /// Gets the maximum units emitted by finishing internal state.
98 ///
99 /// # Returns
100 ///
101 /// the number of units that may be emitted by finishing state.
102 #[inline(always)]
103 fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
104 self.engine.max_finish_output_len()
105 }
106
107 /// Runs before-reset cleanup and emits stream-start output.
108 #[inline(always)]
109 fn reset(
110 &mut self,
111 output: &mut [C::Unit],
112 output_index: usize,
113 ) -> Result<usize, TranscodeError<Self::Error>> {
114 self.engine
115 .reset(output, output_index)
116 .map_err(|error| error.map_domain(flatten_encode_engine_error))
117 }
118
119 /// Encodes values into the supplied output buffer.
120 ///
121 /// # Parameters
122 ///
123 /// - `input`: Input value slice.
124 /// - `input_index`: Absolute input index where encoding starts.
125 /// - `output`: Destination unit slice.
126 /// - `output_index`: Absolute output index where writing starts.
127 ///
128 /// # Returns
129 ///
130 /// Returns conversion progress for consumed input and produced output
131 /// units.
132 ///
133 /// # Errors
134 ///
135 /// Returns an encode error when indices are invalid or when encoding cannot
136 /// continue under current policy.
137 #[inline(always)]
138 fn transcode(
139 &mut self,
140 input: &[C::Value],
141 input_index: usize,
142 output: &mut [C::Unit],
143 output_index: usize,
144 ) -> Result<TranscodeProgress, TranscodeError<Self::Error>> {
145 self.engine
146 .transcode(input, input_index, output, output_index)
147 .map_err(|error| error.map_domain(flatten_encode_engine_error))
148 }
149
150 /// Finishes internally retained output after EOF.
151 ///
152 /// # Parameters
153 ///
154 /// - `output`: Destination unit slice for finalization output.
155 /// - `output_index`: Absolute output index where writing starts.
156 ///
157 /// # Returns
158 ///
159 /// Returns the number of units written by finalization.
160 ///
161 /// # Errors
162 ///
163 /// Returns a finish error if retained output cannot be fully emitted.
164 #[inline(always)]
165 fn finish(
166 &mut self,
167 output: &mut [C::Unit],
168 output_index: usize,
169 ) -> Result<usize, TranscodeError<Self::Error>> {
170 self.engine
171 .finish(output, output_index)
172 .map_err(|error| error.map_domain(flatten_encode_engine_error))
173 }
174}
175
176impl<C> TranscodeEncoder<C::Value, C::Unit> for CodecTranscodeEncoder<C>
177where
178 C: Codec,
179{
180 // empty
181}
182
183impl<C> Default for CodecTranscodeEncoder<C>
184where
185 C: Codec + Default,
186{
187 /// Creates a default codec-backed buffered encoder.
188 ///
189 /// # Returns
190 ///
191 /// Returns an encoder backed by `C::default()`.
192 #[inline(always)]
193 fn default() -> Self {
194 Self::new(C::default())
195 }
196}
197
198#[inline(always)]
199fn flatten_encode_engine_error<E>(
200 error: TranscodeEncodeEngineError<E, CodecEncodeError<E>>,
201) -> CodecEncodeError<E> {
202 match error {
203 TranscodeEncodeEngineError::Codec(error)
204 | TranscodeEncodeEngineError::Hook(error) => error,
205 }
206}