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::super::engine::TranscodeEncodeEngine;
11use super::CodecTranscodeEncodeHooks;
12use crate::CapacityError;
13use crate::Codec;
14use crate::TranscodeEncodeErrorOf;
15use crate::TranscodeEncoder;
16use crate::TranscodeProgress;
17use crate::Transcoder;
18
19/// Encodes values into caller-provided output units by using a [`Codec`].
20///
21/// `CodecTranscodeEncoder` is the default bridge from the low-level unchecked
22/// [`Codec`] contract to the buffered [`Transcoder`] and
23/// [`TranscodeEncoder`] contracts. It encodes complete values only; when the
24/// remaining output capacity is smaller than `codec.encode_len(value)`, it
25/// stops before consuming that input value and reports
26/// [`crate::TranscodeStatus::NeedOutput`].
27///
28/// # Type Parameters
29///
30/// - `C`: Low-level codec used to encode values.
31///
32/// # Examples
33///
34/// ```
35/// use core::convert::Infallible;
36/// use core::num::NonZeroUsize;
37///
38/// use qubit_codec::Codec;
39/// use qubit_codec::CodecTranscodeEncoder;
40/// use qubit_codec::DecodeFailure;
41/// use qubit_codec::Transcoder;
42///
43/// # struct ByteCodec;
44/// # impl Codec for ByteCodec {
45/// # type Value = u8;
46/// # type Unit = u8;
47/// # type DecodeError = Infallible;
48/// # type EncodeError = Infallible;
49/// # const MIN_UNITS_PER_VALUE: usize = 1;
50/// # const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
51/// # const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
52/// # unsafe fn decode(&mut self, input: &[u8], index: usize)
53/// # -> Result<(u8, NonZeroUsize), DecodeFailure<Infallible>> {
54/// # Ok((input[index], NonZeroUsize::new(1).unwrap()))
55/// # }
56/// # unsafe fn encode(&mut self, value: &u8, output: &mut [u8], index: usize)
57/// # -> Result<usize, Infallible> {
58/// # output[index] = *value;
59/// # Ok(1)
60/// # }
61/// # }
62/// let mut encoder = CodecTranscodeEncoder::new(ByteCodec);
63/// let mut output = [0; 2];
64/// assert_eq!(encoder.reset(&mut output, 0).unwrap(), 0);
65/// let progress = encoder.transcode(&[7, 9], 0, &mut output, 0).unwrap();
66/// assert_eq!(progress.written(), 2);
67/// assert_eq!(output, [7, 9]);
68/// ```
69#[derive(Debug)]
70pub struct CodecTranscodeEncoder<C> {
71 /// Common buffered encoding engine.
72 engine: TranscodeEncodeEngine<C, CodecTranscodeEncodeHooks>,
73}
74
75impl<C> CodecTranscodeEncoder<C>
76where
77 C: Codec,
78{
79 /// Creates a buffered encoder backed by `codec`.
80 ///
81 /// # Parameters
82 ///
83 /// - `codec`: Low-level codec used to encode values.
84 ///
85 /// # Returns
86 ///
87 /// Returns a buffered encoder adapter for the supplied codec.
88 #[inline]
89 #[must_use]
90 pub fn new(codec: C) -> Self {
91 Self {
92 engine: TranscodeEncodeEngine::new(codec, CodecTranscodeEncodeHooks),
93 }
94 }
95
96 /// Borrows the codec without allocating or changing stream state.
97 ///
98 /// # Returns
99 ///
100 /// The wrapped codec, borrowed for the lifetime of `self`.
101 #[inline]
102 #[must_use]
103 pub fn codec(&self) -> &C {
104 self.engine.codec()
105 }
106
107 /// Returns a mutable reference to the wrapped codec.
108 ///
109 /// Mutating a codec during an active stream can invalidate that stream's
110 /// assumptions; reset the adapter before continuing with the new codec
111 /// configuration.
112 ///
113 /// # Returns
114 ///
115 /// An exclusive borrow of the wrapped codec tied to `self`.
116 #[inline]
117 #[must_use]
118 pub fn codec_mut(&mut self) -> &mut C {
119 self.engine.codec_mut()
120 }
121
122 /// Consumes the adapter and returns its wrapped codec.
123 ///
124 /// Any buffered lifecycle state and internal hooks are discarded.
125 ///
126 /// # Returns
127 ///
128 /// The owned codec, without resetting or finishing it.
129 #[inline]
130 #[must_use]
131 pub fn into_codec(self) -> C {
132 let (codec, _) = self.engine.into_parts();
133 codec
134 }
135}
136
137impl<C> Transcoder for CodecTranscodeEncoder<C>
138where
139 C: Codec,
140{
141 /// Values accepted by the wrapped codec.
142 type Input = C::Value;
143 /// Representation units emitted by the wrapped codec.
144 type Output = C::Unit;
145 /// Domain, contract, and codec failures during buffered encoding.
146 type Error = TranscodeEncodeErrorOf<C>;
147
148 /// Gets the maximum number of output units needed for `input_len`
149 /// values.
150 ///
151 /// # Parameters
152 ///
153 /// - `input_len`: Logical input values the caller plans to encode.
154 ///
155 /// # Returns
156 ///
157 /// A conservative upper bound for output units.
158 ///
159 /// # Errors
160 ///
161 /// Returns [`CapacityError`] if the engine cannot represent the bound in
162 /// `usize`.
163 #[inline]
164 fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
165 self.engine.max_transcode_output_len(input_len)
166 }
167
168 /// Gets the global maximum units emitted when resetting internal state.
169 ///
170 /// # Returns
171 ///
172 /// The maximum units emitted when resetting internal state.
173 ///
174 /// # Errors
175 ///
176 /// Returns [`CapacityError`] if the engine cannot represent the bound in
177 /// `usize`.
178 #[inline]
179 fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
180 self.engine.max_reset_output_len()
181 }
182
183 /// Gets the global maximum units emitted by finishing internal state.
184 ///
185 /// # Returns
186 ///
187 /// The number of units that may be emitted by finishing state.
188 ///
189 /// # Errors
190 ///
191 /// Returns [`CapacityError`] if the engine cannot represent the bound in
192 /// `usize`.
193 #[inline]
194 fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
195 self.engine.max_finish_output_len()
196 }
197
198 /// Runs before-reset cleanup and emits stream-start output.
199 ///
200 /// # Parameters
201 ///
202 /// - `output`: Destination for codec reset units.
203 /// - `output_index`: Start position in `output`.
204 ///
205 /// # Returns
206 ///
207 /// The number of reset units written; previous lifecycle state is
208 /// discarded.
209 ///
210 /// # Errors
211 ///
212 /// Returns an error for an invalid index, insufficient reset capacity,
213 /// a violated codec contract, or a codec reset failure.
214 #[inline]
215 fn reset(&mut self, output: &mut [C::Unit], output_index: usize) -> Result<usize, TranscodeEncodeErrorOf<C>> {
216 self.engine.reset(output, output_index)
217 }
218
219 /// Encodes values into the supplied output buffer.
220 ///
221 /// # Parameters
222 ///
223 /// - `input`: Input value slice.
224 /// - `input_index`: Absolute input index where encoding starts.
225 /// - `output`: Destination unit slice.
226 /// - `output_index`: Absolute output index where writing starts.
227 ///
228 /// # Returns
229 ///
230 /// Returns conversion progress for consumed input and produced output
231 /// units.
232 ///
233 /// # Errors
234 ///
235 /// Returns an encode error when indices are invalid or when encoding cannot
236 /// continue under current policy.
237 #[inline]
238 fn transcode(
239 &mut self,
240 input: &[C::Value],
241 input_index: usize,
242 output: &mut [C::Unit],
243 output_index: usize,
244 ) -> Result<TranscodeProgress, TranscodeEncodeErrorOf<C>> {
245 self.engine.transcode(input, input_index, output, output_index)
246 }
247
248 /// Finishes internally retained output after EOF.
249 ///
250 /// # Parameters
251 ///
252 /// - `output`: Destination unit slice for finalization output.
253 /// - `output_index`: Absolute output index where writing starts.
254 ///
255 /// # Returns
256 ///
257 /// Returns the number of units written by finalization.
258 ///
259 /// # Errors
260 ///
261 /// Returns a finish error if retained output cannot be fully emitted.
262 #[inline]
263 fn finish(&mut self, output: &mut [C::Unit], output_index: usize) -> Result<usize, TranscodeEncodeErrorOf<C>> {
264 self.engine.finish(output, output_index)
265 }
266}
267
268impl<C> TranscodeEncoder for CodecTranscodeEncoder<C>
269where
270 C: Codec,
271{
272 /// Codec-specific encoding failure carried by the adapter.
273 type EncodeError = C::EncodeError;
274}
275
276impl<C> Default for CodecTranscodeEncoder<C>
277where
278 C: Codec + Default,
279{
280 /// Creates a default codec-backed buffered encoder.
281 ///
282 /// # Returns
283 ///
284 /// Returns an encoder backed by `C::default()`.
285 #[inline]
286 fn default() -> Self {
287 Self::new(C::default())
288 }
289}