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};
26
27use crate::codec::assert_unit_bounds;
28use crate::{
29    Codec,
30    TranscodeError,
31    TranscodeStatus,
32    Transcoder,
33};
34
35/// Decodes an [`Input`] unit stream into an [`Input`] value stream.
36///
37/// This type owns only the unit-level [`qubit_io::BufferedInput`]. Callers pass
38/// a [`Codec`] and error mapper to each decode operation, which lets one
39/// buffered input drive different decoders without nesting buffers or storing
40/// codec-specific state in the buffer owner.
41///
42/// A [`Codec`] has no decoder-owned finish state. Callers that need a stateful
43/// streaming decoder should use [`Self::transcode_into`] and
44/// [`Self::finish_transcode_into`] instead.
45///
46/// # Type Parameters
47///
48/// * `I` - Wrapped unit input.
49pub struct TranscodeDecodeInput<I>
50where
51    I: Input,
52    I::Item: Copy + Default,
53{
54    input: BufferedInput<I>,
55}
56
57impl<I> TranscodeDecodeInput<I>
58where
59    I: Input,
60    I::Item: Copy + Default,
61{
62    /// Creates a decoder input with the default unit buffer capacity.
63    ///
64    /// # Parameters
65    ///
66    /// * `inner` - Unit input read by this adapter.
67    ///
68    /// # Returns
69    ///
70    /// A new buffered decoder input.
71    #[must_use]
72    #[inline]
73    pub fn new(inner: I) -> Self {
74        Self {
75            input: BufferedInput::new(inner),
76        }
77    }
78
79    /// Creates a decoder input with a unit buffer of at least `capacity`.
80    ///
81    /// # Parameters
82    ///
83    /// * `inner` - Unit input read by this adapter.
84    /// * `capacity` - Requested internal unit buffer capacity.
85    ///
86    /// # Returns
87    ///
88    /// A new buffered decoder input.
89    #[must_use]
90    #[inline]
91    pub fn with_capacity(inner: I, capacity: usize) -> Self {
92        Self {
93            input: BufferedInput::with_capacity(inner, capacity),
94        }
95    }
96
97    /// Returns a shared reference to the wrapped unit input.
98    ///
99    /// # Returns
100    ///
101    /// A shared reference to the wrapped unit input.
102    #[must_use]
103    #[inline(always)]
104    pub const fn inner(&self) -> &I {
105        self.input.inner()
106    }
107
108    /// Returns a mutable reference to the wrapped unit input.
109    ///
110    /// # Returns
111    ///
112    /// A mutable reference to the wrapped unit input.
113    #[inline(always)]
114    pub fn inner_mut(&mut self) -> &mut I {
115        self.input.inner_mut()
116    }
117
118    /// Returns the number of unread units currently buffered.
119    ///
120    /// # Returns
121    ///
122    /// The number of unread units in the internal buffer.
123    #[must_use]
124    #[inline(always)]
125    pub fn available(&self) -> usize {
126        self.input.available()
127    }
128
129    /// Returns the currently buffered unread units.
130    ///
131    /// # Returns
132    ///
133    /// Returns a shared slice over the unread portion of the internal unit
134    /// buffer. The slice is valid until this adapter is mutated.
135    #[must_use]
136    #[inline(always)]
137    pub fn unread(&self) -> &[I::Item] {
138        self.input.unread()
139    }
140
141    /// Returns the internal unit buffer capacity.
142    ///
143    /// # Returns
144    ///
145    /// The maximum number of units retained in the internal buffer.
146    #[must_use]
147    #[inline(always)]
148    pub fn capacity(&self) -> usize {
149        self.input.capacity()
150    }
151
152    /// Refills the internal buffer until at least `count` unread units are
153    /// available.
154    ///
155    /// # Parameters
156    ///
157    /// * `count` - Minimum number of unread units required.
158    ///
159    /// # Errors
160    ///
161    /// Returns I/O errors from the wrapped input while refilling.
162    #[inline(always)]
163    pub fn fill_until(&mut self, count: usize) -> std::io::Result<bool> {
164        self.input.fill_until(count)
165    }
166
167    /// Consumes unread units from the current buffer window.
168    ///
169    /// # Parameters
170    ///
171    /// * `count` - Number of unread units to discard.
172    ///
173    /// # Panics
174    ///
175    /// Panics when `count` exceeds [`Self::available`].
176    #[inline(always)]
177    pub fn consume(&mut self, count: usize) {
178        assert!(
179            count <= self.available(),
180            "cannot consume beyond buffered input",
181        );
182        // SAFETY: The assertion above validates the unread input range.
183        unsafe {
184            self.input.consume(count);
185        }
186    }
187
188    /// Copies unread units into an indexed output range without consuming them.
189    ///
190    /// # Parameters
191    ///
192    /// * `output` - Destination storage that receives a copy of unread units.
193    /// * `output_index` - Start index inside `output`.
194    /// * `count` - Number of unread units to copy.
195    ///
196    /// # Safety
197    ///
198    /// The caller must guarantee that `output_index..output_index + count` is
199    /// a valid range inside `output`, that the addition does not overflow, that
200    /// `count <= self.available()`, and that the destination range does not
201    /// overlap with the unread units stored inside this buffer.
202    #[inline(always)]
203    pub unsafe fn copy_unread_to(
204        &mut self,
205        output: &mut [I::Item],
206        output_index: usize,
207        count: usize,
208    ) {
209        // SAFETY: The caller guarantees the destination range and non-overlap
210        // requirements for the unread copy.
211        let unread = self.input.unread();
212        debug_assert!(
213            qubit_io::UncheckedSlice::range_fits(unread.len(), 0, count),
214            "unchecked unread copy range exceeds unread source",
215        );
216        debug_assert!(
217            qubit_io::UncheckedSlice::range_fits(
218                output.len(),
219                output_index,
220                count
221            ),
222            "unchecked copy destination range exceeds output buffer",
223        );
224        unsafe {
225            qubit_io::UncheckedSlice::copy_nonoverlapping(
226                unread,
227                0,
228                output,
229                output_index,
230                count,
231            );
232        }
233    }
234
235    /// Consumes this adapter and returns its parts.
236    ///
237    /// # Returns
238    ///
239    /// The wrapped input and the buffer holding unread units.
240    #[must_use]
241    #[inline]
242    pub fn into_parts(self) -> (I, Buffer<I::Item>) {
243        self.input.into_parts()
244    }
245
246    /// Reads buffered units into an indexed output range.
247    ///
248    /// # Parameters
249    ///
250    /// * `output` - Destination unit storage.
251    /// * `output_index` - Start index inside `output`.
252    /// * `count` - Maximum number of units to read.
253    ///
254    /// # Returns
255    ///
256    /// The number of units copied into `output`.
257    ///
258    /// # Errors
259    ///
260    /// Returns input or buffer validation errors from the wrapped
261    /// [`qubit_io::BufferedInput`].
262    ///
263    /// # Safety
264    ///
265    /// The caller must guarantee that `output_index..output_index + count` is
266    /// a valid range inside `output` and that the addition does not overflow.
267    #[inline(always)]
268    pub unsafe fn read_unchecked(
269        &mut self,
270        output: &mut [I::Item],
271        output_index: usize,
272        count: usize,
273    ) -> Result<usize> {
274        // SAFETY: The caller guarantees the destination range is valid.
275        unsafe { self.input.read_unchecked(output, output_index, count) }
276    }
277
278    /// Decodes values into an indexed output range using a [`Codec`].
279    ///
280    /// # Parameters
281    ///
282    /// * `decoder` - Codec used for this operation.
283    /// * `map_error` - Function mapping decoder errors into I/O errors.
284    /// * `output` - Destination value storage.
285    /// * `output_index` - Start index inside `output`.
286    /// * `count` - Maximum number of values to write.
287    ///
288    /// # Returns
289    ///
290    /// The number of values written. If EOF occurs before
291    /// [`Codec::min_units_per_value`] units are available for the next value,
292    /// the incomplete tail is left buffered and `Ok(written)` is returned.
293    ///
294    /// # Errors
295    ///
296    /// Returns input errors, buffer refill errors, or decoder errors mapped by
297    /// `map_error`.
298    ///
299    /// # Safety
300    ///
301    /// The caller must guarantee that `output_index..output_index + count` is
302    /// a valid range inside `output` and that the addition does not overflow.
303    pub unsafe fn decode_into<C, M>(
304        &mut self,
305        decoder: &mut C,
306        map_error: &mut M,
307        output: &mut [C::Value],
308        output_index: usize,
309        count: usize,
310    ) -> Result<usize>
311    where
312        C: Codec<Unit = I::Item>,
313        M: FnMut(C::DecodeError) -> Error,
314    {
315        debug_assert!(
316            qubit_io::UncheckedSlice::range_fits(
317                output.len(),
318                output_index,
319                count
320            ),
321            "unchecked decoded output range exceeds destination buffer",
322        );
323        if count == 0 {
324            return Ok(0);
325        }
326        assert_unit_bounds::<C>(decoder);
327        let min_units = decoder.min_units_per_value().get();
328        let max_units = decoder.max_units_per_value().get();
329        let mut written_total = 0;
330
331        while written_total < count {
332            if self.input.available() < min_units
333                && !self.input.fill_until(min_units)?
334            {
335                return Ok(written_total);
336            }
337
338            if self.input.available() < max_units
339                && max_units <= self.input.capacity()
340            {
341                let _ = self.input.fill_until(max_units)?;
342            }
343
344            let available = self.input.available();
345            let (value, consumed) = unsafe {
346                // SAFETY: The unread window contains at least
347                // `min_units_per_value` units from index zero.
348                decoder.decode(self.input.unread(), 0)
349            }
350            .map_err(&mut *map_error)?;
351            let consumed = consumed.get();
352            assert!(
353                consumed <= available,
354                "Codec::decode consumed beyond available input",
355            );
356            output[output_index + written_total] = value;
357            unsafe {
358                // SAFETY: The codec-reported consumed count was checked
359                // against the current unread input window.
360                self.input.consume(consumed);
361            }
362            written_total += 1;
363        }
364        Ok(written_total)
365    }
366
367    /// Decodes values into an indexed output range using a streaming
368    /// [`Transcoder`].
369    ///
370    /// # Parameters
371    ///
372    /// * `decoder` - Streaming decoder used for this operation.
373    /// * `map_error` - Function mapping decoder errors into I/O errors.
374    /// * `output` - Destination value storage.
375    /// * `output_index` - Start index inside `output`.
376    /// * `count` - Maximum number of values to write.
377    ///
378    /// # Returns
379    ///
380    /// The number of values written. Incomplete EOF tails are left buffered
381    /// and reported as `Ok(written)`, so callers can apply their own EOF
382    /// policy.
383    ///
384    /// # Errors
385    ///
386    /// Returns input errors, capacity errors from the internal buffer, or
387    /// decoder errors mapped by `map_error`.
388    ///
389    /// # Safety
390    ///
391    /// The caller must guarantee that `output_index..output_index + count` is
392    /// a valid range inside `output` and that the addition does not overflow.
393    pub unsafe fn transcode_into<D, M, Value>(
394        &mut self,
395        decoder: &mut D,
396        map_error: &mut M,
397        output: &mut [Value],
398        output_index: usize,
399        count: usize,
400    ) -> Result<usize>
401    where
402        D: Transcoder<I::Item, Value>,
403        M: FnMut(TranscodeError<D::Error>) -> Error,
404    {
405        debug_assert!(
406            qubit_io::UncheckedSlice::range_fits(
407                output.len(),
408                output_index,
409                count
410            ),
411            "unchecked decoded output range exceeds destination buffer",
412        );
413        if count == 0 {
414            return Ok(0);
415        }
416        let output_end = output_index + count;
417        let output = &mut output[..output_end];
418        let mut written_total = 0;
419        loop {
420            if self.input.available() == 0 && !self.input.fill_more()? {
421                return Ok(written_total);
422            }
423            let units = self.input.unread();
424            let available_input = units.len();
425            let remaining_output = count - written_total;
426            let progress = decoder
427                .transcode(units, 0, output, output_index + written_total)
428                .map_err(&mut *map_error)?;
429            let consumed = progress.read();
430            let written = progress.written();
431            if consumed > available_input {
432                return Err(Error::new(
433                    ErrorKind::InvalidData,
434                    "transcoder consumed beyond available input",
435                ));
436            }
437            if written > remaining_output {
438                return Err(Error::new(
439                    ErrorKind::InvalidData,
440                    "transcoder wrote beyond output range",
441                ));
442            }
443            // SAFETY: The decoder reported consumed units from the currently
444            // unread input window, and the count was validated above.
445            unsafe {
446                self.input.consume(consumed);
447            }
448            written_total += written;
449            match progress.status() {
450                TranscodeStatus::Complete => {
451                    if written_total == count || consumed == 0 {
452                        return Ok(written_total);
453                    }
454                }
455                TranscodeStatus::NeedOutput {
456                    output_index: status_output_index,
457                    ..
458                } => {
459                    if status_output_index != output_index + written_total {
460                        return Err(Error::new(
461                            ErrorKind::InvalidData,
462                            "transcoder reported inconsistent NeedOutput index",
463                        ));
464                    }
465                    return Ok(written_total);
466                }
467                TranscodeStatus::NeedInput {
468                    input_index,
469                    additional,
470                    available,
471                    ..
472                } => {
473                    if input_index != consumed {
474                        return Err(Error::new(
475                            ErrorKind::InvalidData,
476                            "transcoder reported inconsistent NeedInput index",
477                        ));
478                    }
479                    let required = available
480                        .checked_add(additional.get())
481                        .ok_or_else(|| {
482                            Error::new(
483                                ErrorKind::InvalidData,
484                                "transcoder input requirement overflowed",
485                            )
486                        })?;
487                    if self.input.fill_until(required)? {
488                        continue;
489                    }
490                    return Ok(written_total);
491                }
492            }
493        }
494    }
495
496    /// Finishes a streaming decoder into an indexed output range.
497    ///
498    /// # Parameters
499    ///
500    /// * `decoder` - Streaming decoder whose final output is being collected.
501    /// * `map_error` - Function mapping decoder errors into I/O errors.
502    /// * `output` - Destination value storage.
503    /// * `output_index` - Start index inside `output`.
504    /// * `count` - Maximum number of finish values to write.
505    ///
506    /// # Returns
507    ///
508    /// The number of values written by the decoder finish operation.
509    ///
510    /// # Errors
511    ///
512    /// Returns capacity or decoder finalization errors mapped to I/O errors.
513    ///
514    /// # Safety
515    ///
516    /// The caller must guarantee that `output_index..output_index + count` is
517    /// a valid range inside `output` and that the addition does not overflow.
518    pub unsafe fn finish_transcode_into<D, M, Value>(
519        &mut self,
520        decoder: &mut D,
521        map_error: &mut M,
522        output: &mut [Value],
523        output_index: usize,
524        count: usize,
525    ) -> Result<usize>
526    where
527        D: Transcoder<I::Item, Value>,
528        M: FnMut(TranscodeError<D::Error>) -> Error,
529    {
530        debug_assert!(
531            qubit_io::UncheckedSlice::range_fits(
532                output.len(),
533                output_index,
534                count
535            ),
536            "unchecked finish output range exceeds destination buffer",
537        );
538        let required = decoder
539            .max_finish_output_len()
540            .map_err(capacity_to_io_error)?;
541        TranscodeError::<core::convert::Infallible>::ensure_output_range(
542            output.len(),
543            output_index,
544            count,
545            required,
546        )
547        .map_err(transcode_contract_to_io_error)?;
548        let output_end = output_index + count;
549        let output = &mut output[..output_end];
550        let written = decoder
551            .finish(output, output_index)
552            .map_err(&mut *map_error)?;
553        assert!(written <= required, "finish wrote beyond its bound");
554        Ok(written)
555    }
556}
557
558impl<I> TranscodeDecodeInput<I>
559where
560    I: Input<Item = u8> + Seekable<Item = u8>,
561{
562    /// Seeks the wrapped byte input and discards buffered bytes after success.
563    ///
564    /// # Parameters
565    ///
566    /// * `position` - Target seek position.
567    ///
568    /// # Returns
569    ///
570    /// The new stream position reported by the wrapped input.
571    ///
572    /// # Errors
573    ///
574    /// Returns seek errors from the wrapped input.
575    #[inline]
576    pub fn seek(&mut self, position: SeekFrom) -> Result<u64> {
577        self.input.seek_to(position)
578    }
579}
580
581impl<I> Read for TranscodeDecodeInput<I>
582where
583    I: Input<Item = u8>,
584{
585    /// Reads raw bytes through the internal buffer.
586    #[inline]
587    fn read(&mut self, output: &mut [u8]) -> Result<usize> {
588        // SAFETY: The full output slice is a valid destination range.
589        unsafe { self.input.read_unchecked(output, 0, output.len()) }
590    }
591}
592
593impl<I> Seek for TranscodeDecodeInput<I>
594where
595    I: Input<Item = u8> + Seekable<Item = u8>,
596{
597    /// Seeks the wrapped byte input and discards buffered bytes after success.
598    #[inline]
599    fn seek(&mut self, position: SeekFrom) -> Result<u64> {
600        self.seek(position)
601    }
602}
603
604impl<I> fmt::Debug for TranscodeDecodeInput<I>
605where
606    I: Input,
607    I::Item: Copy + Default,
608    BufferedInput<I>: fmt::Debug,
609{
610    /// Formats this buffered decode input for debugging.
611    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
612        formatter
613            .debug_struct("TranscodeDecodeInput")
614            .field("input", &self.input)
615            .finish()
616    }
617}
618
619/// Converts a capacity planning failure into an I/O error.
620fn capacity_to_io_error(error: crate::CapacityError) -> Error {
621    Error::new(ErrorKind::InvalidData, error)
622}
623
624/// Converts a framework transcode contract failure into an I/O error.
625fn transcode_contract_to_io_error(
626    error: TranscodeError<core::convert::Infallible>,
627) -> Error {
628    Error::new(ErrorKind::InvalidData, error)
629}