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 fn default() -> Self {
272 Self::new(C::default())
273 }
274}