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