Skip to main content

qubit_codec/transcode/io/
transcode_decode_input.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 input driver that decodes units into values.
9
10use core::fmt;
11use std::io::{
12    Error,
13    ErrorKind,
14    Read,
15    Result,
16    Seek,
17    SeekFrom,
18};
19
20use qubit_io::{
21    Buffer,
22    BufferedInput,
23    Input,
24    Seekable,
25    UncheckedSlice,
26};
27
28use crate::{
29    TranscodeError,
30    TranscodeStatus,
31    Transcoder,
32};
33
34/// Decodes an [`Input`] unit stream into an [`Input`] value stream.
35///
36/// This type owns only the unit-level [`qubit_io::BufferedInput`]. Callers pass
37/// a streaming [`Transcoder`] and error mapper to each decode operation, which
38/// lets one buffered input drive different decoders without nesting buffers or
39/// storing decoder-specific state in the buffer owner.
40///
41/// # Type Parameters
42///
43/// * `I` - Wrapped unit input.
44pub struct TranscodeDecodeInput<I>
45where
46    I: Input,
47    I::Item: Copy + Default,
48{
49    input: BufferedInput<I>,
50}
51
52impl<I> TranscodeDecodeInput<I>
53where
54    I: Input,
55    I::Item: Copy + Default,
56{
57    /// Creates a decoder input with the default unit buffer capacity.
58    ///
59    /// # Parameters
60    ///
61    /// * `inner` - Unit input read by this adapter.
62    ///
63    /// # Returns
64    ///
65    /// A new buffered decoder input.
66    #[inline]
67    #[must_use]
68    pub fn new(inner: I) -> Self {
69        Self {
70            input: BufferedInput::new(inner),
71        }
72    }
73
74    /// Creates a decoder input with a unit buffer of at least `capacity`.
75    ///
76    /// # Parameters
77    ///
78    /// * `inner` - Unit input read by this adapter.
79    /// * `capacity` - Requested internal unit buffer capacity.
80    ///
81    /// # Returns
82    ///
83    /// A new buffered decoder input.
84    #[inline]
85    #[must_use]
86    pub fn with_capacity(inner: I, capacity: usize) -> Self {
87        Self {
88            input: BufferedInput::with_capacity(inner, capacity),
89        }
90    }
91
92    /// Returns a shared reference to the wrapped unit input.
93    ///
94    /// # Returns
95    ///
96    /// A shared reference to the wrapped unit input.
97    #[inline(always)]
98    #[must_use]
99    pub const fn inner(&self) -> &I {
100        self.input.inner()
101    }
102
103    /// Returns a mutable reference to the wrapped unit input.
104    ///
105    /// # Returns
106    ///
107    /// A mutable reference to the wrapped unit input.
108    #[inline(always)]
109    pub fn inner_mut(&mut self) -> &mut I {
110        self.input.inner_mut()
111    }
112
113    /// Returns the number of unread units currently buffered.
114    ///
115    /// # Returns
116    ///
117    /// The number of unread units in the internal buffer.
118    #[inline(always)]
119    #[must_use]
120    pub fn available(&self) -> usize {
121        self.input.available()
122    }
123
124    /// Returns the currently buffered unread units.
125    ///
126    /// # Returns
127    ///
128    /// Returns a shared slice over the unread portion of the internal unit
129    /// buffer. The slice is valid until this adapter is mutated.
130    #[inline(always)]
131    #[must_use]
132    pub fn unread(&self) -> &[I::Item] {
133        self.input.unread()
134    }
135
136    /// Returns the internal unit buffer capacity.
137    ///
138    /// # Returns
139    ///
140    /// The maximum number of units retained in the internal buffer.
141    #[inline(always)]
142    #[must_use]
143    pub fn capacity(&self) -> usize {
144        self.input.capacity()
145    }
146
147    /// Refills the internal buffer until at least `count` unread units are
148    /// available.
149    ///
150    /// # Parameters
151    ///
152    /// * `count` - Minimum number of unread units required.
153    ///
154    /// # Errors
155    ///
156    /// Returns I/O errors from the wrapped input while refilling.
157    #[inline(always)]
158    pub fn fill_until(&mut self, count: usize) -> std::io::Result<bool> {
159        self.input.fill_until(count)
160    }
161
162    /// Consumes unread units from the current buffer window.
163    ///
164    /// # Parameters
165    ///
166    /// * `count` - Number of unread units to discard.
167    ///
168    /// # Panics
169    ///
170    /// In debug builds, panics when `count` exceeds [`Self::available`].
171    #[inline(always)]
172    pub fn consume(&mut self, count: usize) {
173        debug_assert!(
174            count <= self.available(),
175            "cannot consume beyond buffered input",
176        );
177        // SAFETY: The caller-provided count is within the unread window.
178        unsafe {
179            self.input.consume(count);
180        }
181    }
182
183    /// Copies unread units into an indexed output range without consuming them.
184    ///
185    /// # Parameters
186    ///
187    /// * `output` - Destination storage that receives a copy of unread units.
188    /// * `output_index` - Start index inside `output`.
189    /// * `count` - Number of unread units to copy.
190    ///
191    /// # Safety
192    ///
193    /// The caller must guarantee that `output_index..output_index + count` is
194    /// a valid range inside `output`, that the addition does not overflow, that
195    /// `count <= self.available()`, and that the destination range does not
196    /// overlap with the unread units stored inside this buffer.
197    #[inline(always)]
198    pub unsafe fn copy_unread_to(
199        &mut self,
200        output: &mut [I::Item],
201        output_index: usize,
202        count: usize,
203    ) {
204        // SAFETY: The caller guarantees the destination range and non-overlap
205        // requirements for the unread copy.
206        let unread = self.input.unread();
207        debug_assert!(
208            UncheckedSlice::range_fits(unread.len(), 0, count),
209            "unchecked unread copy range exceeds unread source",
210        );
211        debug_assert!(
212            UncheckedSlice::range_fits(output.len(), output_index, count),
213            "unchecked copy destination range exceeds output buffer",
214        );
215        unsafe {
216            UncheckedSlice::copy_nonoverlapping(
217                unread,
218                0,
219                output,
220                output_index,
221                count,
222            );
223        }
224    }
225
226    /// Consumes this adapter and returns its parts.
227    ///
228    /// # Returns
229    ///
230    /// The wrapped input and the buffer holding unread units.
231    #[inline]
232    #[must_use]
233    pub fn into_parts(self) -> (I, Buffer<I::Item>) {
234        self.input.into_parts()
235    }
236
237    /// Reads buffered units into an indexed output range.
238    ///
239    /// # Parameters
240    ///
241    /// * `output` - Destination unit storage.
242    /// * `output_index` - Start index inside `output`.
243    /// * `count` - Maximum number of units to read.
244    ///
245    /// # Returns
246    ///
247    /// The number of units copied into `output`.
248    ///
249    /// # Errors
250    ///
251    /// Returns input or buffer validation errors from the wrapped
252    /// [`qubit_io::BufferedInput`].
253    ///
254    /// # Safety
255    ///
256    /// The caller must guarantee that `output_index..output_index + count` is
257    /// a valid range inside `output` and that the addition does not overflow.
258    #[inline(always)]
259    pub unsafe fn read_unchecked(
260        &mut self,
261        output: &mut [I::Item],
262        output_index: usize,
263        count: usize,
264    ) -> Result<usize> {
265        // SAFETY: The caller guarantees the destination range is valid.
266        unsafe { self.input.read_unchecked(output, output_index, count) }
267    }
268
269    /// Decodes values into an indexed output range using a streaming
270    /// [`Transcoder`].
271    ///
272    /// # Parameters
273    ///
274    /// * `decoder` - Streaming decoder used for this operation.
275    /// * `map_error` - Function mapping decoder errors into I/O errors.
276    /// * `output` - Destination value storage.
277    /// * `output_index` - Start index inside `output`.
278    /// * `count` - Maximum number of values to write.
279    ///
280    /// # Returns
281    ///
282    /// The number of values written. Incomplete EOF tails are left buffered
283    /// and reported as `Ok(written)`, so callers can apply their own EOF
284    /// policy.
285    ///
286    /// # Errors
287    ///
288    /// Returns input errors, invalid output ranges, capacity errors from the
289    /// internal buffer, or decoder errors mapped by `map_error`.
290    #[inline]
291    pub fn transcode_into<D, M, Value>(
292        &mut self,
293        decoder: &mut D,
294        map_error: &mut M,
295        output: &mut [Value],
296        output_index: usize,
297        count: usize,
298    ) -> Result<usize>
299    where
300        D: Transcoder<I::Item, Value>,
301        M: FnMut(TranscodeError<D::Error>) -> Error,
302    {
303        let output_end = UncheckedSlice::checked_range_end(
304            output.len(),
305            output_index,
306            count,
307            "decoded output range exceeds destination buffer",
308        )?;
309        if count == 0 {
310            return Ok(0);
311        }
312        let output = &mut output[..output_end];
313        let mut written_total = 0;
314        loop {
315            if self.input.available() == 0 && !self.input.fill_more()? {
316                return Ok(written_total);
317            }
318            let units = self.input.unread();
319            let available_input = units.len();
320            let remaining_output = count - written_total;
321            let progress = decoder
322                .transcode(units, 0, output, output_index + written_total)
323                .map_err(&mut *map_error)?;
324            progress
325                .validate(
326                    0,
327                    available_input,
328                    output_index + written_total,
329                    remaining_output,
330                )
331                .map_err(|error| Error::new(ErrorKind::InvalidData, error))?;
332            let consumed = progress.read();
333            let written = progress.written();
334            // SAFETY: TranscodeProgress::validate proved that the decoder
335            // consumed no more than the currently unread input window.
336            unsafe {
337                self.input.consume(consumed);
338            }
339            written_total += written;
340            match progress.status() {
341                TranscodeStatus::Complete => {
342                    if written_total == count || consumed == 0 {
343                        return Ok(written_total);
344                    }
345                }
346                TranscodeStatus::NeedOutput { .. } => {
347                    return Ok(written_total);
348                }
349                TranscodeStatus::NeedInput { required, .. } => {
350                    if self.input.fill_until(required.get())? {
351                        continue;
352                    }
353                    return Ok(written_total);
354                }
355            }
356        }
357    }
358
359    /// Finishes a streaming decoder into an indexed output range.
360    ///
361    /// # Parameters
362    ///
363    /// * `decoder` - Streaming decoder whose final output is being collected.
364    /// * `map_error` - Function mapping decoder errors into I/O errors.
365    /// * `output` - Destination value storage.
366    /// * `output_index` - Start index inside `output`.
367    /// * `count` - Maximum number of finish values to write.
368    ///
369    /// # Returns
370    ///
371    /// The number of values written by the decoder finish operation.
372    ///
373    /// # Errors
374    ///
375    /// Returns invalid output ranges, capacity errors, or decoder finalization
376    /// errors mapped to I/O errors.
377    #[inline]
378    pub fn finish_transcode_into<D, M, Value>(
379        &mut self,
380        decoder: &mut D,
381        map_error: &mut M,
382        output: &mut [Value],
383        output_index: usize,
384        count: usize,
385    ) -> Result<usize>
386    where
387        D: Transcoder<I::Item, Value>,
388        M: FnMut(TranscodeError<D::Error>) -> Error,
389    {
390        let required = decoder
391            .max_finish_output_len()
392            .map_err(capacity_to_io_error)?;
393        // Validate the caller-supplied count range first (InvalidInput).
394        let output_end = UncheckedSlice::checked_range_end(
395            output.len(),
396            output_index,
397            count,
398            "finish output range exceeds destination buffer",
399        )?;
400        // Then verify the finish bound fits in the available space
401        // (InvalidData).
402        let available = output.len().saturating_sub(output_index);
403        if available < required {
404            return Err(Error::new(
405                ErrorKind::InvalidData,
406                "insufficient output for decoder finish bound",
407            ));
408        }
409        let output_end = output_end.max(output_index + required);
410        let output = &mut output[..output_end];
411        let written = decoder
412            .finish(output, output_index)
413            .map_err(&mut *map_error)?;
414        debug_assert!(written <= required, "finish wrote beyond its bound");
415        Ok(written)
416    }
417}
418
419impl<I> TranscodeDecodeInput<I>
420where
421    I: Input<Item = u8> + Seekable<Item = u8>,
422{
423    /// Seeks the wrapped byte input and discards buffered bytes after success.
424    ///
425    /// # Parameters
426    ///
427    /// * `position` - Target seek position.
428    ///
429    /// # Returns
430    ///
431    /// The new stream position reported by the wrapped input.
432    ///
433    /// # Errors
434    ///
435    /// Returns seek errors from the wrapped input.
436    #[inline]
437    pub fn seek(&mut self, position: SeekFrom) -> Result<u64> {
438        self.input.seek_to(position)
439    }
440}
441
442impl<I> Read for TranscodeDecodeInput<I>
443where
444    I: Input<Item = u8>,
445{
446    /// Reads raw bytes through the internal buffer.
447    #[inline]
448    fn read(&mut self, output: &mut [u8]) -> Result<usize> {
449        // SAFETY: The full output slice is a valid destination range.
450        unsafe { self.input.read_unchecked(output, 0, output.len()) }
451    }
452}
453
454impl<I> Seek for TranscodeDecodeInput<I>
455where
456    I: Input<Item = u8> + Seekable<Item = u8>,
457{
458    /// Seeks the wrapped byte input and discards buffered bytes after success.
459    #[inline]
460    fn seek(&mut self, position: SeekFrom) -> Result<u64> {
461        self.seek(position)
462    }
463}
464
465impl<I> fmt::Debug for TranscodeDecodeInput<I>
466where
467    I: Input,
468    I::Item: Copy + Default,
469    BufferedInput<I>: fmt::Debug,
470{
471    /// Formats this buffered decode input for debugging.
472    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
473        formatter
474            .debug_struct("TranscodeDecodeInput")
475            .field("input", &self.input)
476            .finish()
477    }
478}
479
480/// Converts a capacity planning failure into an I/O error.
481fn capacity_to_io_error(error: crate::CapacityError) -> Error {
482    Error::new(ErrorKind::InvalidData, error)
483}