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 std::io::{
12    Error,
13    ErrorKind,
14    Result,
15    Seek,
16    SeekFrom,
17    Write,
18};
19
20use qubit_io::{
21    Buffer,
22    BufferedOutput,
23    Output,
24    Seekable,
25};
26
27use crate::{
28    TranscodeError,
29    TranscodeStatus,
30    Transcoder,
31};
32
33/// Encodes an [`Output`] value stream into an [`Output`] unit stream.
34///
35/// This type owns only the unit-level [`qubit_io::BufferedOutput`]. Callers
36/// pass a [`crate::Codec`] and error mapper to each encode operation, which
37/// lets one buffered output drive different encoders without nesting buffers or
38/// storing codec-specific state in the buffer owner.
39///
40/// [`Self::flush`] only drains already buffered units. State-aware streaming
41/// encoders can use [`Self::transcode_from`] and [`Self::finish`] explicitly.
42///
43/// # Type Parameters
44///
45/// * `O` - Wrapped unit output.
46pub struct TranscodeEncodeOutput<O>
47where
48    O: Output,
49    O::Item: Copy + Default,
50{
51    output: BufferedOutput<O>,
52}
53
54impl<O> TranscodeEncodeOutput<O>
55where
56    O: Output,
57    O::Item: Copy + Default,
58{
59    /// Creates an encoder output with the default unit buffer capacity.
60    ///
61    /// # Parameters
62    ///
63    /// * `inner` - Unit output written by this adapter.
64    ///
65    /// # Returns
66    ///
67    /// A new buffered encoder output.
68    #[must_use]
69    #[inline]
70    pub fn new(inner: O) -> Self {
71        Self {
72            output: BufferedOutput::new(inner),
73        }
74    }
75
76    /// Creates an encoder output with a unit buffer of at least `capacity`.
77    ///
78    /// # Parameters
79    ///
80    /// * `inner` - Unit output written by this adapter.
81    /// * `capacity` - Requested internal unit buffer capacity.
82    ///
83    /// # Returns
84    ///
85    /// A new buffered encoder output.
86    #[must_use]
87    #[inline]
88    pub fn with_capacity(inner: O, capacity: usize) -> Self {
89        Self {
90            output: BufferedOutput::with_capacity(inner, capacity),
91        }
92    }
93
94    /// Returns a shared reference to the wrapped unit output.
95    ///
96    /// # Returns
97    ///
98    /// A shared reference to the wrapped unit output.
99    #[must_use]
100    #[inline(always)]
101    pub const fn inner(&self) -> &O {
102        self.output.inner()
103    }
104
105    /// Returns a mutable reference to the wrapped unit output.
106    ///
107    /// # Returns
108    ///
109    /// A mutable reference to the wrapped unit output.
110    #[inline(always)]
111    pub fn inner_mut(&mut self) -> &mut O {
112        self.output.inner_mut()
113    }
114
115    /// Returns the available capacity of the spare output buffer.
116    ///
117    /// # Returns
118    ///
119    /// The number of output units that can still be appended without flushing.
120    #[must_use]
121    #[inline(always)]
122    pub fn spare_capacity(&self) -> usize {
123        self.output.spare_capacity()
124    }
125
126    /// Returns raw spare-buffer parts for the internal output buffer.
127    ///
128    /// # Returns
129    ///
130    /// The full backing storage, the spare start index, and the spare unit
131    /// count.
132    #[inline(always)]
133    #[must_use]
134    pub fn spare_raw_parts_mut(&mut self) -> (&mut [O::Item], usize, usize) {
135        self.output.spare_raw_parts_mut()
136    }
137
138    /// Marks `count` units from [`Self::spare_raw_parts_mut`] as written.
139    ///
140    /// # Safety
141    ///
142    /// The caller must guarantee that `count <= Self::spare_capacity()` and
143    /// that the corresponding units in the returned spare slice have been
144    /// initialized.
145    #[inline(always)]
146    pub unsafe fn advance(&mut self, count: usize) {
147        // SAFETY: The caller guarantees `count` and initialization invariants.
148        unsafe { self.output.advance(count) }
149    }
150
151    /// Ensures that at least `count` spare units are available.
152    ///
153    /// # Parameters
154    ///
155    /// * `count` - Number of spare units required.
156    ///
157    /// # Errors
158    ///
159    /// Returns I/O errors from the wrapped output while flushing pending units.
160    #[inline(always)]
161    pub fn ensure_spare_capacity(&mut self, count: usize) -> Result<()> {
162        self.output.ensure_spare_capacity(count)
163    }
164
165    /// Consumes this adapter and returns its parts.
166    ///
167    /// # Returns
168    ///
169    /// The wrapped output and the buffer holding pending units.
170    #[must_use]
171    #[inline]
172    pub fn into_parts(self) -> (O, Buffer<O::Item>) {
173        self.output.into_parts()
174    }
175
176    /// Flushes buffered units without finishing any encoder stream.
177    ///
178    /// # Errors
179    ///
180    /// Returns errors from the wrapped output while flushing pending units.
181    #[inline]
182    pub fn flush(&mut self) -> Result<()> {
183        self.output.flush()
184    }
185
186    /// Encodes values from an indexed input range using a streaming
187    /// [`Transcoder`].
188    ///
189    /// # Parameters
190    ///
191    /// * `encoder` - Streaming encoder used for this operation.
192    /// * `map_error` - Function mapping encoder errors into I/O errors.
193    /// * `input` - Source values.
194    /// * `input_index` - Start index inside `input`.
195    /// * `count` - Maximum number of values to encode.
196    ///
197    /// # Returns
198    ///
199    /// The number of source values consumed.
200    ///
201    /// # Errors
202    ///
203    /// Returns capacity, encoder, or output errors.
204    ///
205    /// # Safety
206    ///
207    /// The caller must guarantee that `input_index..input_index + count` is
208    /// a valid range inside `input` and that the addition does not overflow.
209    pub unsafe fn transcode_from<E, M, Value>(
210        &mut self,
211        encoder: &mut E,
212        map_error: &mut M,
213        input: &[Value],
214        input_index: usize,
215        count: usize,
216    ) -> Result<usize>
217    where
218        E: Transcoder<Value, O::Item>,
219        M: FnMut(TranscodeError<E::Error>) -> Error,
220    {
221        debug_assert!(
222            qubit_io::UncheckedSlice::range_fits(
223                input.len(),
224                input_index,
225                count
226            ),
227            "unchecked encode input range exceeds source buffer",
228        );
229        if count == 0 {
230            return Ok(0);
231        }
232        let input_end = input_index + count;
233        let input = &input[..input_end];
234        let mut read_total = 0;
235        while read_total < count {
236            // Each encoder step writes into the spare output window. When the
237            // buffer is full of pending units, spare capacity drops to zero and
238            // `transcode` cannot make progress. Reserving one spare slot drains
239            // pending units to the wrapped output only when needed.
240            self.output.ensure_spare_capacity(1)?;
241            let (units, output_index, available_output) =
242                self.output.spare_raw_parts_mut();
243            let remaining_input = count - read_total;
244            let progress = encoder
245                .transcode(input, input_index + read_total, units, output_index)
246                .map_err(&mut *map_error)?;
247            let read = progress.read();
248            let written = progress.written();
249            if read > remaining_input {
250                return Err(Error::new(
251                    ErrorKind::InvalidData,
252                    "transcoder consumed beyond input range",
253                ));
254            }
255            if written > available_output {
256                return Err(Error::new(
257                    ErrorKind::InvalidData,
258                    "transcoder wrote beyond spare output",
259                ));
260            }
261            // SAFETY: The encoder reported initialized units in the spare
262            // output window, and the count was validated above.
263            unsafe {
264                self.output.advance(written);
265            }
266            read_total += read;
267            match progress.status() {
268                TranscodeStatus::Complete => return Ok(read_total),
269                TranscodeStatus::NeedOutput {
270                    output_index: status_output_index,
271                    additional,
272                    available,
273                    ..
274                } => {
275                    if status_output_index != output_index + written {
276                        return Err(Error::new(
277                            ErrorKind::InvalidData,
278                            "transcoder reported inconsistent NeedOutput index",
279                        ));
280                    }
281                    // `available + additional` is the spare window size the
282                    // encoder needs before it can continue. Drain pending units
283                    // to the wrapped output only when the current spare window
284                    // is smaller than that requirement.
285                    let required = available
286                        .checked_add(additional.get())
287                        .ok_or_else(|| {
288                            Error::new(
289                                ErrorKind::InvalidData,
290                                "transcoder output requirement overflowed",
291                            )
292                        })?;
293                    self.output.ensure_spare_capacity(required)?;
294                }
295                TranscodeStatus::NeedInput { .. } => {
296                    return Err(Error::new(
297                        ErrorKind::InvalidData,
298                        "encoder unexpectedly requested more input",
299                    ));
300                }
301            }
302        }
303        Ok(read_total)
304    }
305
306    /// Finishes the encoder and flushes the wrapped unit output.
307    ///
308    /// # Parameters
309    ///
310    /// * `encoder` - Encoder whose final units are being collected.
311    /// * `map_error` - Function mapping encoder errors into I/O errors.
312    ///
313    /// # Errors
314    ///
315    /// Returns capacity, encoder finalization, or wrapped output flush errors.
316    pub fn finish<E, M, Value>(
317        &mut self,
318        encoder: &mut E,
319        map_error: &mut M,
320    ) -> Result<()>
321    where
322        E: Transcoder<Value, O::Item>,
323        M: FnMut(TranscodeError<E::Error>) -> Error,
324    {
325        let required = encoder
326            .max_finish_output_len()
327            .map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
328        self.output.ensure_spare_capacity(required)?;
329        let (units, output_index, available) =
330            self.output.spare_raw_parts_mut();
331        debug_assert!(
332            available >= required,
333            "insufficient finish capacity reserved in spare output buffer",
334        );
335        let written = encoder
336            .finish(units, output_index)
337            .map_err(&mut *map_error)?;
338        assert!(written <= required, "finish wrote beyond its bound");
339        // SAFETY: The encoder reported initialized units within the spare
340        // range that was reserved above.
341        unsafe {
342            self.output.advance(written);
343        }
344        self.output.flush()
345    }
346}
347
348impl<O> TranscodeEncodeOutput<O>
349where
350    O: Output<Item = u8> + Seekable<Item = u8>,
351{
352    /// Flushes pending bytes, then seeks the wrapped byte output.
353    ///
354    /// # Parameters
355    ///
356    /// * `position` - Target seek position.
357    ///
358    /// # Returns
359    ///
360    /// The new stream position reported by the wrapped output.
361    ///
362    /// # Errors
363    ///
364    /// Returns flush or seek errors from the wrapped output.
365    #[inline]
366    pub fn seek(&mut self, position: SeekFrom) -> Result<u64> {
367        self.output.seek_to(position)
368    }
369}
370
371impl<O> Write for TranscodeEncodeOutput<O>
372where
373    O: Output<Item = u8>,
374{
375    /// Writes raw bytes through the internal buffer.
376    #[inline]
377    fn write(&mut self, input: &[u8]) -> Result<usize> {
378        Output::write(&mut self.output, input)
379    }
380
381    /// Writes all raw bytes through the internal buffer.
382    #[inline]
383    fn write_all(&mut self, input: &[u8]) -> Result<()> {
384        self.output.write_all(input)
385    }
386
387    /// Flushes buffered bytes to the wrapped output.
388    #[inline]
389    fn flush(&mut self) -> Result<()> {
390        TranscodeEncodeOutput::flush(self)
391    }
392}
393
394impl<O> Seek for TranscodeEncodeOutput<O>
395where
396    O: Output<Item = u8> + Seekable<Item = u8>,
397{
398    /// Flushes pending bytes, then seeks the wrapped byte output.
399    #[inline]
400    fn seek(&mut self, position: SeekFrom) -> Result<u64> {
401        self.seek(position)
402    }
403}
404
405impl<O> fmt::Debug for TranscodeEncodeOutput<O>
406where
407    O: Output,
408    O::Item: Copy + Default,
409    BufferedOutput<O>: fmt::Debug,
410{
411    /// Formats this buffered encode output for debugging.
412    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
413        formatter
414            .debug_struct("TranscodeEncodeOutput")
415            .field("output", &self.output)
416            .finish()
417    }
418}