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}