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 super::ValueDecoder;
13use crate::{
14 Codec,
15 CodecDecodeError,
16 CodecValueExt,
17 TranscodeError,
18 codec::assert_unit_bounds,
19};
20
21/// Decodes one encoded unit slice into one owned value by using a [`Codec`].
22///
23/// `CodecValueDecoder` is the default bridge from the low-level unchecked
24/// [`Codec`] contract to the convenience-layer [`ValueDecoder`] contract. The
25/// supplied input slice must contain exactly one encoded value. After a
26/// successful decode, the adapter calls [`Codec::decode_flush`] to reset
27/// decode-side stream state for the next call.
28///
29/// # Type Parameters
30///
31/// - `C`: Low-level codec used to decode one value.
32pub struct CodecValueDecoder<C>
33where
34 C: Codec,
35{
36 /// Low-level codec used for one-value decoding.
37 codec: C,
38 /// Reusable storage for values emitted by `Codec::decode_flush`.
39 flush_scratch: Vec<C::Value>,
40}
41
42impl<C> fmt::Debug for CodecValueDecoder<C>
43where
44 C: Codec + fmt::Debug,
45{
46 /// Formats the decoder without requiring flushed values to be printable.
47 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
48 formatter
49 .debug_struct("CodecValueDecoder")
50 .field("codec", &self.codec)
51 .field("flush_scratch_len", &self.flush_scratch.len())
52 .field("flush_scratch_capacity", &self.flush_scratch.capacity())
53 .finish()
54 }
55}
56
57impl<C> Default for CodecValueDecoder<C>
58where
59 C: Codec + Default,
60{
61 /// Creates a decoder from the default codec.
62 #[inline(always)]
63 fn default() -> Self {
64 Self::new(C::default())
65 }
66}
67
68impl<C> CodecValueDecoder<C>
69where
70 C: Codec,
71{
72 /// Creates a decoder backed by `codec`.
73 ///
74 /// # Parameters
75 ///
76 /// - `codec`: Low-level codec used to decode one value.
77 ///
78 /// # Returns
79 ///
80 /// Returns a value decoder adapter for the supplied codec.
81 ///
82 /// # Panics
83 ///
84 /// In debug builds, panics when the supplied codec violates the
85 /// [`Codec::MIN_UNITS_PER_VALUE`] / [`Codec::MAX_UNITS_PER_VALUE`] ordering
86 /// invariant. Release builds skip this check because the invariant is the
87 /// responsibility of the [`Codec`] implementation.
88 #[inline]
89 #[must_use]
90 pub fn new(codec: C) -> Self {
91 assert_unit_bounds::<C>();
92 Self {
93 codec,
94 flush_scratch: Vec::new(),
95 }
96 }
97}
98
99impl<C> ValueDecoder<[C::Unit]> for CodecValueDecoder<C>
100where
101 C: Codec,
102 C::Value: Default,
103{
104 type Output = C::Value;
105 type Error = TranscodeError<CodecDecodeError<C::DecodeError>>;
106
107 /// Decodes exactly one encoded value from `input`.
108 ///
109 /// # Parameters
110 ///
111 /// - `input`: Encoded units for exactly one value.
112 ///
113 /// # Returns
114 ///
115 /// Returns the decoded value.
116 ///
117 /// # Errors
118 ///
119 /// Returns [`CodecDecodeError::Incomplete`] when fewer than
120 /// [`Codec::MIN_UNITS_PER_VALUE`] units are available. Returns
121 /// [`CodecDecodeError::Decode`] when the wrapped codec rejects the input.
122 /// Returns [`CodecDecodeError::TrailingInput`] when a value is decoded but
123 /// extra input remains.
124 ///
125 /// # Panics
126 ///
127 /// Panics when the wrapped codec reports a consumed unit count larger than
128 /// the input slice length, or when flush output exceeds
129 /// [`Codec::MAX_DECODE_FLUSH_VALUES`].
130 fn decode(
131 &mut self,
132 input: &[C::Unit],
133 ) -> Result<Self::Output, Self::Error> {
134 let flush_cap = C::MAX_DECODE_FLUSH_VALUES;
135 let (value, _) = if flush_cap == 0 {
136 self.codec
137 .decode_exact_value_with_flush(input, &mut [], 0)?
138 } else {
139 if self.flush_scratch.len() < flush_cap {
140 self.flush_scratch.resize_with(flush_cap, C::Value::default);
141 }
142 self.codec.decode_exact_value_with_flush(
143 input,
144 &mut self.flush_scratch,
145 0,
146 )?
147 };
148
149 Ok(value)
150 }
151}