Skip to main content

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    fn reset(&mut self, output: &mut [C::Unit], output_index: usize) -> Result<usize, TranscodeEncodeErrorOf<C>> {
215        self.engine.reset(output, output_index)
216    }
217
218    /// Encodes values into the supplied output buffer.
219    ///
220    /// # Parameters
221    ///
222    /// - `input`: Input value slice.
223    /// - `input_index`: Absolute input index where encoding starts.
224    /// - `output`: Destination unit slice.
225    /// - `output_index`: Absolute output index where writing starts.
226    ///
227    /// # Returns
228    ///
229    /// Returns conversion progress for consumed input and produced output
230    /// units.
231    ///
232    /// # Errors
233    ///
234    /// Returns an encode error when indices are invalid or when encoding cannot
235    /// continue under current policy.
236    fn transcode(
237        &mut self,
238        input: &[C::Value],
239        input_index: usize,
240        output: &mut [C::Unit],
241        output_index: usize,
242    ) -> Result<TranscodeProgress, TranscodeEncodeErrorOf<C>> {
243        self.engine.transcode(input, input_index, output, output_index)
244    }
245
246    /// Finishes internally retained output after EOF.
247    ///
248    /// # Parameters
249    ///
250    /// - `output`: Destination unit slice for finalization output.
251    /// - `output_index`: Absolute output index where writing starts.
252    ///
253    /// # Returns
254    ///
255    /// Returns the number of units written by finalization.
256    ///
257    /// # Errors
258    ///
259    /// Returns a finish error if retained output cannot be fully emitted.
260    fn finish(&mut self, output: &mut [C::Unit], output_index: usize) -> Result<usize, TranscodeEncodeErrorOf<C>> {
261        self.engine.finish(output, output_index)
262    }
263}
264
265impl<C> TranscodeEncoder for CodecTranscodeEncoder<C>
266where
267    C: Codec,
268{
269    /// Codec-specific encoding failure carried by the adapter.
270    type EncodeError = C::EncodeError;
271}
272
273impl<C> Default for CodecTranscodeEncoder<C>
274where
275    C: Codec + Default,
276{
277    /// Creates a default codec-backed buffered encoder.
278    ///
279    /// # Returns
280    ///
281    /// Returns an encoder backed by `C::default()`.
282    fn default() -> Self {
283        Self::new(C::default())
284    }
285}