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 qubit_codec::{Codec, CodecValueEncoder};
40///
41/// fn make_encoder<C: Codec>(codec: C) -> CodecValueEncoder<C> {
42///     CodecValueEncoder::new(codec)
43/// }
44/// ```
45pub struct CodecValueEncoder<C> {
46    /// Low-level codec used for one-value encoding.
47    codec: C,
48}
49
50impl<C> CodecValueEncoder<C>
51where
52    C: Codec,
53{
54    /// Creates an encoder backed by `codec`.
55    ///
56    /// # Parameters
57    ///
58    /// - `codec`: Low-level codec used to encode one value.
59    ///
60    /// # Returns
61    ///
62    /// Returns a value encoder adapter for the supplied codec.
63    ///
64    /// # Compile-Time Checks
65    ///
66    /// Fails to compile when the supplied codec declares a zero decode unit
67    /// bound or when [`Codec::MIN_UNITS_PER_VALUE`] exceeds
68    /// [`Codec::MAX_DECODE_UNITS_PER_VALUE`]. The encode bound may be zero for
69    /// a fully buffered codec.
70    #[inline]
71    #[must_use]
72    pub fn new(codec: C) -> Self {
73        assert_unit_bounds::<C>();
74        Self { codec }
75    }
76
77    /// Returns a shared reference to the wrapped codec.
78    #[inline(always)]
79    #[must_use]
80    pub const fn codec(&self) -> &C {
81        &self.codec
82    }
83
84    /// Returns a mutable reference to the wrapped codec.
85    ///
86    /// The next value operation starts a fresh codec lifecycle, so mutations
87    /// are applied to the following operation.
88    #[inline(always)]
89    #[must_use]
90    pub fn codec_mut(&mut self) -> &mut C {
91        &mut self.codec
92    }
93
94    /// Consumes the adapter and returns its wrapped codec.
95    #[inline(always)]
96    #[must_use]
97    pub fn into_codec(self) -> C {
98        self.codec
99    }
100
101    /// Encodes one borrowed value and appends the emitted units to `output`.
102    ///
103    /// This method is the reusable-buffer counterpart of
104    /// [`ValueEncoder::encode`]. It emits stream-start output through
105    /// [`Codec::encode_reset`], encodes `input` through [`Codec::encode`], and
106    /// finishes encode-side state through [`Codec::encode_finish`], appending
107    /// only the units actually written. When encoding fails, the vector length
108    /// is restored to its original value.
109    ///
110    /// # Parameters
111    ///
112    /// - `input`: Value to encode.
113    /// - `output`: Destination vector receiving appended encoded units.
114    ///
115    /// # Returns
116    ///
117    /// Returns the number of units appended to `output`.
118    ///
119    /// # Errors
120    ///
121    /// Returns the wrapped codec's encode error when reset output, `input`, or
122    /// finish output cannot be represented. Returns a framework error when
123    /// output length arithmetic overflows.
124    ///
125    /// # Panics
126    ///
127    /// Panics when the wrapped codec reports more reset or finish output than
128    /// its declared bounds, when [`Codec::encode_len`] exceeds
129    /// [`Codec::MAX_ENCODE_UNITS_PER_VALUE`], or when encoding writes a
130    /// different value width than [`Codec::encode_len`].
131    pub fn encode_into(
132        &mut self,
133        input: &C::Value,
134        output: &mut Vec<C::Unit>,
135    ) -> Result<usize, TranscodeEncodeErrorOf<C>>
136    where
137        C::Unit: Default,
138    {
139        let units = max_complete_encode_units::<C>()?;
140        let original_len = output.len();
141        let target_len = original_len
142            .checked_add(units)
143            .ok_or(CapacityError::OutputLengthOverflow)?;
144        try_reserve_vec(output, units).map_err(|_| TranscodeFailure::allocation_failed())?;
145        output.resize_with(target_len, C::Unit::default);
146
147        match encode_complete_value_into_reserved(&mut self.codec, input, output, original_len, units) {
148            Ok(written) => {
149                output.truncate(original_len + written);
150                Ok(written)
151            }
152            Err(error) => {
153                output.truncate(original_len);
154                Err(error)
155            }
156        }
157    }
158}
159
160impl<C> ValueEncoder<C::Value> for CodecValueEncoder<C>
161where
162    C: Codec,
163    C::Unit: Default,
164{
165    type Output = Vec<C::Unit>;
166    type Error = TranscodeEncodeErrorOf<C>;
167
168    /// Encodes one borrowed value into owned units.
169    ///
170    /// # Parameters
171    ///
172    /// - `input`: Value to encode.
173    ///
174    /// # Returns
175    ///
176    /// Returns stream-start output followed by the units written for `input`
177    /// and any encode-finish output.
178    ///
179    /// # Errors
180    ///
181    /// Returns the wrapped codec's encode error when reset output, `input`, or
182    /// finish output cannot be represented.
183    ///
184    /// # Panics
185    ///
186    /// Panics when the wrapped codec reports more reset or finish output than
187    /// its declared bounds, or a value width different from
188    /// [`Codec::encode_len`].
189    fn encode(&mut self, input: &C::Value) -> Result<Self::Output, Self::Error> {
190        let mut output = Vec::new();
191        self.encode_into(input, &mut output)?;
192        Ok(output)
193    }
194}
195
196impl<C> fmt::Debug for CodecValueEncoder<C>
197where
198    C: Codec + fmt::Debug,
199{
200    /// Formats the encoder without requiring finished values to be printable.
201    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
202        formatter
203            .debug_struct("CodecValueEncoder")
204            .field("codec", &self.codec)
205            .finish()
206    }
207}
208
209impl<C> Default for CodecValueEncoder<C>
210where
211    C: Codec + Default,
212{
213    /// Creates an encoder from the default codec.
214    #[inline(always)]
215    fn default() -> Self {
216        Self::new(C::default())
217    }
218}