Skip to main content

qubit_codec/buffered/
buffered_decode_hooks.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Policy hooks used by buffered decoder engines.
11
12use super::{
13    decode_action::DecodeAction,
14    decode_context::DecodeContext,
15};
16use crate::{
17    CapacityError,
18    Codec,
19};
20
21/// Policy hooks for [`crate::BufferedDecodeEngine`].
22///
23/// Hooks own policy state, such as malformed-input replacement behavior. The
24/// engine passes the codec into hook methods when policy code needs codec
25/// metadata.
26///
27/// Implement this trait when a buffered decoder needs policy decisions after
28/// the low-level codec reports an error. The engine handles input/output cursor
29/// bookkeeping, output-capacity checks, and successful one-value decodes; hooks
30/// decide whether a decode error means "need more input", "skip these units",
31/// "emit a replacement value", or "return an error".
32///
33/// The hook receives a [`DecodeContext`] with absolute input/output cursors, so
34/// errors can include useful positions without duplicating engine arithmetic.
35/// Stateful hooks may also use [`finish`](Self::finish) to emit final values
36/// after the caller has supplied all input and handled any incomplete tail.
37///
38/// # Example
39///
40/// This hook maps incomplete codec errors to `NeedInput`, replaces malformed
41/// units with `b'?'`, and otherwise lets the engine keep decoding.
42///
43/// ```rust
44/// use core::num::NonZeroUsize;
45/// use qubit_codec::{
46///     BufferedDecodeHooks,
47///     Codec,
48///     CodecDecodeError,
49///     DecodeAction,
50///     DecodeContext,
51/// };
52///
53/// #[derive(Clone, Copy)]
54/// struct MyCodec;
55///
56/// #[derive(Clone, Copy, Debug, Eq, PartialEq)]
57/// enum MyDecodeError {
58///     Incomplete { required_total: usize },
59///     Malformed { consumed: NonZeroUsize },
60/// }
61///
62/// unsafe impl Codec for MyCodec {
63///     type Value = u8;
64///     type Unit = u8;
65///     type DecodeError = MyDecodeError;
66///     type EncodeError = core::convert::Infallible;
67///
68///     fn min_units_per_value(&self) -> NonZeroUsize {
69///         NonZeroUsize::MIN
70///     }
71///
72///     fn max_units_per_value(&self) -> NonZeroUsize {
73///         NonZeroUsize::MIN
74///     }
75///
76///     unsafe fn decode_unchecked(
77///         &self,
78///         input: &[u8],
79///         index: usize,
80///     ) -> Result<(u8, NonZeroUsize), Self::DecodeError> {
81///         match input[index] {
82///             0xff => Err(MyDecodeError::Malformed {
83///                 consumed: NonZeroUsize::MIN,
84///             }),
85///             value => Ok((value, NonZeroUsize::MIN)),
86///         }
87///     }
88///
89///     unsafe fn encode_unchecked(
90///         &self,
91///         value: &u8,
92///         output: &mut [u8],
93///         index: usize,
94///     ) -> Result<usize, Self::EncodeError> {
95///         output[index] = *value;
96///         Ok(1)
97///     }
98/// }
99///
100/// struct ReplacementHooks;
101///
102/// impl BufferedDecodeHooks<MyCodec> for ReplacementHooks {
103///     type Error = CodecDecodeError<MyDecodeError>;
104///
105///     fn handle_decode_error(
106///         &mut self,
107///         _codec: &MyCodec,
108///         error: MyDecodeError,
109///         _context: DecodeContext,
110///     ) -> Result<DecodeAction<u8>, Self::Error> {
111///         match error {
112///             MyDecodeError::Incomplete { required_total } => {
113///                 Ok(DecodeAction::NeedInput { required_total })
114///             }
115///             MyDecodeError::Malformed { consumed } => {
116///                 Ok(DecodeAction::Emit { value: b'?', consumed })
117///             }
118///         }
119///     }
120///
121///     fn invalid_input_index(
122///         &mut self,
123///         _codec: &MyCodec,
124///         index: usize,
125///         input_len: usize,
126///     ) -> Self::Error {
127///         CodecDecodeError::invalid_input_index(index, input_len)
128///     }
129///
130///     fn invalid_output_index(
131///         &mut self,
132///         _codec: &MyCodec,
133///         index: usize,
134///         output_len: usize,
135///     ) -> Self::Error {
136///         CodecDecodeError::invalid_output_index(index, output_len)
137///     }
138/// }
139/// ```
140///
141/// # Type Parameters
142///
143/// - `C`: Low-level codec owned by the engine.
144pub trait BufferedDecodeHooks<C>
145where
146    C: Codec,
147{
148    /// Error type returned by the buffered decoder.
149    type Error;
150
151    /// Returns an upper bound for decoded values produced from `input_len` units.
152    ///
153    /// # Parameters
154    ///
155    /// - `codec`: Low-level codec owned by the engine.
156    /// - `input_len`: Number of source units the caller plans to decode.
157    ///
158    /// # Returns
159    ///
160    /// Returns a conservative upper bound derived from
161    /// [`Codec::min_units_per_value`].
162    #[must_use = "capacity planning can fail on overflow"]
163    #[inline]
164    fn max_output_len(&self, codec: &C, input_len: usize) -> Result<usize, CapacityError> {
165        Ok(input_len / codec.min_units_per_value().get())
166    }
167
168    /// Returns an upper bound for values emitted by finishing hook-owned state.
169    ///
170    /// `finish` never receives more input. Implementations must only report
171    /// output derived from hook-owned state that remains after the caller has
172    /// handled any incomplete input tail.
173    ///
174    /// # Parameters
175    ///
176    /// - `codec`: Low-level codec owned by the engine.
177    ///
178    /// # Returns
179    ///
180    /// Returns the finite final-output upper bound.
181    #[must_use]
182    #[inline(always)]
183    fn max_finish_output_len(&self, _codec: &C) -> usize {
184        0
185    }
186
187    /// Handles a codec decode error during `transcode`.
188    ///
189    /// # Parameters
190    ///
191    /// - `codec`: Low-level codec owned by the engine.
192    /// - `error`: Error returned by the codec.
193    /// - `context`: Decode attempt context.
194    ///
195    /// # Returns
196    ///
197    /// Returns the action selected by this hook policy.
198    ///
199    /// Returned actions must be consistent with `context.available`:
200    /// - `NeedInput.required_total` must be greater than `context.available`;
201    /// - `Skip.consumed` and `Emit.consumed` must not exceed
202    ///   `context.available`.
203    ///
204    /// The engine treats violations as hook bugs and panics.
205    ///
206    /// # Errors
207    ///
208    /// Returns `Self::Error` when the policy rejects the input.
209    fn handle_decode_error(
210        &mut self,
211        codec: &C,
212        error: C::DecodeError,
213        context: DecodeContext,
214    ) -> Result<DecodeAction<C::Value>, Self::Error>;
215
216    /// Creates an error for a caller-supplied input index outside the input slice.
217    ///
218    /// The generic engine detects this before invoking the codec. The hook owns
219    /// the concrete decoder error type, so it also owns the adapter-level error
220    /// construction.
221    ///
222    /// # Parameters
223    ///
224    /// - `codec`: Low-level codec owned by the engine.
225    /// - `index`: Invalid input index supplied by the caller.
226    /// - `input_len`: Length of the input slice.
227    ///
228    /// # Returns
229    ///
230    /// Returns the hook-specific error representing `index > input_len`.
231    fn invalid_input_index(&mut self, codec: &C, index: usize, input_len: usize) -> Self::Error;
232
233    /// Creates an error for a caller-supplied output index outside the output slice.
234    ///
235    /// The generic engine detects this before writing any decoded value. The
236    /// hook owns the concrete decoder error type, so it also owns the
237    /// adapter-level error construction.
238    ///
239    /// # Parameters
240    ///
241    /// - `codec`: Low-level codec owned by the engine.
242    /// - `index`: Invalid output index supplied by the caller.
243    /// - `output_len`: Length of the output slice.
244    ///
245    /// # Returns
246    ///
247    /// Returns the hook-specific error representing `index > output_len`.
248    fn invalid_output_index(&mut self, codec: &C, index: usize, output_len: usize) -> Self::Error;
249
250    /// Finishes hook-owned state and writes any retained output.
251    ///
252    /// The default implementation is a no-op for stateless decode hooks.
253    /// Stateful hooks may emit final values such as checksums, reset markers, or
254    /// other trailer data. The caller must provide at least
255    /// [`BufferedDecodeHooks::max_finish_output_len`] writable slots from
256    /// `output_index`. Engines may pass an output slice whose upper bound is
257    /// capped at `output_index + max_finish_output_len`, so implementations must
258    /// not write beyond that declared final-output bound.
259    ///
260    /// # Parameters
261    ///
262    /// - `codec`: Low-level codec owned by the engine.
263    /// - `output`: Output value slice visible to the hook.
264    /// - `output_index`: Absolute output value index where writing starts.
265    ///
266    /// # Returns
267    ///
268    /// Returns the number of values written by finalization. This count must not
269    /// exceed [`BufferedDecodeHooks::max_finish_output_len`].
270    ///
271    /// # Errors
272    ///
273    /// Returns `Self::Error` when hook-owned state cannot be finalized.
274    #[inline]
275    fn finish(&mut self, _codec: &C, _output: &mut [C::Value], _output_index: usize) -> Result<usize, Self::Error> {
276        Ok(0)
277    }
278
279    /// Resets hook-owned policy state.
280    ///
281    /// # Parameters
282    ///
283    /// - `codec`: Low-level codec owned by the engine.
284    #[inline(always)]
285    fn reset(&mut self, _codec: &C) {}
286}