Skip to main content

qubit_codec/value/
codec_value_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//! Value encoder adapter backed by a low-level codec.
9
10use core::fmt;
11
12use qubit_utils::try_reserve_vec;
13
14use super::ValueEncoder;
15use crate::CapacityError;
16use crate::Codec;
17use crate::TranscodeEncodeErrorOf;
18use crate::TranscodeFailure;
19use crate::codec::assert_unit_bounds;
20use crate::value::codec_value_lifecycle::encode_complete_value_into_reserved;
21use crate::value::codec_value_lifecycle::max_complete_encode_units;
22
23/// Encodes one borrowed value into owned units by using a [`Codec`].
24///
25/// `CodecValueEncoder` is the default bridge from the low-level unchecked
26/// [`Codec`] contract to the convenience-layer [`ValueEncoder`] contract. Each
27/// call emits stream-start output through [`Codec::encode_reset`], encodes one
28/// value through [`Codec::encode`], finishes encode-side state through
29/// [`Codec::encode_finish`], and returns the owned output truncated to the
30/// units actually written.
31///
32/// # Type Parameters
33///
34/// - `C`: Low-level codec used to encode one value.
35///
36/// # Examples
37///
38/// ```
39/// use core::convert::Infallible;
40/// use core::num::NonZeroUsize;
41///
42/// use qubit_codec::Codec;
43/// use qubit_codec::CodecValueEncoder;
44/// use qubit_codec::DecodeFailure;
45/// use qubit_codec::ValueEncoder;
46///
47/// # struct IdentityCodec;
48/// # impl Codec for IdentityCodec {
49/// #     type Value = u8;
50/// #     type Unit = u8;
51/// #     type DecodeError = Infallible;
52/// #     type EncodeError = Infallible;
53/// #     const MIN_UNITS_PER_VALUE: usize = 1;
54/// #     const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
55/// #     const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
56/// #     unsafe fn decode(&mut self, input: &[u8], index: usize)
57/// #         -> Result<(u8, NonZeroUsize), DecodeFailure<Infallible>> {
58/// #         Ok((input[index], NonZeroUsize::MIN))
59/// #     }
60/// #     unsafe fn encode(&mut self, value: &u8, output: &mut [u8], index: usize)
61/// #         -> Result<usize, Infallible> {
62/// #         output[index] = *value;
63/// #         Ok(1)
64/// #     }
65/// # }
66/// let mut encoder = CodecValueEncoder::new(IdentityCodec);
67/// assert_eq!(encoder.encode(&7_u8).unwrap(), vec![7]);
68/// ```
69pub struct CodecValueEncoder<C> {
70    /// Low-level codec used for one-value encoding.
71    codec: C,
72}
73
74impl<C> CodecValueEncoder<C>
75where
76    C: Codec,
77{
78    /// Creates an encoder backed by `codec`.
79    ///
80    /// # Parameters
81    ///
82    /// - `codec`: Low-level codec used to encode one value.
83    ///
84    /// # Returns
85    ///
86    /// Returns a value encoder adapter for the supplied codec.
87    ///
88    /// # Compile-Time Checks
89    ///
90    /// Fails to compile when the supplied codec declares a zero decode unit
91    /// bound or when [`Codec::MIN_UNITS_PER_VALUE`] exceeds
92    /// [`Codec::MAX_DECODE_UNITS_PER_VALUE`]. The encode bound may be zero for
93    /// a fully buffered codec.
94    #[inline]
95    #[must_use]
96    pub fn new(codec: C) -> Self {
97        assert_unit_bounds::<C>();
98        Self { codec }
99    }
100
101    /// Returns a shared reference to the wrapped codec.
102    ///
103    /// # Returns
104    ///
105    /// The codec borrowed for as long as this adapter is shared, without
106    /// cloning.
107    ///
108    /// The returned reference should be used to inspect the wrapped codec.
109    #[inline]
110    #[must_use = "use the reference to inspect the wrapped codec"]
111    pub const fn codec(&self) -> &C {
112        &self.codec
113    }
114
115    /// Returns a mutable reference to the wrapped codec.
116    ///
117    /// The next value operation starts a fresh codec lifecycle, so mutations
118    /// are applied to the following operation.
119    ///
120    /// The returned reference should be used to update the wrapped codec.
121    ///
122    /// # Returns
123    ///
124    /// An exclusive codec borrow that prevents simultaneous adapter operations.
125    #[inline]
126    #[must_use = "use the reference to update the wrapped codec"]
127    pub fn codec_mut(&mut self) -> &mut C {
128        &mut self.codec
129    }
130
131    /// Consumes the adapter and returns its wrapped codec.
132    ///
133    /// # Returns
134    ///
135    /// The owned codec in its current lifecycle state, without resetting it.
136    #[inline]
137    #[must_use]
138    pub fn into_codec(self) -> C {
139        self.codec
140    }
141
142    /// Encodes one borrowed value and appends the emitted units to `output`.
143    ///
144    /// This method is the reusable-buffer counterpart of
145    /// [`ValueEncoder::encode`]. It emits stream-start output through
146    /// [`Codec::encode_reset`], encodes `input` through [`Codec::encode`], and
147    /// finishes encode-side state through [`Codec::encode_finish`], appending
148    /// only the units actually written. When encoding fails, the vector length
149    /// is restored to its original value.
150    ///
151    /// # Parameters
152    ///
153    /// - `input`: Value to encode.
154    /// - `output`: Destination vector receiving appended encoded units.
155    ///
156    /// # Returns
157    ///
158    /// Returns the number of units appended to `output`.
159    ///
160    /// # Errors
161    ///
162    /// Returns the wrapped codec's encode error when reset output, `input`, or
163    /// finish output cannot be represented. Returns a framework error when
164    /// output length arithmetic overflows or the output allocation fails.
165    ///
166    /// # Panics
167    ///
168    /// Panics when the wrapped codec reports more reset or finish output than
169    /// its declared bounds, when [`Codec::encode_len`] exceeds
170    /// [`Codec::MAX_ENCODE_UNITS_PER_VALUE`], or when encoding writes a
171    /// different value width than [`Codec::encode_len`].
172    pub fn encode_into(
173        &mut self,
174        input: &C::Value,
175        output: &mut Vec<C::Unit>,
176    ) -> Result<usize, TranscodeEncodeErrorOf<C>>
177    where
178        C::Unit: Default,
179    {
180        let units = max_complete_encode_units::<C>()?;
181        let original_len = output.len();
182        let target_len = original_len
183            .checked_add(units)
184            .ok_or(CapacityError::OutputLengthOverflow)?;
185        try_reserve_vec(output, units).map_err(|_| TranscodeFailure::allocation_failed())?;
186        output.resize_with(target_len, C::Unit::default);
187
188        match encode_complete_value_into_reserved(&mut self.codec, input, output, original_len, units) {
189            Ok(written) => {
190                output.truncate(original_len + written);
191                Ok(written)
192            }
193            Err(error) => {
194                output.truncate(original_len);
195                Err(error)
196            }
197        }
198    }
199}
200
201impl<C> ValueEncoder<C::Value> for CodecValueEncoder<C>
202where
203    C: Codec,
204    C::Unit: Default,
205{
206    /// Owned units containing the complete reset/value/finish representation.
207    type Output = Vec<C::Unit>;
208    /// Codec-domain failures or framework capacity/allocation failures.
209    type Error = TranscodeEncodeErrorOf<C>;
210
211    /// Encodes one borrowed value into owned units.
212    ///
213    /// # Parameters
214    ///
215    /// - `input`: Value to encode.
216    ///
217    /// # Returns
218    ///
219    /// Returns stream-start output followed by the units written for `input`
220    /// and any encode-finish output.
221    ///
222    /// # Errors
223    ///
224    /// Returns the wrapped codec's encode error when reset output, `input`, or
225    /// finish output cannot be represented. Also returns framework capacity
226    /// overflow or allocation failures while preparing the output vector.
227    ///
228    /// # Panics
229    ///
230    /// Panics when the wrapped codec reports more reset or finish output than
231    /// its declared bounds, or a value width different from
232    /// [`Codec::encode_len`].
233    fn encode(&mut self, input: &C::Value) -> Result<Self::Output, Self::Error> {
234        let mut output = Vec::new();
235        self.encode_into(input, &mut output)?;
236        Ok(output)
237    }
238}
239
240impl<C> fmt::Debug for CodecValueEncoder<C>
241where
242    C: Codec + fmt::Debug,
243{
244    /// Formats the encoder without requiring finished values to be printable.
245    ///
246    /// # Parameters
247    ///
248    /// - `formatter`: Destination receiving the wrapped codec description.
249    ///
250    /// # Returns
251    ///
252    /// `Ok(())` when the description has been written.
253    ///
254    /// # Errors
255    ///
256    /// Propagates errors from the destination formatter.
257    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
258        formatter
259            .debug_struct("CodecValueEncoder")
260            .field("codec", &self.codec)
261            .finish()
262    }
263}
264
265impl<C> Default for CodecValueEncoder<C>
266where
267    C: Codec + Default,
268{
269    /// Creates an encoder from the default codec.
270    ///
271    /// # Returns
272    ///
273    /// An adapter owning `C::default()`, with the same compile-time unit-bound
274    /// checks as [`Self::new`].
275    #[inline]
276    fn default() -> Self {
277        Self::new(C::default())
278    }
279}