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}