Skip to main content

qubit_codec/buffered/
buffered_encode_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 encoder engines.
11
12use super::{
13    encode_context::EncodeContext,
14    encode_plan::EncodePlan,
15};
16use crate::{
17    CapacityError,
18    Codec,
19};
20
21/// Policy hooks for [`crate::BufferedEncodeEngine`].
22///
23/// Hooks own policy state, such as replacement or ignore behavior, but not the
24/// codec or engine cursor state. The engine passes the codec into hook methods
25/// when policy code needs codec metadata or one-value encode operations.
26///
27/// Implement this trait when a buffered encoder needs policy decisions around
28/// individual values while reusing the common engine loop. Examples include
29/// rejecting unsupported values with adapter-level context, consuming values
30/// without writing output, writing replacement units, or emitting final state in
31/// [`finish`](Self::finish).
32///
33/// The engine calls [`prepare_encode`](Self::prepare_encode) before each value
34/// is consumed. The returned [`EncodePlan`] states the required output capacity
35/// and may carry an action computed by the hook. Only after that capacity is
36/// available does the engine call [`write_encode`](Self::write_encode) with the
37/// same cursor context and the prepared plan. This split lets the engine stop
38/// with [`crate::TranscodeStatus::NeedOutput`]
39/// without consuming the next input value.
40///
41/// # Example
42///
43/// This hook writes each value with the wrapped codec and uses the codec's
44/// maximum width as the capacity plan.
45///
46/// ```rust
47/// use core::{
48///     convert::Infallible,
49///     num::NonZeroUsize,
50/// };
51/// use qubit_codec::{
52///     BufferedEncodeHooks,
53///     Codec,
54///     CodecEncodeError,
55///     EncodeContext,
56///     EncodePlan,
57/// };
58///
59/// #[derive(Clone, Copy)]
60/// struct ByteCodec;
61///
62/// unsafe impl Codec for ByteCodec {
63///     type Value = u8;
64///     type Unit = u8;
65///     type DecodeError = Infallible;
66///     type EncodeError = 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///         Ok((input[index], NonZeroUsize::MIN))
82///     }
83///
84///     unsafe fn encode_unchecked(
85///         &self,
86///         value: &u8,
87///         output: &mut [u8],
88///         index: usize,
89///     ) -> Result<usize, Self::EncodeError> {
90///         output[index] = *value;
91///         Ok(1)
92///     }
93/// }
94///
95/// struct StrictHooks;
96///
97/// impl<C> BufferedEncodeHooks<C> for StrictHooks
98/// where
99///     C: Codec,
100/// {
101///     type Error = CodecEncodeError<C::EncodeError>;
102///     type PlanAction = ();
103///
104///     fn prepare_encode(
105///         &mut self,
106///         codec: &C,
107///         _value: &C::Value,
108///         _input_index: usize,
109///     ) -> Result<EncodePlan<()>, Self::Error> {
110///         Ok(EncodePlan::new(codec.max_units_per_value().get(), ()))
111///     }
112///
113///     unsafe fn write_encode(
114///         &mut self,
115///         codec: &C,
116///         context: EncodeContext<'_, C::Value, C::Unit>,
117///         _plan: EncodePlan<()>,
118///     ) -> Result<usize, Self::Error> {
119///         unsafe {
120///             codec.encode_unchecked(context.input_value, context.output, context.output_index)
121///         }
122///         .map_err(|error| CodecEncodeError::encode(error, context.input_index))
123///     }
124///
125///     fn invalid_input_index(
126///         &mut self,
127///         _codec: &C,
128///         index: usize,
129///         input_len: usize,
130///     ) -> Self::Error {
131///         CodecEncodeError::invalid_input_index(index, input_len)
132///     }
133///
134///     fn invalid_output_index(
135///         &mut self,
136///         _codec: &C,
137///         index: usize,
138///         output_len: usize,
139///     ) -> Self::Error {
140///         CodecEncodeError::invalid_output_index(index, output_len)
141///     }
142/// }
143/// ```
144///
145/// # Type Parameters
146///
147/// - `C`: Low-level codec owned by the engine.
148pub trait BufferedEncodeHooks<C>
149where
150    C: Codec,
151{
152    /// Error type returned by the buffered encoder.
153    type Error;
154
155    /// Concrete action stored in [`EncodePlan::action`].
156    type PlanAction;
157
158    /// Returns the maximum output units needed for `input_len` values.
159    ///
160    /// # Parameters
161    ///
162    /// - `codec`: Low-level codec owned by the engine.
163    /// - `input_len`: Number of input values the caller plans to encode.
164    ///
165    /// # Returns
166    ///
167    /// Returns a conservative upper bound derived from the codec's
168    /// [`Codec::max_units_per_value`].
169    #[must_use = "capacity planning can fail on overflow"]
170    #[inline]
171    fn max_output_len(&self, codec: &C, input_len: usize) -> Result<usize, CapacityError> {
172        input_len
173            .checked_mul(codec.max_units_per_value().get())
174            .ok_or(CapacityError::OutputLengthOverflow)
175    }
176
177    /// Returns an upper bound for units emitted by finishing hook-owned state.
178    ///
179    /// `finish` never receives more input values. Implementations must only
180    /// report output derived from hook-owned state that remains after the caller
181    /// has supplied all input.
182    ///
183    /// # Parameters
184    ///
185    /// - `codec`: Low-level codec owned by the engine.
186    ///
187    /// # Returns
188    ///
189    /// Returns the finite final-output upper bound.
190    #[must_use]
191    #[inline(always)]
192    fn max_finish_output_len(&self, _codec: &C) -> usize {
193        0
194    }
195
196    /// Prepares an encoding plan for one input value.
197    ///
198    /// This method must not write output. It decides the output capacity bound
199    /// needed before [`write_encode`](Self::write_encode) may be called and
200    /// returns an implementation-specific plan action.
201    ///
202    /// # Parameters
203    ///
204    /// - `codec`: Low-level codec owned by the engine.
205    /// - `input_value`: Input value being encoded.
206    /// - `input_index`: Absolute input index of `value`.
207    ///
208    /// # Returns
209    ///
210    /// Returns the write plan for `value`.
211    ///
212    /// # Errors
213    ///
214    /// Returns `Self::Error` when this value cannot be encoded under the hook
215    /// policy.
216    fn prepare_encode(
217        &mut self,
218        codec: &C,
219        input_value: &C::Value,
220        input_index: usize,
221    ) -> Result<EncodePlan<Self::PlanAction>, Self::Error>;
222
223    /// Writes one input value according to a previously prepared plan.
224    ///
225    /// This method is called only after the engine has verified that
226    /// [`EncodePlan::max_output_units`] units from `plan` are writable from
227    /// [`EncodeContext::output_index`]. Implementations may rely on that
228    /// capacity guarantee and do not need to report output starvation here. If
229    /// a value needs more output than the plan declared, fix
230    /// [`prepare_encode`](Self::prepare_encode) to return a larger bound.
231    ///
232    /// # Parameters
233    ///
234    /// - `codec`: Low-level codec owned by the engine.
235    /// - `context`: Encode-write context containing the input value, input
236    ///   index, output slice, and output cursor.
237    /// - `plan`: Prepared plan returned by [`prepare_encode`](Self::prepare_encode).
238    ///
239    /// # Returns
240    ///
241    /// Returns the number of output units written.
242    ///
243    /// # Errors
244    ///
245    /// Returns `Self::Error` when writing fails under the hook policy. Output
246    /// capacity exhaustion is handled before this method is called and should
247    /// not be reported as a write error.
248    ///
249    /// # Safety
250    ///
251    /// The caller must guarantee that at least the corresponding
252    /// [`EncodePlan::max_output_units`] units are writable from
253    /// [`EncodeContext::output_index`] in [`EncodeContext::output`].
254    unsafe fn write_encode(
255        &mut self,
256        codec: &C,
257        context: EncodeContext<'_, C::Value, C::Unit>,
258        plan: EncodePlan<Self::PlanAction>,
259    ) -> Result<usize, Self::Error>;
260
261    /// Builds an error for a caller-supplied input index outside the input slice.
262    ///
263    /// The engine calls this hook before it reads input. Keeping this
264    /// construction in the hook lets codec-backed adapters preserve their own
265    /// concrete error type without a separate public factory trait.
266    ///
267    /// # Parameters
268    ///
269    /// - `codec`: Low-level codec owned by the engine.
270    /// - `index`: Invalid absolute input index supplied by the caller.
271    /// - `input_len`: Length of the input slice.
272    ///
273    /// # Returns
274    ///
275    /// Returns the hook-specific invalid-input-index error.
276    fn invalid_input_index(&mut self, codec: &C, index: usize, input_len: usize) -> Self::Error;
277
278    /// Builds an error for a caller-supplied output index outside the output slice.
279    ///
280    /// The engine calls this hook before it writes output. Keeping this
281    /// construction in the hook lets codec-backed adapters preserve their own
282    /// concrete error type without a separate public factory trait.
283    ///
284    /// # Parameters
285    ///
286    /// - `codec`: Low-level codec owned by the engine.
287    /// - `index`: Invalid absolute output index supplied by the caller.
288    /// - `output_len`: Length of the output slice.
289    ///
290    /// # Returns
291    ///
292    /// Returns the hook-specific invalid-output-index error.
293    fn invalid_output_index(&mut self, codec: &C, index: usize, output_len: usize) -> Self::Error;
294
295    /// Finishes hook-owned state and writes any retained output units.
296    ///
297    /// The default implementation is a no-op for stateless encode hooks.
298    /// Stateful hooks may emit final units such as reset sequences, checksums, or
299    /// trailers. The caller must provide at least
300    /// [`BufferedEncodeHooks::max_finish_output_len`] writable units from
301    /// `output_index`. Engines may pass an output slice whose upper bound is
302    /// capped at `output_index + max_finish_output_len`, so implementations must
303    /// not write beyond that declared final-output bound.
304    ///
305    /// # Parameters
306    ///
307    /// - `codec`: Low-level codec owned by the engine.
308    /// - `output`: Output unit slice visible to the hook.
309    /// - `output_index`: Absolute output unit index where writing starts.
310    ///
311    /// # Returns
312    ///
313    /// Returns the number of units written by finalization. This count must not
314    /// exceed [`BufferedEncodeHooks::max_finish_output_len`].
315    ///
316    /// # Errors
317    ///
318    /// Returns `Self::Error` when hook-owned state cannot be finalized.
319    #[inline]
320    fn finish(&mut self, _codec: &C, _output: &mut [C::Unit], _output_index: usize) -> Result<usize, Self::Error> {
321        Ok(0)
322    }
323
324    /// Resets hook-owned policy state.
325    ///
326    /// # Parameters
327    ///
328    /// - `codec`: Low-level codec owned by the engine.
329    #[inline(always)]
330    fn reset(&mut self, _codec: &C) {}
331}