qubit_codec/value/codec_value_decoder.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 decoder adapter backed by a low-level codec.
9
10use core::fmt;
11use qubit_utils::try_reserve_vec;
12
13use super::{
14 DecodeLifecycleOutput,
15 DecodeLifecycleProgress,
16 ValueDecoder,
17};
18use crate::{
19 Codec,
20 TranscodeDecodeErrorOf,
21 TranscodeFailure,
22 codec::assert_unit_bounds,
23 value::codec_value_lifecycle::decode_exact_complete_value,
24};
25
26/// Decodes one encoded unit slice into one owned value by using a [`Codec`].
27///
28/// `CodecValueDecoder` is the default bridge from the low-level unchecked
29/// [`Codec`] contract to the convenience-layer [`ValueDecoder`] contract. The
30/// supplied input slice must contain exactly one encoded value. The adapter
31/// runs the complete decode lifecycle, including [`Codec::decode_finish`],
32/// before returning the decoded value.
33///
34/// # Type Parameters
35///
36/// - `C`: Low-level codec used to decode one value.
37pub struct CodecValueDecoder<C>
38where
39 C: Codec,
40{
41 /// Low-level codec used for one-value decoding.
42 codec: C,
43}
44
45impl<C> CodecValueDecoder<C>
46where
47 C: Codec,
48{
49 /// Creates a decoder backed by `codec`.
50 ///
51 /// # Parameters
52 ///
53 /// - `codec`: Low-level codec used to decode one value.
54 ///
55 /// # Returns
56 ///
57 /// Returns a value decoder adapter for the supplied codec.
58 ///
59 /// # Panics
60 ///
61 /// Panics when the supplied codec declares invalid unit bounds or an
62 /// invalid decode unit bounds.
63 #[inline]
64 #[must_use]
65 pub fn new(codec: C) -> Self {
66 assert_unit_bounds::<C>();
67 Self { codec }
68 }
69
70 /// Returns a shared reference to the wrapped codec.
71 #[inline(always)]
72 #[must_use]
73 pub const fn codec(&self) -> &C {
74 &self.codec
75 }
76
77 /// Returns a mutable reference to the wrapped codec.
78 ///
79 /// The next value operation starts a fresh codec lifecycle, so mutations
80 /// are applied to the following operation.
81 #[inline(always)]
82 #[must_use]
83 pub fn codec_mut(&mut self) -> &mut C {
84 &mut self.codec
85 }
86
87 /// Consumes the adapter and returns its wrapped codec.
88 #[inline(always)]
89 #[must_use]
90 pub fn into_codec(self) -> C {
91 self.codec
92 }
93
94 /// Decodes exactly one encoded value from `input`.
95 ///
96 /// # Parameters
97 ///
98 /// - `input`: Encoded units for exactly one value.
99 ///
100 /// # Returns
101 ///
102 /// Returns the decoded value.
103 ///
104 /// Codecs that may emit decode reset or finish values must use
105 /// [`Self::decode_lifecycle`] or [`Self::decode_lifecycle_with_scratch`].
106 ///
107 /// # Errors
108 ///
109 /// Returns [`crate::TranscodeFailure::UnsupportedDecodeLifecycleOutput`]
110 /// before inspecting `input` or running codec hooks when reset or finish
111 /// may emit values. Returns
112 /// [`crate::TranscodeFailure::IncompleteInput`] when fewer than
113 /// [`Codec::MIN_UNITS_PER_VALUE`] units are available or when
114 /// [`crate::DecodeFailure::Incomplete`] is reported by the codec. In this
115 /// one-shot API, incomplete input is a terminal error rather than a
116 /// resumable streaming status. Returns a domain error when the wrapped
117 /// codec rejects or cannot finish the input. Returns
118 /// [`crate::TranscodeFailure::TrailingInput`] when a value is decoded but
119 /// extra input remains.
120 ///
121 /// # Panics
122 ///
123 /// Panics when the wrapped codec reports a consumed unit count larger than
124 /// the input slice length or [`Codec::MAX_DECODE_UNITS_PER_VALUE`].
125 pub fn decode(
126 &mut self,
127 input: &[C::Unit],
128 ) -> Result<C::Value, TranscodeDecodeErrorOf<C>> {
129 TranscodeFailure::ensure_no_decode_lifecycle_output::<C>()?;
130 let (value, reset_written, finish_written) =
131 decode_exact_complete_value(
132 &mut self.codec,
133 input,
134 &mut [],
135 &mut [],
136 )?;
137 debug_assert_eq!(0, reset_written);
138 debug_assert_eq!(0, finish_written);
139 Ok(value)
140 }
141
142 /// Decodes exactly one value and preserves all lifecycle output.
143 ///
144 /// # Parameters
145 ///
146 /// - `input`: Encoded units for exactly one value.
147 ///
148 /// # Returns
149 ///
150 /// Returns reset values, the main decoded value, and finish values in
151 /// separate owned buffers.
152 ///
153 /// # Errors
154 ///
155 /// Returns a framework error when input is incomplete or has trailing
156 /// units. Returns a phase-aware domain error when reset, decode, or finish
157 /// fails.
158 ///
159 /// # Panics
160 ///
161 /// Panics when the wrapped codec violates its declared reset, decode, or
162 /// finish bounds.
163 pub fn decode_lifecycle(
164 &mut self,
165 input: &[C::Unit],
166 ) -> Result<DecodeLifecycleOutput<C::Value>, TranscodeDecodeErrorOf<C>>
167 where
168 C::Value: Default,
169 {
170 let mut reset = Vec::new();
171 try_reserve_vec(&mut reset, C::MAX_DECODE_RESET_VALUES)
172 .map_err(|_| TranscodeFailure::allocation_failed())?;
173 reset.resize_with(C::MAX_DECODE_RESET_VALUES, C::Value::default);
174 let mut finish = Vec::new();
175 try_reserve_vec(&mut finish, C::MAX_DECODE_FINISH_VALUES)
176 .map_err(|_| TranscodeFailure::allocation_failed())?;
177 finish.resize_with(C::MAX_DECODE_FINISH_VALUES, C::Value::default);
178 let (value, reset_written, finish_written) = self
179 .decode_lifecycle_with_scratch(input, &mut reset, &mut finish)?
180 .into_parts();
181 reset.truncate(reset_written);
182 finish.truncate(finish_written);
183 Ok(DecodeLifecycleOutput::new(reset, value, finish))
184 }
185
186 /// Decodes exactly one value into separate lifecycle output buffers.
187 ///
188 /// # Parameters
189 ///
190 /// - `input`: Encoded units for exactly one value.
191 /// - `reset_output`: Destination for values emitted by decode reset.
192 /// - `finish_output`: Destination for values emitted by decode finish.
193 ///
194 /// # Returns
195 ///
196 /// Returns the main decoded value and the initialized lengths of both
197 /// lifecycle output buffers.
198 ///
199 /// # Errors
200 ///
201 /// Returns a framework error before running any lifecycle hook when either
202 /// output buffer is shorter than its corresponding codec bound. Also
203 /// returns incomplete-input, trailing-input, or phase-aware domain errors.
204 ///
205 /// # Panics
206 ///
207 /// Panics when the wrapped codec violates its declared reset, decode, or
208 /// finish bounds.
209 pub fn decode_lifecycle_with_scratch(
210 &mut self,
211 input: &[C::Unit],
212 reset_output: &mut [C::Value],
213 finish_output: &mut [C::Value],
214 ) -> Result<DecodeLifecycleProgress<C::Value>, TranscodeDecodeErrorOf<C>>
215 {
216 let (value, reset_written, finish_written) =
217 decode_exact_complete_value(
218 &mut self.codec,
219 input,
220 reset_output,
221 finish_output,
222 )?;
223 Ok(DecodeLifecycleProgress::new(
224 value,
225 reset_written,
226 finish_written,
227 ))
228 }
229}
230
231impl<C> ValueDecoder<[C::Unit]> for CodecValueDecoder<C>
232where
233 C: Codec,
234{
235 type Output = C::Value;
236 type Error = TranscodeDecodeErrorOf<C>;
237
238 #[inline(always)]
239 fn decode(
240 &mut self,
241 input: &[C::Unit],
242 ) -> Result<Self::Output, Self::Error> {
243 self.decode(input)
244 }
245}
246
247impl<C> fmt::Debug for CodecValueDecoder<C>
248where
249 C: Codec + fmt::Debug,
250{
251 /// Formats the decoder without requiring finished values to be printable.
252 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
253 formatter
254 .debug_struct("CodecValueDecoder")
255 .field("codec", &self.codec)
256 .finish()
257 }
258}
259
260impl<C> Default for CodecValueDecoder<C>
261where
262 C: Codec + Default,
263{
264 /// Creates a decoder from the default codec.
265 #[inline(always)]
266 fn default() -> Self {
267 Self::new(C::default())
268 }
269}