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