Skip to main content

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}