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    #[inline]
108    #[must_use]
109    pub const fn codec(&self) -> &C {
110        &self.codec
111    }
112
113    /// Returns a mutable reference to the wrapped codec.
114    ///
115    /// The next value operation starts a fresh codec lifecycle, so mutations
116    /// are applied to the following operation.
117    ///
118    /// # Returns
119    ///
120    /// An exclusive codec borrow that prevents simultaneous adapter operations.
121    #[inline]
122    #[must_use]
123    pub fn codec_mut(&mut self) -> &mut C {
124        &mut self.codec
125    }
126
127    /// Consumes the adapter and returns its wrapped codec.
128    ///
129    /// # Returns
130    ///
131    /// The owned codec in its current lifecycle state, without resetting it.
132    #[inline]
133    #[must_use]
134    pub fn into_codec(self) -> C {
135        self.codec
136    }
137
138    /// Encodes one borrowed value and appends the emitted units to `output`.
139    ///
140    /// This method is the reusable-buffer counterpart of
141    /// [`ValueEncoder::encode`]. It emits stream-start output through
142    /// [`Codec::encode_reset`], encodes `input` through [`Codec::encode`], and
143    /// finishes encode-side state through [`Codec::encode_finish`], appending
144    /// only the units actually written. When encoding fails, the vector length
145    /// is restored to its original value.
146    ///
147    /// # Parameters
148    ///
149    /// - `input`: Value to encode.
150    /// - `output`: Destination vector receiving appended encoded units.
151    ///
152    /// # Returns
153    ///
154    /// Returns the number of units appended to `output`.
155    ///
156    /// # Errors
157    ///
158    /// Returns the wrapped codec's encode error when reset output, `input`, or
159    /// finish output cannot be represented. Returns a framework error when
160    /// output length arithmetic overflows or the output allocation fails.
161    ///
162    /// # Panics
163    ///
164    /// Panics when the wrapped codec reports more reset or finish output than
165    /// its declared bounds, when [`Codec::encode_len`] exceeds
166    /// [`Codec::MAX_ENCODE_UNITS_PER_VALUE`], or when encoding writes a
167    /// different value width than [`Codec::encode_len`].
168    pub fn encode_into(
169        &mut self,
170        input: &C::Value,
171        output: &mut Vec<C::Unit>,
172    ) -> Result<usize, TranscodeEncodeErrorOf<C>>
173    where
174        C::Unit: Default,
175    {
176        let units = max_complete_encode_units::<C>()?;
177        let original_len = output.len();
178        let target_len = original_len
179            .checked_add(units)
180            .ok_or(CapacityError::OutputLengthOverflow)?;
181        try_reserve_vec(output, units).map_err(|_| TranscodeFailure::allocation_failed())?;
182        output.resize_with(target_len, C::Unit::default);
183
184        match encode_complete_value_into_reserved(&mut self.codec, input, output, original_len, units) {
185            Ok(written) => {
186                output.truncate(original_len + written);
187                Ok(written)
188            }
189            Err(error) => {
190                output.truncate(original_len);
191                Err(error)
192            }
193        }
194    }
195}
196
197impl<C> ValueEncoder<C::Value> for CodecValueEncoder<C>
198where
199    C: Codec,
200    C::Unit: Default,
201{
202    /// Owned units containing the complete reset/value/finish representation.
203    type Output = Vec<C::Unit>;
204    /// Codec-domain failures or framework capacity/allocation failures.
205    type Error = TranscodeEncodeErrorOf<C>;
206
207    /// Encodes one borrowed value into owned units.
208    ///
209    /// # Parameters
210    ///
211    /// - `input`: Value to encode.
212    ///
213    /// # Returns
214    ///
215    /// Returns stream-start output followed by the units written for `input`
216    /// and any encode-finish output.
217    ///
218    /// # Errors
219    ///
220    /// Returns the wrapped codec's encode error when reset output, `input`, or
221    /// finish output cannot be represented. Also returns framework capacity
222    /// overflow or allocation failures while preparing the output vector.
223    ///
224    /// # Panics
225    ///
226    /// Panics when the wrapped codec reports more reset or finish output than
227    /// its declared bounds, or a value width different from
228    /// [`Codec::encode_len`].
229    fn encode(&mut self, input: &C::Value) -> Result<Self::Output, Self::Error> {
230        let mut output = Vec::new();
231        self.encode_into(input, &mut output)?;
232        Ok(output)
233    }
234}
235
236impl<C> fmt::Debug for CodecValueEncoder<C>
237where
238    C: Codec + fmt::Debug,
239{
240    /// Formats the encoder without requiring finished values to be printable.
241    ///
242    /// # Parameters
243    ///
244    /// - `formatter`: Destination receiving the wrapped codec description.
245    ///
246    /// # Returns
247    ///
248    /// `Ok(())` when the description has been written.
249    ///
250    /// # Errors
251    ///
252    /// Propagates errors from the destination formatter.
253    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
254        formatter
255            .debug_struct("CodecValueEncoder")
256            .field("codec", &self.codec)
257            .finish()
258    }
259}
260
261impl<C> Default for CodecValueEncoder<C>
262where
263    C: Codec + Default,
264{
265    /// Creates an encoder from the default codec.
266    ///
267    /// # Returns
268    ///
269    /// An adapter owning `C::default()`, with the same compile-time unit-bound
270    /// checks as [`Self::new`].
271    #[inline]
272    fn default() -> Self {
273        Self::new(C::default())
274    }
275}