Skip to main content

qubit_codec/transcode/engine/
transcode_encode_engine.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//! Reusable buffered encoder engine.
9
10use core::num::NonZeroUsize;
11
12use super::super::internal::encode_attempt::EncodeAttempt;
13use super::super::internal::encode_state::EncodeState;
14use super::super::internal::lifecycle_guard::LifecycleGuard;
15use super::EncodeOutcome;
16use super::EncodeUnencodableAction;
17use super::TranscodeEncodeHooks;
18use crate::CapacityError;
19use crate::Codec;
20use crate::TranscodeEncodeError;
21use crate::TranscodeEncodeErrorOf;
22use crate::TranscodeEncoder;
23use crate::TranscodeFailure;
24use crate::TranscodeProgress;
25use crate::Transcoder;
26use crate::codec::assert_unit_bounds;
27
28/// Reusable buffered encoding engine for codec-backed encoders.
29///
30/// The engine owns the low-level codec and hook object. It keeps the common
31/// buffered encoding loop private: input-index validation, output-capacity
32/// checks, input consumption, output progress, and [`crate::TranscodeStatus`]
33/// reporting.
34///
35/// Use this type to build a streaming encoder over a one-value [`Codec`]. The
36/// engine does not allocate output. It encodes values accepted by
37/// [`Codec::can_encode_value`] directly through the codec. When a value is
38/// outside the codec's encodable domain, the engine asks hooks whether to
39/// reject, skip, or replace it. If the current output buffer is too small for
40/// the selected value, the engine returns
41/// [`crate::TranscodeStatus::NeedOutput`] without consuming that input; the
42/// caller can provide a larger or fresh output buffer and resume with the
43/// returned input index.
44///
45/// For the common strict policy that simply wraps codec errors, use
46/// [`crate::CodecTranscodeEncoder`]. Use `TranscodeEncodeEngine` directly when
47/// the encode policy needs custom planning, replacement, skipped values, or
48/// finish-time output.
49///
50/// # Examples
51///
52/// ```rust
53/// use core::{
54///     convert::Infallible,
55///     num::NonZeroUsize,
56/// };
57/// use qubit_codec::{
58///     Codec,
59///     TranscodeEncodeErrorOf,
60///     DecodeFailure,
61///     TranscodeStatus,
62/// };
63/// use qubit_codec::engine::{
64///     EncodeContext,
65///     EncodeUnencodableAction,
66///     TranscodeEncodeEngine,
67///     TranscodeEncodeHooks,
68/// };
69///
70/// #[derive(Clone, Copy)]
71/// struct ByteCodec;
72///
73/// impl Codec for ByteCodec {
74///     type Value = u8;
75///     type Unit = u8;
76///     type DecodeError = Infallible;
77///     type EncodeError = Infallible;
78///
79///     const MIN_UNITS_PER_VALUE: usize = 1;
80///     const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
81///     const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
82///
83///     unsafe fn decode(
84///         &mut self,
85///         input: &[u8],
86///         index: usize,
87///     ) -> Result<(u8, NonZeroUsize), DecodeFailure<Self::DecodeError>> {
88///         Ok((input[index], NonZeroUsize::MIN))
89///     }
90///
91///     unsafe fn encode(
92///         &mut self,
93///         value: &u8,
94///         output: &mut [u8],
95///         index: usize,
96///     ) -> Result<usize, Self::EncodeError> {
97///         output[index] = *value;
98///         Ok(1)
99///     }
100/// }
101///
102/// struct StrictHooks;
103///
104/// impl TranscodeEncodeHooks<ByteCodec> for StrictHooks {
105///     fn handle_unencodable_encode(
106///         &mut self,
107///         _codec: &mut ByteCodec,
108///         _context: &EncodeContext<'_, u8>,
109///     ) -> Result<EncodeUnencodableAction<u8>, TranscodeEncodeErrorOf<ByteCodec>> {
110///         unreachable!("ByteCodec accepts every u8")
111///     }
112/// }
113///
114/// let mut engine = TranscodeEncodeEngine::new(ByteCodec, StrictHooks);
115/// let input = [1_u8, 2, 3];
116/// let mut output = [0_u8; 2];
117/// let mut reset_output = [];
118/// engine.reset(&mut reset_output, 0)?;
119///
120/// let progress = engine.transcode(&input, 0, &mut output, 0)?;
121/// match progress.status() {
122///     TranscodeStatus::Complete => unreachable!("output is intentionally short"),
123///     TranscodeStatus::NeedOutput { .. } => {
124///         assert_eq!(2, progress.written());
125///         assert_eq!([1, 2], output);
126///         // Write out `output[..progress.written()]`, then resume at
127///         // `progress.read()` with fresh output capacity.
128///     }
129///     TranscodeStatus::NeedInput { .. } => unreachable!("encoders do not read encoded input"),
130/// }
131/// # Ok::<(), TranscodeEncodeErrorOf<ByteCodec>>(())
132/// ```
133///
134/// # Type Parameters
135///
136/// - `C`: Low-level codec used by the engine.
137/// - `H`: Policy hook object used by the engine.
138#[derive(Debug)]
139pub struct TranscodeEncodeEngine<C, H> {
140    codec: C,
141    hooks: H,
142    /// Guard for the `reset → transcode* → finish` lifecycle in every build
143    /// profile.
144    lifecycle: LifecycleGuard,
145}
146
147impl<C, H> TranscodeEncodeEngine<C, H>
148where
149    C: Codec,
150    H: TranscodeEncodeHooks<C>,
151{
152    /// Creates a buffered encoder engine.
153    ///
154    /// # Parameters
155    ///
156    /// - `codec`: Low-level codec used for one-value encoding.
157    /// - `hooks`: Policy hooks used for planning and writing values.
158    ///
159    /// # Returns
160    ///
161    /// Returns a buffered encoder engine.
162    ///
163    /// # Compile-Time Checks
164    ///
165    /// Fails to compile when the supplied codec declares a zero decode unit
166    /// bound or when [`Codec::MIN_UNITS_PER_VALUE`] exceeds
167    /// [`Codec::MAX_DECODE_UNITS_PER_VALUE`]. The encode bound may be zero for
168    /// a fully buffered codec.
169    #[inline]
170    #[must_use]
171    pub fn new(codec: C, hooks: H) -> Self {
172        assert_unit_bounds::<C>();
173        Self {
174            codec,
175            hooks,
176            lifecycle: LifecycleGuard::new(),
177        }
178    }
179
180    /// Returns the wrapped low-level codec.
181    ///
182    /// # Returns
183    ///
184    /// Returns a shared reference to the codec owned by this engine.
185    #[inline(always)]
186    #[must_use]
187    pub const fn codec(&self) -> &C {
188        &self.codec
189    }
190
191    /// Returns the wrapped low-level codec mutably.
192    ///
193    /// # Returns
194    ///
195    /// Returns a mutable reference to the codec owned by this engine.
196    #[inline(always)]
197    #[must_use]
198    pub fn codec_mut(&mut self) -> &mut C {
199        &mut self.codec
200    }
201
202    /// Returns the encode hooks used by this engine.
203    ///
204    /// # Returns
205    ///
206    /// Returns a shared reference to the hook object owned by this engine.
207    #[inline(always)]
208    #[must_use]
209    pub const fn hooks(&self) -> &H {
210        &self.hooks
211    }
212
213    /// Returns the encode hooks mutably.
214    ///
215    /// # Returns
216    ///
217    /// Returns a mutable reference to the hook object owned by this engine.
218    #[inline(always)]
219    #[must_use]
220    pub fn hooks_mut(&mut self) -> &mut H {
221        &mut self.hooks
222    }
223
224    /// Consumes the engine and returns its codec and hooks.
225    ///
226    /// Any lifecycle state owned by the engine is discarded.
227    ///
228    /// # Returns
229    ///
230    /// Returns the wrapped codec followed by the encode hooks.
231    #[inline]
232    #[must_use]
233    pub fn into_parts(self) -> (C, H) {
234        let Self { codec, hooks, .. } = self;
235        (codec, hooks)
236    }
237
238    /// Gets a conservative upper bound for output units needed for
239    /// `input_len` values.
240    ///
241    /// This bound covers only the streaming encode phase. It is delegated to
242    /// [`TranscodeEncodeHooks::max_transcode_output_len`], so it includes hook
243    /// policy and is valid for every reachable transient codec and hook state.
244    /// Downstream encoders must use this engine-level API for capacity planning
245    /// instead of recomputing the bound from [`Codec`] constants.
246    ///
247    /// # Parameters
248    ///
249    /// - `input_len`: Number of input values the caller plans to encode.
250    ///
251    /// # Returns
252    ///
253    /// a conservative upper bound for output units, or a capacity error on
254    /// arithmetic overflow.
255    #[inline(always)]
256    #[must_use = "capacity planning can fail on overflow"]
257    pub fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
258        self.hooks.max_transcode_output_len(&self.codec, input_len)
259    }
260
261    /// Gets the global maximum output units emitted by stream reset.
262    ///
263    /// # Returns
264    ///
265    /// the codec's reset-output upper bound.
266    #[inline(always)]
267    #[must_use = "capacity planning can fail on overflow"]
268    pub fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
269        Ok(C::MAX_ENCODE_RESET_UNITS)
270    }
271
272    /// Gets the global maximum output units emitted by finishing codec and hook
273    /// state.
274    ///
275    /// Returns the sum of [`Codec::MAX_ENCODE_FINISH_UNITS`] and the
276    /// hook-provided final-output bound. The codec finish portion covers units
277    /// written by [`Codec::encode_finish`]; hook implementations must not
278    /// include that portion in
279    /// [`TranscodeEncodeHooks::max_finish_output_len`]. Both component bounds
280    /// cover every reachable transient state.
281    ///
282    /// # Returns
283    ///
284    /// the combined codec-finish and hook-finish output bound.
285    #[inline(always)]
286    #[must_use = "capacity planning can fail on overflow"]
287    pub fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
288        C::MAX_ENCODE_FINISH_UNITS
289            .checked_add(self.hooks.max_finish_output_len(&self.codec))
290            .ok_or(CapacityError::OutputLengthOverflow)
291    }
292
293    /// Gets the maximum output units needed by a complete one-shot encode
294    /// stream.
295    ///
296    /// The returned bound covers reset output, the streaming encode phase for
297    /// `input_len` values, and finish output. Its components are global across
298    /// transient state. Higher-level complete encode helpers should use this
299    /// engine-level bound instead of recomputing capacity from [`Codec`]
300    /// constants, because hook policy may change streaming or finish output.
301    ///
302    /// # Parameters
303    ///
304    /// - `input_len`: Number of input values in the complete stream.
305    ///
306    /// # Returns
307    ///
308    /// Returns the complete-stream output bound, or a capacity error on
309    /// arithmetic overflow.
310    #[inline(never)]
311    #[must_use = "capacity planning can fail on overflow"]
312    pub fn max_total_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
313        let reset = self.max_reset_output_len()?;
314        let transcode = self.max_transcode_output_len(input_len)?;
315        let finish = self.max_finish_output_len()?;
316        reset
317            .checked_add(transcode)
318            .and_then(|len| len.checked_add(finish))
319            .ok_or(CapacityError::OutputLengthOverflow)
320    }
321
322    /// Resets codec encode state, hook-owned state, and stream-start output.
323    ///
324    /// # Parameters
325    ///
326    /// - `output`: Complete output unit slice visible to the encoder.
327    /// - `output_index`: Absolute output unit index where writing starts.
328    ///
329    /// # Returns
330    ///
331    /// Returns the number of reset units written.
332    ///
333    /// # Errors
334    ///
335    /// Returns framework errors when the caller provides invalid or
336    /// insufficient output capacity. Returns domain errors when codec reset or
337    /// hook reset handling fails. Capacity and index failures occur before any
338    /// reset state is changed. Once reset execution starts, an error poisons
339    /// the engine until a later reset succeeds.
340    pub fn reset(&mut self, output: &mut [C::Unit], output_index: usize) -> Result<usize, TranscodeEncodeErrorOf<C>> {
341        let required = self.max_reset_output_len()?;
342        TranscodeFailure::ensure_output_capacity(output.len(), output_index, required)?;
343        self.lifecycle.on_reset_start();
344        self.hooks.reset_hooks(&mut self.codec);
345        let written = unsafe {
346            // SAFETY: The capacity check above reserves the codec's declared
347            // reset-output bound at `output_index`.
348            self.codec.encode_reset(output, output_index)
349        }
350        .map_err(TranscodeEncodeError::domain_reset)?;
351        assert!(written <= required, "Codec::encode_reset wrote beyond its reset bound",);
352        self.lifecycle.on_reset_success();
353        Ok(written)
354    }
355
356    /// Encodes values into a caller-provided output buffer.
357    ///
358    /// The engine stops before consuming the next input value when the current
359    /// output buffer does not satisfy that value's planned capacity bound.
360    ///
361    /// # Parameters
362    ///
363    /// - `input`: Complete input value slice visible to the encoder.
364    /// - `input_index`: Absolute input value index where encoding starts.
365    /// - `output`: Complete output unit slice visible to the encoder.
366    /// - `output_index`: Absolute output unit index where writing starts.
367    ///
368    /// # Returns
369    ///
370    /// Returns progress describing input values consumed, output units written,
371    /// and why encoding stopped.
372    ///
373    /// # Errors
374    ///
375    /// Returns hook errors when `input_index` is outside `input`, when
376    /// `output_index` is outside `output`, or when hook planning or writing
377    /// rejects a value. Returns
378    /// [`TranscodeFailure::TranscodeBeforeReset`] when the engine has not
379    /// completed its first reset. Returns
380    /// [`TranscodeFailure::TranscodeAfterFinish`] when the logical stream was
381    /// already finished and has not been reset, or
382    /// [`TranscodeFailure::LifecyclePoisoned`] when an earlier reset or finish
383    /// failed after execution started.
384    pub fn transcode(
385        &mut self,
386        input: &[C::Value],
387        input_index: usize,
388        output: &mut [C::Unit],
389        output_index: usize,
390    ) -> Result<TranscodeProgress, TranscodeEncodeErrorOf<C>> {
391        self.lifecycle.on_transcode()?;
392        TranscodeFailure::ensure_transcode_indices(input.len(), input_index, output.len(), output_index)?;
393        let mut state = EncodeState::new(input, input_index, output, output_index);
394
395        while state.has_input() {
396            // SAFETY: The loop condition proves that the current input cursor
397            // points at an available value.
398            let attempt = unsafe { state.context_unchecked() };
399            let outcome = self.encode_one(attempt)?;
400            if let Some(progress) = state.apply_encode_outcome(outcome) {
401                return Ok(progress);
402            }
403        }
404
405        Ok(state.complete_progress())
406    }
407
408    /// Finishes codec and hook-owned output after EOF.
409    ///
410    /// Finalization first finishes encode-side codec state through
411    /// [`Codec::encode_finish`], then lets hook implementations finish their
412    /// own retained state. The caller must provide enough output capacity for
413    /// [`TranscodeEncodeEngine::max_finish_output_len`], which includes both
414    /// the codec finish bound and the hook-owned finish bound.
415    ///
416    /// # Parameters
417    ///
418    /// - `output`: Complete output unit slice visible to the encoder.
419    /// - `output_index`: Absolute output unit index where writing starts.
420    ///
421    /// # Returns
422    ///
423    /// Returns the number of units written by finalization.
424    ///
425    /// # Errors
426    ///
427    /// Returns framework errors when the caller provides invalid or
428    /// insufficient output capacity. Returns domain errors when codec finish or
429    /// hook finalization fails. Returns
430    /// [`TranscodeFailure::FinishBeforeReset`] when the engine has not
431    /// completed its first reset. Returns
432    /// [`TranscodeFailure::FinishAfterFinish`] when the logical stream was
433    /// already finished and has not been reset, or
434    /// [`TranscodeFailure::LifecyclePoisoned`] when an earlier reset or finish
435    /// failed after execution started. Capacity and index failures occur before
436    /// finish execution and remain retryable; later failures poison the engine
437    /// until reset succeeds.
438    ///
439    /// # Panics
440    ///
441    /// Panics when the codec finish writes beyond
442    /// [`Codec::MAX_ENCODE_FINISH_UNITS`] or when the combined codec and hook
443    /// finalization writes beyond
444    /// [`TranscodeEncodeEngine::max_finish_output_len`].
445    pub fn finish(&mut self, output: &mut [C::Unit], output_index: usize) -> Result<usize, TranscodeEncodeErrorOf<C>> {
446        self.lifecycle.on_finish_attempt()?;
447        let required = self.max_finish_output_len()?;
448        TranscodeFailure::ensure_output_capacity(output.len(), output_index, required)?;
449        self.lifecycle.on_finish_start();
450        let finished = unsafe {
451            // SAFETY: The capacity check above reserves the codec's declared
452            // finish-output bound at `output_index`.
453            self.codec.encode_finish(output, output_index)
454        }
455        .map_err(TranscodeEncodeError::domain_finish)?;
456        assert!(
457            finished <= C::MAX_ENCODE_FINISH_UNITS,
458            "Codec::encode_finish wrote beyond its finish bound",
459        );
460        let written = self
461            .hooks
462            .finish_hooks(&mut self.codec, output, output_index + finished)?;
463        assert!(
464            finished + written <= required,
465            "TranscodeEncodeEngine hook wrote beyond its finish bound",
466        );
467        self.lifecycle.on_finish_success();
468        Ok(finished + written)
469    }
470
471    /// Runs a complete one-shot `reset -> transcode -> finish` encode stream.
472    ///
473    /// The complete input is supplied as `input`, and output starts at index
474    /// `0` in `output`. Callers that need subranges should slice their
475    /// buffers before calling this method. Downstream one-shot encoder
476    /// helpers should call this engine method instead of reproducing the
477    /// reset, transcode, and finish sequence themselves.
478    ///
479    /// # Parameters
480    ///
481    /// - `input`: Complete input values.
482    /// - `output`: Output unit slice for the whole encoded stream.
483    ///
484    /// # Returns
485    ///
486    /// Returns the number of output units written.
487    ///
488    /// # Errors
489    ///
490    /// Returns framework errors for insufficient output or capacity overflow,
491    /// and domain errors from reset, encode, or finish.
492    #[inline]
493    pub fn transcode_complete_into(
494        &mut self,
495        input: &[C::Value],
496        output: &mut [C::Unit],
497    ) -> Result<usize, TranscodeEncodeErrorOf<C>> {
498        <Self as Transcoder>::transcode_complete_into(self, input, output)
499    }
500
501    /// Encodes one value through the codec and unencodable-value hooks.
502    ///
503    /// This is the single entry point for `TranscodeConvertEngine` to drive
504    /// the encode side without accessing `codec` and `hooks` directly.
505    ///
506    /// # Parameters
507    ///
508    /// - `context`: Encode context for the current value.
509    ///
510    /// # Returns
511    ///
512    /// Returns whether the value was consumed or needs more output capacity.
513    ///
514    /// # Errors
515    ///
516    /// Returns an engine-domain error when the codec fails or when the hook
517    /// rejects an unencodable value.
518    pub(in crate::transcode) fn encode_one(
519        &mut self,
520        attempt: EncodeAttempt<'_, C::Value, C::Unit>,
521    ) -> Result<EncodeOutcome, TranscodeEncodeErrorOf<C>> {
522        let encodable = {
523            let context = attempt.context();
524            self.codec.can_encode_value(context.input_value())
525        };
526        if encodable {
527            return self.encode_encodable_value(attempt);
528        }
529        let action = {
530            let context = attempt.context();
531            self.hooks.handle_unencodable_encode(&mut self.codec, &context)?
532        };
533        self.apply_unencodable_action(action, attempt)
534    }
535
536    /// Encodes an encodable input value.
537    ///
538    /// # Parameters
539    ///
540    /// - `context`: Encode context for the current value.
541    ///
542    /// # Returns
543    ///
544    /// Returns the encode outcome for the current value.
545    ///
546    /// # Errors
547    ///
548    /// Returns a codec error when encoding fails.
549    ///
550    /// # Panics
551    ///
552    /// Panics when [`Codec::encode_len`] exceeds
553    /// [`Codec::MAX_ENCODE_UNITS_PER_VALUE`] or [`Codec::encode`] writes a
554    /// different number of units than planned.
555    fn encode_encodable_value(
556        &mut self,
557        attempt: EncodeAttempt<'_, C::Value, C::Unit>,
558    ) -> Result<EncodeOutcome, TranscodeEncodeErrorOf<C>> {
559        let required = self.codec.encode_len(attempt.value());
560        assert!(
561            required <= C::MAX_ENCODE_UNITS_PER_VALUE,
562            "Codec::encode_len exceeded Codec::MAX_ENCODE_UNITS_PER_VALUE",
563        );
564        if attempt.available_output() < required {
565            return Ok(EncodeOutcome::need_output(
566                NonZeroUsize::new(required).expect("required output is non-zero when capacity is insufficient"),
567            ));
568        }
569        let (value, input_index, output, output_index) = attempt.into_parts();
570        let written = unsafe {
571            // SAFETY: The capacity check above reserves the exact value width.
572            self.codec.encode(value, output, output_index)
573        }
574        .map_err(|error| TranscodeEncodeError::domain_main(error, input_index))?;
575        assert!(
576            written == required,
577            "Codec::encode wrote a different length than Codec::encode_len",
578        );
579        Ok(EncodeOutcome::consumed(written))
580    }
581
582    /// Applies the hook-selected unencodable-value action.
583    ///
584    /// # Parameters
585    ///
586    /// - `action`: Policy action selected by the encode hooks.
587    /// - `context`: Encode context for the rejected input value.
588    ///
589    /// # Returns
590    ///
591    /// Returns the encode outcome for the current input value.
592    ///
593    /// # Errors
594    ///
595    /// Returns a codec error when replacement encoding fails.
596    #[inline(always)]
597    fn apply_unencodable_action(
598        &mut self,
599        action: EncodeUnencodableAction<C::Value>,
600        attempt: EncodeAttempt<'_, C::Value, C::Unit>,
601    ) -> Result<EncodeOutcome, TranscodeEncodeErrorOf<C>> {
602        match action {
603            EncodeUnencodableAction::Reject => {
604                // Keep the streaming encoder usable for non-Clone values.
605                // The failure still carries the absolute input index; callers
606                // that need value context can add it in a higher-level facade.
607                Err(TranscodeEncodeError::unencodable_without_context(attempt.input_index()))
608            }
609            EncodeUnencodableAction::Skip => Ok(EncodeOutcome::consumed(0)),
610            EncodeUnencodableAction::Replace { value } => self.encode_replacement_value(value, attempt),
611        }
612    }
613
614    /// Encodes a hook-provided replacement value.
615    ///
616    /// # Parameters
617    ///
618    /// - `value`: Replacement value selected by hooks.
619    /// - `context`: Encode context for the original unencodable input value.
620    ///
621    /// # Returns
622    ///
623    /// Returns the encode outcome for the replacement.
624    ///
625    /// # Errors
626    ///
627    /// Returns a codec error when replacement encoding fails.
628    ///
629    /// # Panics
630    ///
631    /// Panics when hooks return a replacement value that the codec cannot
632    /// encode, [`Codec::encode_len`] exceeds
633    /// [`Codec::MAX_ENCODE_UNITS_PER_VALUE`], or [`Codec::encode`] writes a
634    /// different number of units than planned.
635    fn encode_replacement_value(
636        &mut self,
637        value: C::Value,
638        attempt: EncodeAttempt<'_, C::Value, C::Unit>,
639    ) -> Result<EncodeOutcome, TranscodeEncodeErrorOf<C>> {
640        assert!(
641            self.codec.can_encode_value(&value),
642            "EncodeUnencodableAction::Replace returned an unencodable replacement value",
643        );
644        let required = self.codec.encode_len(&value);
645        assert!(
646            required <= C::MAX_ENCODE_UNITS_PER_VALUE,
647            "Codec::encode_len exceeded Codec::MAX_ENCODE_UNITS_PER_VALUE",
648        );
649        if attempt.available_output() < required {
650            return Ok(EncodeOutcome::need_output(
651                NonZeroUsize::new(required).expect("required output is non-zero when capacity is insufficient"),
652            ));
653        }
654        let (_, input_index, output, output_index) = attempt.into_parts();
655        let written = unsafe {
656            // SAFETY: The capacity check above reserves the exact replacement
657            // value width, and the hook contract requires encodability.
658            self.codec.encode(&value, output, output_index)
659        }
660        .map_err(|error| TranscodeEncodeError::domain_main(error, input_index))?;
661        assert!(
662            written == required,
663            "Codec::encode wrote a different length than Codec::encode_len",
664        );
665        Ok(EncodeOutcome::consumed(written))
666    }
667}
668
669impl<C, H> Default for TranscodeEncodeEngine<C, H>
670where
671    C: Codec + Default,
672    H: TranscodeEncodeHooks<C> + Default,
673{
674    /// Creates a default buffered encoder engine.
675    ///
676    /// # Returns
677    ///
678    /// Returns an engine with default codec and hooks.
679    #[inline(always)]
680    fn default() -> Self {
681        Self::new(C::default(), H::default())
682    }
683}
684
685impl<C, H> Transcoder for TranscodeEncodeEngine<C, H>
686where
687    C: Codec,
688    H: TranscodeEncodeHooks<C>,
689{
690    type Input = C::Value;
691    type Output = C::Unit;
692    type Error = TranscodeEncodeErrorOf<C>;
693
694    /// Returns an upper bound for units produced from `input_len` values.
695    #[inline(always)]
696    fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
697        TranscodeEncodeEngine::max_transcode_output_len(self, input_len)
698    }
699
700    /// Returns the maximum units emitted when resetting stream state.
701    #[inline(always)]
702    fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
703        TranscodeEncodeEngine::max_reset_output_len(self)
704    }
705
706    /// Returns the maximum units emitted by finishing internal state.
707    #[inline(always)]
708    fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
709        TranscodeEncodeEngine::max_finish_output_len(self)
710    }
711
712    /// Resets codec encode state, hook-owned state, and stream-start output.
713    #[inline(always)]
714    fn reset(&mut self, output: &mut [C::Unit], output_index: usize) -> Result<usize, TranscodeEncodeErrorOf<C>> {
715        TranscodeEncodeEngine::reset(self, output, output_index)
716    }
717
718    /// Encodes input values into caller-provided output units.
719    #[inline(always)]
720    fn transcode(
721        &mut self,
722        input: &[C::Value],
723        input_index: usize,
724        output: &mut [C::Unit],
725        output_index: usize,
726    ) -> Result<TranscodeProgress, TranscodeEncodeErrorOf<C>> {
727        TranscodeEncodeEngine::transcode(self, input, input_index, output, output_index)
728    }
729
730    /// Finishes hook-owned encoder state.
731    #[inline(always)]
732    fn finish(&mut self, output: &mut [C::Unit], output_index: usize) -> Result<usize, TranscodeEncodeErrorOf<C>> {
733        TranscodeEncodeEngine::finish(self, output, output_index)
734    }
735}
736
737impl<C, H> TranscodeEncoder for TranscodeEncodeEngine<C, H>
738where
739    C: Codec,
740    H: TranscodeEncodeHooks<C>,
741{
742    type EncodeError = C::EncodeError;
743}