Skip to main content

qubit_codec/transcode/io/
transcode_encode_output.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//! Buffered output driver that encodes values into units.
9
10use core::fmt;
11use core::num::NonZeroUsize;
12use core::result::Result as CoreResult;
13use std::collections::TryReserveError;
14use std::io::Error;
15use std::io::ErrorKind;
16use std::io::Result;
17use std::io::Seek;
18use std::io::SeekFrom;
19use std::io::Write;
20
21use qubit_io::Buffer;
22use qubit_io::BufferedOutput;
23use qubit_io::Output;
24use qubit_io::Seekable;
25use qubit_utils::SliceRange;
26use qubit_utils::allocation_error;
27
28use super::transcode_progress_validation::validate_encode_progress;
29use crate::CapacityError;
30use crate::Codec;
31use crate::TranscodeEncodeError;
32use crate::TranscodeEncodeErrorOf;
33use crate::TranscodeEncoder;
34use crate::TranscodeStatus;
35use crate::Transcoder;
36use crate::value::codec_value_lifecycle::encode_complete_value_into_reserved;
37use crate::value::codec_value_lifecycle::max_complete_encode_units;
38
39/// Encodes an [`Output`] value stream into an [`Output`] unit stream.
40///
41/// This type owns only the unit-level [`qubit_io::BufferedOutput`]. Callers
42/// pass a [`crate::Codec`] and error mapper to each encode operation, which
43/// lets one buffered output drive different encoders without nesting buffers or
44/// storing codec-specific state in the buffer owner.
45///
46/// [`Self::flush`] only drains already buffered units. State-aware streaming
47/// encoders can use [`Self::reset`], [`Self::transcode`], and [`Self::finish`]
48/// explicitly.
49///
50/// # Type Parameters
51///
52/// * `O` - Wrapped unit output.
53///
54/// # Examples
55///
56/// ```
57/// use std::io::Cursor;
58///
59/// use qubit_codec::TranscodeEncodeOutput;
60///
61/// let output = TranscodeEncodeOutput::with_capacity(Cursor::new(Vec::<u8>::new()), 8);
62/// assert!(output.spare_capacity() >= 8);
63/// ```
64pub struct TranscodeEncodeOutput<O>
65where
66    O: Output,
67    O::Item: Copy + Default,
68{
69    /// Owns the wrapped unit sink and pending encoded units awaiting delivery.
70    output: BufferedOutput<O>,
71}
72
73impl<O> TranscodeEncodeOutput<O>
74where
75    O: Output,
76    O::Item: Copy + Default,
77{
78    /// Creates an encoder output with the default unit buffer capacity.
79    ///
80    /// # Parameters
81    ///
82    /// * `inner` - Unit output written by this adapter.
83    ///
84    /// # Returns
85    ///
86    /// A new buffered encoder output.
87    #[must_use]
88    pub fn new(inner: O) -> Self {
89        Self {
90            output: BufferedOutput::new(inner),
91        }
92    }
93
94    /// Creates an encoder output with a unit buffer of at least `capacity`.
95    ///
96    /// # Parameters
97    ///
98    /// * `inner` - Unit output written by this adapter.
99    /// * `capacity` - Requested internal unit buffer capacity.
100    ///
101    /// # Returns
102    ///
103    /// A new buffered encoder output.
104    #[must_use]
105    pub fn with_capacity(inner: O, capacity: usize) -> Self {
106        Self {
107            output: BufferedOutput::with_capacity(inner, capacity),
108        }
109    }
110
111    /// Creates an encoder output with a unit buffer of at least `capacity`.
112    ///
113    /// # Parameters
114    ///
115    /// * `inner` - Unit output written by this adapter.
116    /// * `capacity` - Requested internal unit buffer capacity.
117    ///
118    /// # Errors
119    ///
120    /// Returns an allocation error when the requested buffer cannot be
121    /// allocated.
122    ///
123    /// # Returns
124    ///
125    /// A buffered adapter owning `inner` on success.
126    pub fn try_with_capacity(inner: O, capacity: usize) -> CoreResult<Self, TryReserveError> {
127        Ok(Self {
128            output: BufferedOutput::try_with_capacity(inner, capacity)?,
129        })
130    }
131
132    /// Returns a shared reference to the wrapped unit output.
133    ///
134    /// Pending units remain in this adapter's internal buffer and are not
135    /// visible through the returned output until [`Self::flush`] succeeds.
136    ///
137    /// # Returns
138    ///
139    /// A shared reference to the wrapped unit output.
140    #[must_use]
141    #[inline]
142    pub const fn inner(&self) -> &O {
143        self.output.inner()
144    }
145
146    /// Returns the available capacity of the spare output buffer.
147    ///
148    /// # Returns
149    ///
150    /// The number of output units that can still be appended without flushing.
151    #[must_use]
152    #[inline]
153    pub fn spare_capacity(&self) -> usize {
154        self.output.spare_capacity()
155    }
156
157    /// Returns raw spare-buffer parts for the internal output buffer.
158    ///
159    /// # Returns
160    ///
161    /// The full backing storage, the spare start index, and the spare unit
162    /// count.
163    #[must_use]
164    #[inline]
165    pub fn spare_raw_parts_mut(&mut self) -> (&mut [O::Item], usize, usize) {
166        self.output.spare_raw_parts_mut()
167    }
168
169    /// Marks `count` units from [`Self::spare_raw_parts_mut`] as written.
170    ///
171    /// # Parameters
172    ///
173    /// * `count` - Initialized units to commit from the spare window.
174    ///
175    /// # Safety
176    ///
177    /// The caller must guarantee that `count <= Self::spare_capacity()` and
178    /// that the corresponding units in the returned spare slice have been
179    /// initialized.
180    #[inline]
181    pub unsafe fn advance(&mut self, count: usize) {
182        // SAFETY: The caller guarantees `count` and initialization invariants.
183        unsafe { self.output.advance(count) }
184    }
185
186    /// Ensures that at least `count` spare units are available.
187    ///
188    /// # Parameters
189    ///
190    /// * `count` - Number of spare units required.
191    ///
192    /// # Errors
193    ///
194    /// Returns allocation errors mapped to [`ErrorKind::OutOfMemory`], or I/O
195    /// errors from the wrapped output while flushing pending units.
196    pub fn ensure_spare_capacity(&mut self, count: usize) -> Result<()> {
197        let pending = self.output.capacity() - self.output.spare_capacity();
198        let required_capacity = pending.saturating_add(count);
199        self.output
200            .try_reserve_capacity(required_capacity)
201            .map_err(allocation_error)?;
202        self.output.ensure_spare_capacity(count)
203    }
204
205    /// Consumes this adapter without flushing the wrapped output.
206    ///
207    /// This method does not call [`Self::flush`] and performs no I/O. Pending
208    /// units remain in the returned buffer, which transfers their delivery
209    /// responsibility to the caller.
210    ///
211    /// # Returns
212    ///
213    /// The wrapped output and the buffer holding pending units.
214    #[must_use = "the returned output and pending buffer must be handled"]
215    #[inline]
216    pub fn into_parts(self) -> (O, Buffer<O::Item>) {
217        self.output.into_parts()
218    }
219
220    /// Flushes buffered units without finishing any encoder stream.
221    ///
222    /// # Errors
223    ///
224    /// Returns errors from the wrapped output while flushing pending units.
225    pub fn flush(&mut self) -> Result<()> {
226        self.output.flush()
227    }
228
229    /// Encodes one codec value into this buffered unit output.
230    ///
231    /// The method grows the persistent internal buffer when necessary, then
232    /// writes the complete encoded value into its spare window.
233    ///
234    /// # Type Parameters
235    ///
236    /// * `C` - Codec producing the wrapped output unit type.
237    /// * `M` - Mapper from codec errors into I/O errors.
238    ///
239    /// # Parameters
240    ///
241    /// * `codec` - Codec used for this single-value encode.
242    /// * `value` - Value to encode.
243    /// * `map_error` - Mapper for codec-domain encode errors.
244    ///
245    /// # Errors
246    ///
247    /// Returns I/O errors from the wrapped output, `InvalidInput` when the
248    /// codec output bound overflows or the value is outside the codec domain,
249    /// or the error returned by `map_error` for codec encode, reset, and finish
250    /// failures.
251    ///
252    /// # Panics
253    ///
254    /// Panics when the codec violates its declared reset, value-width, or
255    /// finish bounds, or when `encode` writes a length different from
256    /// `encode_len` in the same reset state.
257    pub fn write_encoded_with<C, M>(&mut self, codec: &mut C, value: &C::Value, mut map_error: M) -> Result<()>
258    where
259        C: Codec<Unit = O::Item>,
260        M: FnMut(C::EncodeError) -> Error,
261    {
262        let max_units = map_encode_value_result(
263            max_complete_encode_units::<C>().map_err(TranscodeEncodeErrorOf::<C>::from),
264            &mut map_error,
265        )?;
266        self.ensure_spare_capacity(max_units)?;
267        let (units, output_index, available) = self.output.spare_raw_parts_mut();
268        debug_assert!(
269            available >= max_units,
270            "reserved spare buffer is smaller than codec upper bound",
271        );
272        let written = map_encode_value_result(
273            encode_complete_value_into_reserved(codec, value, units, output_index, max_units),
274            &mut map_error,
275        )?;
276        // SAFETY: The spare buffer has the conservative complete lifecycle
277        // capacity, and the helper reports how many initialized units it
278        // actually wrote.
279        unsafe {
280            self.output.advance(written);
281        }
282        Ok(())
283    }
284
285    /// Runs encoder reset and buffers any stream-prefix units.
286    ///
287    /// This method reserves enough persistent buffer capacity for all reset
288    /// output before calling `encoder`. It does not flush pending units.
289    ///
290    /// # Type Parameters
291    ///
292    /// * `E` - Transcoder producing the wrapped output unit type.
293    /// * `M` - Mapper from transcoder errors into I/O errors.
294    /// * `Value` - Source value type accepted by the transcoder.
295    ///
296    /// # Parameters
297    ///
298    /// * `encoder` - Transcoder whose stream prefix is being collected.
299    /// * `map_error` - Mapper for reset failures.
300    ///
301    /// # Errors
302    ///
303    /// Returns capacity errors, allocation errors, or reset errors mapped by
304    /// `map_error`.
305    ///
306    /// # Panics
307    ///
308    /// Panics when `encoder` writes more than its declared reset bound.
309    pub fn reset<E, M, Value>(&mut self, encoder: &mut E, map_error: &mut M) -> Result<()>
310    where
311        E: Transcoder<Input = Value, Output = O::Item>,
312        M: FnMut(E::Error) -> Error,
313    {
314        let required = encoder.max_reset_output_len().map_err(capacity_error_to_invalid_data)?;
315        self.ensure_spare_capacity(required)?;
316        let (units, output_index, available) = self.output.spare_raw_parts_mut();
317        debug_assert!(
318            available >= required,
319            "insufficient reset capacity reserved in spare output buffer",
320        );
321        let written = encoder.reset(units, output_index).map_err(&mut *map_error)?;
322        assert!(written <= required, "reset wrote beyond its bound");
323        // SAFETY: The encoder reported initialized units within the spare
324        // range reserved above.
325        unsafe {
326            self.output.advance(written);
327        }
328        Ok(())
329    }
330
331    /// Encodes values from an indexed input range using a streaming
332    /// [`Transcoder`].
333    ///
334    /// This streaming path writes directly into the internal spare buffer and
335    /// grows it for any larger `NeedOutput.required` value reported by
336    /// `encoder`.
337    ///
338    /// # Type Parameters
339    ///
340    /// * `E` - Encoder producing the wrapped output unit type.
341    /// * `M` - Mapper from encoder errors into I/O errors.
342    /// * `Value` - Source value type accepted by the encoder.
343    ///
344    /// # Parameters
345    ///
346    /// * `encoder` - Streaming encoder used for this operation.
347    /// * `map_error` - Function mapping transcode errors into I/O errors.
348    /// * `input` - Source values.
349    /// * `input_index` - Start index inside `input`.
350    /// * `count` - Maximum number of values to encode.
351    ///
352    /// # Returns
353    ///
354    /// The number of source values consumed.
355    ///
356    /// # Errors
357    ///
358    /// Returns invalid input ranges, capacity, transcode, or output errors.
359    ///
360    /// # Examples
361    ///
362    /// A value that only implements [`Transcoder`] is not an encoder and cannot
363    /// be passed to this method.
364    ///
365    /// ```compile_fail
366    /// use std::io::Error;
367    /// use std::io::sink;
368    ///
369    /// use qubit_codec::CapacityError;
370    /// use qubit_codec::TranscodeEncodeError;
371    /// use qubit_codec::TranscodeEncodeOutput;
372    /// use qubit_codec::TranscodeProgress;
373    /// use qubit_codec::Transcoder;
374    ///
375    /// struct GenericTranscoder;
376    ///
377    /// impl Transcoder for GenericTranscoder {
378    ///     type Input = u8;
379    ///     type Output = u8;
380    ///     type Error = TranscodeEncodeError<(), u8>;
381    ///
382    ///     fn max_transcode_output_len(
383    ///         &self,
384    ///         input_len: usize,
385    ///     ) -> Result<usize, CapacityError> {
386    ///         Ok(input_len)
387    ///     }
388    ///
389    ///     fn reset(
390    ///         &mut self,
391    ///         _output: &mut [u8],
392    ///         _output_index: usize,
393    ///     ) -> Result<usize, Self::Error> {
394    ///         Ok(0)
395    ///     }
396    ///
397    ///     fn transcode(
398    ///         &mut self,
399    ///         input: &[u8],
400    ///         input_index: usize,
401    ///         _output: &mut [u8],
402    ///         _output_index: usize,
403    ///     ) -> Result<TranscodeProgress, Self::Error> {
404    ///         Ok(TranscodeProgress::complete(input.len() - input_index, 0))
405    ///     }
406    ///
407    ///     fn finish(
408    ///         &mut self,
409    ///         _output: &mut [u8],
410    ///         _output_index: usize,
411    ///     ) -> Result<usize, Self::Error> {
412    ///         Ok(0)
413    ///     }
414    /// }
415    ///
416    /// let mut output = TranscodeEncodeOutput::with_capacity(sink(), 1);
417    /// let mut transcoder = GenericTranscoder;
418    /// let mut map_error = |_| Error::other("transcode error");
419    /// let _ = output.transcode(
420    ///     &mut transcoder,
421    ///     &mut map_error,
422    ///     &[0_u8],
423    ///     0,
424    ///     1,
425    /// );
426    /// ```
427    pub fn transcode<E, M, Value>(
428        &mut self,
429        encoder: &mut E,
430        map_error: &mut M,
431        input: &[Value],
432        input_index: usize,
433        count: usize,
434    ) -> Result<usize>
435    where
436        E: TranscodeEncoder<Input = Value, Output = O::Item>,
437        M: FnMut(E::Error) -> Error,
438    {
439        let input_end = SliceRange::checked_range_end(
440            input.len(),
441            input_index,
442            count,
443            "encode input range exceeds source buffer",
444        )?;
445        if count == 0 {
446            return Ok(0);
447        }
448        let input = &input[..input_end];
449        let mut read_total = 0;
450        let mut required_spare = NonZeroUsize::MIN;
451        while read_total < count {
452            self.ensure_transcode_spare_capacity(required_spare)?;
453            let (units, output_index, available_output) = self.output.spare_raw_parts_mut();
454            debug_assert!(
455                available_output >= required_spare.get(),
456                "reserved spare buffer is smaller than required encoder output",
457            );
458            let remaining_input = count - read_total;
459            let progress = encoder
460                .transcode(input, input_index + read_total, units, output_index)
461                .map_err(&mut *map_error)?;
462            let progress = validate_encode_progress(
463                progress,
464                input_index + read_total,
465                remaining_input,
466                output_index,
467                available_output,
468            )?;
469            let read = progress.read();
470            let written = progress.written();
471            // SAFETY: The progress bounds check above proved that the encoder
472            // initialized no more than the available spare output window.
473            unsafe {
474                self.output.advance(written);
475            }
476            read_total += read;
477            if progress.is_complete() {
478                return Ok(read_total);
479            }
480            if let TranscodeStatus::NeedOutput { required, .. } = progress.status() {
481                required_spare = required;
482                if read_total == count {
483                    self.ensure_transcode_spare_capacity(required)?;
484                }
485            }
486        }
487        Ok(read_total)
488    }
489
490    /// Finishes the encoder and flushes the wrapped unit output.
491    ///
492    /// This method writes final units directly into the internal spare buffer,
493    /// growing it when [`Transcoder::max_finish_output_len`] exceeds the
494    /// current capacity.
495    ///
496    /// # Type Parameters
497    ///
498    /// * `E` - Transcoder producing the wrapped output unit type.
499    /// * `M` - Mapper from transcoder errors into I/O errors.
500    /// * `Value` - Source value type accepted by the transcoder.
501    ///
502    /// # Parameters
503    ///
504    /// * `encoder` - Encoder whose final units are being collected.
505    /// * `map_error` - Function mapping transcode errors into I/O errors.
506    ///
507    /// # Errors
508    ///
509    /// Returns capacity, transcode finalization, or wrapped output flush
510    /// errors.
511    ///
512    /// # Panics
513    ///
514    /// Panics when the encoder exceeds its declared finish-output bound.
515    pub fn finish<E, M, Value>(&mut self, encoder: &mut E, map_error: &mut M) -> Result<()>
516    where
517        E: Transcoder<Input = Value, Output = O::Item>,
518        M: FnMut(E::Error) -> Error,
519    {
520        self.finish_to_buffer(encoder, map_error)?;
521        self.output.flush()
522    }
523
524    /// Finishes the encoder while retaining final units in the output buffer.
525    ///
526    /// This method separates encoder finalization from output delivery. It is
527    /// useful when a caller must record successful finalization before a later
528    /// flush can fail. Call [`Self::flush`] to deliver the retained units.
529    ///
530    /// # Type Parameters
531    ///
532    /// * `E` - Transcoder producing the wrapped output unit type.
533    /// * `M` - Mapper from transcoder errors into I/O errors.
534    /// * `Value` - Source value type accepted by the transcoder.
535    ///
536    /// # Parameters
537    ///
538    /// * `encoder` - Encoder whose final units are being collected.
539    /// * `map_error` - Function mapping transcode errors into I/O errors.
540    ///
541    /// # Errors
542    ///
543    /// Returns capacity planning, allocation, or transcoder finalization
544    /// errors. It does not perform output I/O.
545    ///
546    /// # Panics
547    ///
548    /// Panics when `encoder` writes more units than its declared finish bound.
549    pub fn finish_to_buffer<E, M, Value>(&mut self, encoder: &mut E, map_error: &mut M) -> Result<()>
550    where
551        E: Transcoder<Input = Value, Output = O::Item>,
552        M: FnMut(E::Error) -> Error,
553    {
554        let required = match encoder.max_finish_output_len() {
555            Ok(required) => required,
556            Err(error) => return Err(capacity_error_to_invalid_data(error)),
557        };
558        self.ensure_spare_capacity(required)?;
559        let (units, output_index, available) = self.output.spare_raw_parts_mut();
560        debug_assert!(
561            available >= required,
562            "insufficient finish capacity reserved in spare output buffer",
563        );
564        let written = encoder.finish(units, output_index).map_err(&mut *map_error)?;
565        assert!(written <= required, "finish wrote beyond its bound");
566        // SAFETY: The encoder reported initialized units within the spare
567        // range that was reserved above.
568        unsafe {
569            self.output.advance(written);
570        }
571        Ok(())
572    }
573
574    /// Ensures enough output capacity for one non-zero required unit count.
575    ///
576    /// The non-zero wrapper preserves the engine's progress contract while
577    /// this adapter performs any necessary allocation.
578    ///
579    /// # Parameters
580    ///
581    /// * `required` - Minimum writable unit count requested by the engine.
582    ///
583    /// # Errors
584    ///
585    /// Propagates buffer allocation or wrapped-output flush errors.
586    #[inline]
587    fn ensure_transcode_spare_capacity(&mut self, required: NonZeroUsize) -> Result<()> {
588        self.ensure_spare_capacity(required.get())
589    }
590}
591
592impl<O> TranscodeEncodeOutput<O>
593where
594    O: Output<Item = u8> + Seekable<Unit = u8>,
595{
596    /// Flushes pending bytes, then seeks the wrapped byte output.
597    ///
598    /// # Parameters
599    ///
600    /// * `position` - Target seek position.
601    ///
602    /// # Returns
603    ///
604    /// The new stream position reported by the wrapped output.
605    ///
606    /// # Errors
607    ///
608    /// Returns flush or seek errors from the wrapped output.
609    pub fn seek(&mut self, position: SeekFrom) -> Result<u64> {
610        self.output.seek_to(position)
611    }
612}
613
614impl<O> Write for TranscodeEncodeOutput<O>
615where
616    O: Output<Item = u8>,
617{
618    /// Writes raw bytes through the internal buffer without encoding them.
619    ///
620    /// # Parameters
621    ///
622    /// * `input` - Bytes to append to the buffered unit output.
623    ///
624    /// # Returns
625    ///
626    /// The number of bytes accepted, which may be shorter than `input`.
627    ///
628    /// # Errors
629    ///
630    /// Propagates wrapped-output errors when buffer delivery is needed.
631    #[inline]
632    fn write(&mut self, input: &[u8]) -> Result<usize> {
633        Output::write(&mut self.output, input)
634    }
635
636    /// Writes all raw bytes through the internal buffer without encoding them.
637    ///
638    /// # Parameters
639    ///
640    /// * `input` - Bytes that must all be accepted before success.
641    ///
642    /// # Errors
643    ///
644    /// Propagates output errors and returns `WriteZero` if output stops
645    /// progressing.
646    ///
647    /// # Panics
648    ///
649    /// Panics when the wrapped output reports more bytes than supplied.
650    fn write_all(&mut self, input: &[u8]) -> Result<()> {
651        let mut written = 0;
652        while written < input.len() {
653            let count = Output::write(&mut self.output, &input[written..])?;
654            if count == 0 {
655                return Err(Error::from(ErrorKind::WriteZero));
656            }
657            assert!(
658                count <= input.len() - written,
659                "Output::write returned a count beyond the input length",
660            );
661            written += count;
662        }
663        Ok(())
664    }
665
666    /// Flushes buffered bytes to the wrapped output without finishing an
667    /// encoder.
668    ///
669    /// # Errors
670    ///
671    /// Propagates wrapped-output flush errors.
672    #[inline]
673    fn flush(&mut self) -> Result<()> {
674        TranscodeEncodeOutput::flush(self)
675    }
676}
677
678impl<O> Seek for TranscodeEncodeOutput<O>
679where
680    O: Output<Item = u8> + Seekable<Unit = u8>,
681{
682    /// Flushes pending bytes, then seeks the wrapped byte output.
683    ///
684    /// # Parameters
685    ///
686    /// * `position` - Target position relative to the selected seek origin.
687    ///
688    /// # Returns
689    ///
690    /// The new absolute byte position.
691    ///
692    /// # Errors
693    ///
694    /// Propagates flush or seek errors from the wrapped output.
695    #[inline]
696    fn seek(&mut self, position: SeekFrom) -> Result<u64> {
697        self.seek(position)
698    }
699}
700
701impl<O> fmt::Debug for TranscodeEncodeOutput<O>
702where
703    O: Output,
704    O::Item: Copy + Default,
705    BufferedOutput<O>: fmt::Debug,
706{
707    /// Formats the owned buffer and underlying output for diagnostics.
708    ///
709    /// # Parameters
710    ///
711    /// * `formatter` - Destination and formatting options.
712    ///
713    /// # Errors
714    ///
715    /// Returns a formatting error if the destination rejects output.
716    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
717        formatter
718            .debug_struct("TranscodeEncodeOutput")
719            .field("output", &self.output)
720            .finish()
721    }
722}
723
724/// Wraps a streaming capacity failure as `InvalidData`, retaining its source.
725///
726/// # Parameters
727///
728/// * `error` - Capacity planning failure from a streaming transcoder.
729///
730/// # Returns
731///
732/// An owned I/O error carrying the original capacity error.
733fn capacity_error_to_invalid_data(error: CapacityError) -> Error {
734    Error::new(ErrorKind::InvalidData, error)
735}
736
737/// Preserves successful values and maps one-value failures into I/O errors.
738///
739/// # Type Parameters
740///
741/// * `T` - Successful result value.
742/// * `E` - Codec-domain error passed to the mapper.
743/// * `Value` - Source value type carried by an unencodable error.
744///
745/// # Parameters
746///
747/// * `result` - Codec lifecycle result to translate.
748/// * `map_error` - Called only for a codec-domain failure.
749///
750/// # Returns
751///
752/// The original successful value without transformation.
753///
754/// # Errors
755///
756/// Returns mapped domain errors or `InvalidInput` for bounds and domain
757/// rejection.
758fn map_encode_value_result<T, E, Value>(
759    result: CoreResult<T, TranscodeEncodeError<E, Value>>,
760    map_error: &mut dyn FnMut(E) -> Error,
761) -> Result<T> {
762    match result {
763        Ok(value) => Ok(value),
764        Err(error) => Err(map_encode_value_error(error, map_error)),
765    }
766}
767
768/// Maps domain failures through the caller and other failures to
769/// `InvalidInput`.
770///
771/// # Type Parameters
772///
773/// * `E` - Codec-domain error passed to the mapper.
774/// * `Value` - Rejected source value type, discarded during I/O translation.
775///
776/// # Parameters
777///
778/// * `error` - Encoding failure consumed by this translation.
779/// * `map_error` - Invoked only for the `Domain` variant.
780///
781/// # Returns
782///
783/// An I/O error retaining mapped domain detail or a bound/rejection message.
784#[inline(never)]
785fn map_encode_value_error<E, Value>(
786    error: TranscodeEncodeError<E, Value>,
787    map_error: &mut dyn FnMut(E) -> Error,
788) -> Error {
789    match error {
790        TranscodeEncodeError::Domain(error) => map_error(error.into_source()),
791        TranscodeEncodeError::Unencodable { .. } => Error::new(ErrorKind::InvalidInput, "codec cannot encode value"),
792        TranscodeEncodeError::Failure(_) => Error::new(ErrorKind::InvalidInput, "codec output bound overflow"),
793    }
794}