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