Skip to main content

qubit_codec/transcode/io/
async_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 asynchronous input driver that decodes units into values.
9
10use core::fmt;
11use std::collections::TryReserveError;
12use std::future::poll_fn;
13use std::io::Error;
14use std::io::ErrorKind;
15use std::io::Result;
16use std::pin::Pin;
17use std::task::Context;
18use std::task::Poll;
19
20use qubit_io::AsyncBufferedInput;
21use qubit_io::AsyncInput;
22use qubit_io::Buffer;
23use qubit_utils::SliceRange;
24use qubit_utils::allocation_error;
25
26use super::async_transcode_decode_step::AsyncTranscodeDecodeStep;
27use super::transcode_progress_validation::validate_decode_progress;
28use crate::CapacityError;
29use crate::TranscodeProgress;
30use crate::Transcoder;
31
32/// Decodes an asynchronous unit stream into values.
33///
34/// The adapter owns unit buffering but not decoder state. Callers supply a
35/// streaming decoder and error mapper for each operation.
36///
37/// # Type Parameters
38///
39/// - `I`: Wrapped asynchronous unit input.
40///
41/// # Examples
42///
43/// ```
44/// use qubit_codec::AsyncTranscodeDecodeInput;
45/// use qubit_io::AsyncInput;
46///
47/// # use std::io::Result;
48/// # use std::pin::Pin;
49/// # use std::task::Context;
50/// # use std::task::Poll;
51/// # struct EmptyInput;
52/// # impl AsyncInput for EmptyInput {
53/// #     type Item = u8;
54/// #     unsafe fn poll_read_unchecked(self: Pin<&mut Self>, _cx: &mut Context<'_>, _output: &mut [u8], _index: usize, _count: usize) -> Poll<Result<usize>> {
55/// #         Poll::Ready(Ok(0))
56/// #     }
57/// # }
58/// let mut input = AsyncTranscodeDecodeInput::with_capacity(EmptyInput, 8);
59/// assert!(input.capacity() >= 8);
60/// assert_eq!(input.unread_len(), 0);
61/// input.consume(0);
62/// assert!(input.unread().is_empty());
63/// ```
64#[must_use]
65pub struct AsyncTranscodeDecodeInput<I>
66where
67    I: AsyncInput,
68    I::Item: Copy + Default,
69{
70    /// Buffered asynchronous unit input.
71    input: AsyncBufferedInput<I>,
72}
73
74impl<I> AsyncTranscodeDecodeInput<I>
75where
76    I: AsyncInput,
77    I::Item: Copy + Default,
78{
79    /// Creates an adapter with the default unit-buffer capacity.
80    ///
81    /// # Parameters
82    ///
83    /// - `inner`: Unit input read by this adapter.
84    ///
85    /// # Returns
86    ///
87    /// Returns a new buffered asynchronous decoder input.
88    pub fn new(inner: I) -> Self {
89        Self {
90            input: AsyncBufferedInput::new(inner),
91        }
92    }
93
94    /// Creates an adapter with an internal unit buffer of at least capacity.
95    ///
96    /// # Parameters
97    ///
98    /// - `inner`: Unit input read by this adapter.
99    /// - `capacity`: Requested internal unit-buffer capacity.
100    ///
101    /// # Returns
102    ///
103    /// Returns a new buffered asynchronous decoder input.
104    pub fn with_capacity(inner: I, capacity: usize) -> Self {
105        Self {
106            input: AsyncBufferedInput::with_capacity(inner, capacity),
107        }
108    }
109
110    /// Tries to create an adapter with an internal unit buffer of at least
111    /// capacity.
112    ///
113    /// # Parameters
114    ///
115    /// - `inner`: Asynchronous unit source owned by the adapter.
116    /// - `capacity`: Minimum requested unit-buffer capacity.
117    ///
118    /// # Returns
119    ///
120    /// The adapter with an empty unread window on successful allocation.
121    ///
122    /// # Errors
123    ///
124    /// Returns the allocation error when the internal buffer cannot be
125    /// allocated.
126    pub fn try_with_capacity(inner: I, capacity: usize) -> std::result::Result<Self, TryReserveError> {
127        Ok(Self {
128            input: AsyncBufferedInput::try_with_capacity(inner, capacity)?,
129        })
130    }
131
132    /// Returns a shared reference to the wrapped asynchronous input.
133    ///
134    /// The input can be physically positioned after units retained in this
135    /// adapter's unread buffer.
136    ///
137    /// # Returns
138    ///
139    /// A shared borrow of the underlying source, excluding buffered units.
140    #[must_use]
141    #[inline]
142    pub const fn inner(&self) -> &I {
143        self.input.inner()
144    }
145
146    /// Returns a mutable reference to the wrapped asynchronous input.
147    ///
148    /// Direct reads can invalidate the logical stream position represented by
149    /// unread buffered units.
150    ///
151    /// # Returns
152    ///
153    /// An exclusive borrow of the source; direct reads bypass buffered units.
154    #[must_use]
155    #[inline]
156    pub fn inner_mut(&mut self) -> &mut I {
157        self.input.inner_mut()
158    }
159
160    /// Returns the number of unread units currently buffered.
161    ///
162    /// # Returns
163    ///
164    /// The number of buffered units not yet consumed.
165    #[must_use]
166    #[inline]
167    pub const fn unread_len(&self) -> usize {
168        self.input.unread_len()
169    }
170
171    /// Returns the unread buffered unit window.
172    ///
173    /// # Returns
174    ///
175    /// The currently retained units borrowed without allocation.
176    #[must_use]
177    #[inline]
178    pub fn unread(&self) -> &[I::Item] {
179        self.input.unread()
180    }
181
182    /// Returns the total internal unit-buffer capacity.
183    ///
184    /// # Returns
185    ///
186    /// The total number of units the internal buffer can hold.
187    #[must_use]
188    #[inline]
189    pub fn capacity(&self) -> usize {
190        self.input.capacity()
191    }
192
193    /// Consumes unread units from the current buffer window.
194    ///
195    /// # Parameters
196    ///
197    /// - `count`: Number of retained units to discard from the unread prefix.
198    ///
199    /// # Panics
200    ///
201    /// Panics when count exceeds the unread unit count.
202    #[inline]
203    pub fn consume(&mut self, count: usize) {
204        assert!(count <= self.unread_len(), "cannot consume beyond buffered input",);
205        // SAFETY: The asserted bound proves count fits the unread window.
206        unsafe {
207            self.input.consume(count);
208        }
209    }
210
211    /// Copies unread units into an indexed output range without consuming them.
212    ///
213    /// # Parameters
214    ///
215    /// - `output`: Destination slice receiving copies of retained units.
216    /// - `output_index`: First destination index.
217    /// - `count`: Number of unread units to copy without consuming them.
218    ///
219    /// # Safety
220    ///
221    /// The caller must guarantee that the indexed range fits in output, does
222    /// not overflow, holds no overlapping unread units, and count is no
223    /// greater than the unread unit count.
224    pub unsafe fn copy_unread_to(&self, output: &mut [I::Item], output_index: usize, count: usize) {
225        // SAFETY: The caller upholds the delegated buffer-copy contract.
226        unsafe {
227            self.input.copy_unread_to(output, output_index, count);
228        }
229    }
230
231    /// Consumes this adapter and returns the input plus its unread buffer.
232    ///
233    /// # Returns
234    ///
235    /// The owned source and buffer, preserving all unread units.
236    #[must_use = "the returned input and unread buffer must be handled"]
237    #[inline]
238    pub fn into_parts(self) -> (I, Buffer<I::Item>) {
239        self.input.into_parts()
240    }
241
242    /// Runs decoder reset into an indexed output range without I/O.
243    ///
244    /// # Type Parameters
245    ///
246    /// - `D`: Stateful decoder from source units into values.
247    /// - `M`: Callback mapping decoder errors to I/O errors.
248    /// - `Value`: Destination value type.
249    ///
250    /// # Parameters
251    ///
252    /// - `decoder`: Decoder whose state may change during this operation.
253    /// - `map_error`: Callback invoked for decoder errors.
254    /// - `output`: Destination value slice.
255    /// - `output_index`: First destination index.
256    /// - `count`: Number of writable destination positions.
257    ///
258    /// # Returns
259    ///
260    /// Number of values emitted during decoder reset.
261    ///
262    /// # Errors
263    ///
264    /// Returns invalid output-range, capacity, or mapped decoder-reset errors.
265    ///
266    /// # Panics
267    ///
268    /// Panics if the decoder writes more values than its reset bound.
269    pub fn reset<D, M, Value>(
270        &self,
271        decoder: &mut D,
272        map_error: &mut M,
273        output: &mut [Value],
274        output_index: usize,
275        count: usize,
276    ) -> Result<usize>
277    where
278        D: Transcoder<Input = I::Item, Output = Value>,
279        M: FnMut(D::Error) -> Error,
280    {
281        let required = decoder.max_reset_output_len().map_err(capacity_to_io_error)?;
282        let output_end = SliceRange::checked_range_end(
283            output.len(),
284            output_index,
285            count,
286            "reset output range exceeds destination buffer",
287        )?;
288        if count < required {
289            return Err(Error::new(
290                ErrorKind::InvalidData,
291                "insufficient output for decoder reset bound",
292            ));
293        }
294        let output = &mut output[..output_end];
295        let written = decoder.reset(output, output_index).map_err(&mut *map_error)?;
296        assert!(written <= required, "reset wrote beyond its bound");
297        Ok(written)
298    }
299
300    /// Finishes a decoder into an indexed output range without I/O.
301    ///
302    /// # Type Parameters
303    ///
304    /// - `D`: Stateful decoder from source units into values.
305    /// - `M`: Callback mapping decoder errors to I/O errors.
306    /// - `Value`: Destination value type.
307    ///
308    /// # Parameters
309    ///
310    /// - `decoder`: Decoder whose state may change during this operation.
311    /// - `map_error`: Callback invoked for decoder errors.
312    /// - `output`: Destination value slice.
313    /// - `output_index`: First destination index.
314    /// - `count`: Number of writable destination positions.
315    ///
316    /// # Returns
317    ///
318    /// Number of values emitted while finishing the decoder.
319    ///
320    /// # Errors
321    ///
322    /// Returns invalid output-range, capacity, or mapped decoder-finish
323    /// errors.
324    ///
325    /// # Panics
326    ///
327    /// Panics if the decoder writes more values than its finish bound.
328    pub fn finish<D, M, Value>(
329        &self,
330        decoder: &mut D,
331        map_error: &mut M,
332        output: &mut [Value],
333        output_index: usize,
334        count: usize,
335    ) -> Result<usize>
336    where
337        D: Transcoder<Input = I::Item, Output = Value>,
338        M: FnMut(D::Error) -> Error,
339    {
340        let required = decoder.max_finish_output_len().map_err(capacity_to_io_error)?;
341        let output_end = SliceRange::checked_range_end(
342            output.len(),
343            output_index,
344            count,
345            "finish output range exceeds destination buffer",
346        )?;
347        if count < required {
348            return Err(Error::new(
349                ErrorKind::InvalidData,
350                "insufficient output for decoder finish bound",
351            ));
352        }
353        let output = &mut output[..output_end];
354        let written = decoder.finish(output, output_index).map_err(&mut *map_error)?;
355        assert!(written <= required, "finish wrote beyond its bound");
356        Ok(written)
357    }
358}
359
360impl<I> AsyncTranscodeDecodeInput<I>
361where
362    I: AsyncInput + Unpin,
363    I::Item: Copy + Default + Unpin,
364{
365    /// Appends at least one unit to the unread buffer.
366    ///
367    /// # Returns
368    ///
369    /// Returns true when data was appended, or false at end of input.
370    ///
371    /// # Errors
372    ///
373    /// Returns input or buffer-management errors while refilling.
374    pub async fn fill_more_async(&mut self) -> Result<bool> {
375        self.input.fill_more_async().await
376    }
377
378    /// Refills until at least count unread units are available.
379    ///
380    /// # Parameters
381    ///
382    /// - `count`: Minimum desired unread unit count; grows the buffer if
383    ///   needed.
384    ///
385    /// # Returns
386    ///
387    /// Returns true when count units are available, or false when EOF occurs
388    /// first.
389    ///
390    /// # Errors
391    ///
392    /// Returns allocation, input, or buffer-management errors while refilling.
393    pub async fn fill_until_async(&mut self, count: usize) -> Result<bool> {
394        if count > self.input.capacity() {
395            self.input.try_reserve_capacity(count).map_err(allocation_error)?;
396        }
397        self.input.fill_until_async(count).await
398    }
399
400    /// Polls one cancellation-safe decoder operation.
401    ///
402    /// The operation first refills only when no unread unit is buffered. After
403    /// a decoder call changes either the decoder or the buffered input, it
404    /// immediately returns [`AsyncTranscodeDecodeStep::Progress`] and never
405    /// polls the input again. This makes the returned progress the commit
406    /// boundary for cancellation: resume with the adapter's current state,
407    /// rather than replaying the previous source range.
408    ///
409    /// # Type Parameters
410    ///
411    /// - `D`: Stateful decoder from source units into values.
412    /// - `M`: Callback mapping decoder errors to I/O errors.
413    /// - `Value`: Destination value type.
414    ///
415    /// # Parameters
416    ///
417    /// - `cx`: Task context used when polling the source.
418    /// - `decoder`: Decoder whose state may change during this operation.
419    /// - `map_error`: Callback invoked for decoder errors.
420    /// - `output`: Destination value slice.
421    /// - `output_index`: First destination index.
422    /// - `count`: Number of writable destination positions.
423    ///
424    /// # Returns
425    ///
426    /// `Pending` only while awaiting source input before decoding; otherwise
427    /// ready progress or end-of-input. Successful progress has already
428    /// consumed source units.
429    ///
430    /// # Errors
431    ///
432    /// Returns input, allocation, output-range, invalid-progress, or mapped
433    /// decoder errors.
434    pub fn poll_transcode<D, M, Value>(
435        mut self: Pin<&mut Self>,
436        cx: &mut Context<'_>,
437        decoder: &mut D,
438        map_error: &mut M,
439        output: &mut [Value],
440        output_index: usize,
441        count: usize,
442    ) -> Poll<Result<AsyncTranscodeDecodeStep>>
443    where
444        D: Transcoder<Input = I::Item, Output = Value>,
445        M: FnMut(D::Error) -> Error,
446    {
447        let output_end = SliceRange::checked_range_end(
448            output.len(),
449            output_index,
450            count,
451            "decoded output range exceeds destination buffer",
452        )?;
453        if count == 0 {
454            return Poll::Ready(Ok(AsyncTranscodeDecodeStep::Progress(TranscodeProgress::complete(
455                0, 0,
456            ))));
457        }
458        let output = &mut output[..output_end];
459        if self.as_ref().get_ref().unread_len() == 0 {
460            let this = self.as_mut().get_mut();
461            match Pin::new(&mut this.input).poll_fill_more(cx) {
462                Poll::Ready(Ok(true)) => {}
463                Poll::Ready(Ok(false)) => {
464                    return Poll::Ready(Ok(AsyncTranscodeDecodeStep::EndOfInput));
465                }
466                Poll::Ready(Err(error)) => return Poll::Ready(Err(error)),
467                Poll::Pending => return Poll::Pending,
468            }
469        }
470        let this = self.as_mut().get_mut();
471        let available_input = this.unread_len();
472        let progress = decoder
473            .transcode(this.unread(), 0, output, output_index)
474            .map_err(&mut *map_error)
475            .and_then(|progress| validate_decode_progress(progress, 0, available_input, output_index, count));
476        let progress = match progress {
477            Ok(progress) => progress,
478            Err(error) => return Poll::Ready(Err(error)),
479        };
480        this.consume(progress.read());
481        Poll::Ready(Ok(AsyncTranscodeDecodeStep::Progress(progress)))
482    }
483
484    /// Performs one decode step after the caller has established EOF.
485    ///
486    /// This method performs no I/O. It passes the current unread buffer to
487    /// [`Transcoder::transcode_eof`], validates progress, and commits consumed
488    /// source units before returning.
489    ///
490    /// # Type Parameters
491    ///
492    /// - `D`: Stateful decoder from source units into values.
493    /// - `M`: Callback mapping decoder errors to I/O errors.
494    /// - `Value`: Destination value type.
495    ///
496    /// # Parameters
497    ///
498    /// - `decoder`: Decoder whose state may change during this operation.
499    /// - `map_error`: Callback invoked for decoder errors.
500    /// - `output`: Destination value slice.
501    /// - `output_index`: First destination index.
502    /// - `count`: Number of writable destination positions.
503    ///
504    /// # Returns
505    ///
506    /// Validated progress with source consumption committed; zero progress when
507    /// the output range or unread input is empty.
508    ///
509    /// # Errors
510    ///
511    /// Returns invalid output-range errors, or decoder errors mapped by
512    /// `map_error`, and invalid-progress errors from contract validation.
513    pub fn transcode_eof_step<D, M, Value>(
514        &mut self,
515        decoder: &mut D,
516        map_error: &mut M,
517        output: &mut [Value],
518        output_index: usize,
519        count: usize,
520    ) -> Result<TranscodeProgress>
521    where
522        D: Transcoder<Input = I::Item, Output = Value>,
523        M: FnMut(D::Error) -> Error,
524    {
525        let output_end = SliceRange::checked_range_end(
526            output.len(),
527            output_index,
528            count,
529            "decoded EOF output range exceeds destination buffer",
530        )?;
531        if count == 0 {
532            return Ok(TranscodeProgress::complete(0, 0));
533        }
534        if self.unread_len() == 0 {
535            return Ok(TranscodeProgress::complete(0, 0));
536        }
537        let available_input = self.unread_len();
538        let progress = decoder
539            .transcode_eof(self.unread(), 0, &mut output[..output_end], output_index)
540            .map_err(&mut *map_error)
541            .and_then(|progress| validate_decode_progress(progress, 0, available_input, output_index, count))?;
542        self.consume(progress.read());
543        Ok(progress)
544    }
545
546    /// Decodes one cancellation-safe progress step into an indexed output
547    /// range.
548    ///
549    /// This is the async wrapper for [`Self::poll_transcode`]. It returns after
550    /// one decoder invocation; callers that require a complete destination
551    /// range must drive successive steps and retain each returned progress.
552    ///
553    /// # Type Parameters
554    ///
555    /// - `D`: Stateful decoder from source units into values.
556    /// - `M`: Callback mapping decoder errors to I/O errors.
557    /// - `Value`: Destination value type.
558    ///
559    /// # Parameters
560    ///
561    /// - `decoder`: Decoder whose state may change during this operation.
562    /// - `map_error`: Callback invoked for decoder errors.
563    /// - `output`: Destination value slice.
564    /// - `output_index`: First destination index.
565    /// - `count`: Number of writable destination positions.
566    ///
567    /// # Returns
568    ///
569    /// One committed progress step, or end-of-input when the source is
570    /// exhausted.
571    ///
572    /// # Errors
573    ///
574    /// Returns input, allocation, output-range, invalid-progress, or mapped
575    /// decoder errors from `poll_transcode`.
576    pub async fn transcode_async<D, M, Value>(
577        &mut self,
578        decoder: &mut D,
579        map_error: &mut M,
580        output: &mut [Value],
581        output_index: usize,
582        count: usize,
583    ) -> Result<AsyncTranscodeDecodeStep>
584    where
585        D: Transcoder<Input = I::Item, Output = Value>,
586        M: FnMut(D::Error) -> Error,
587    {
588        poll_fn(|cx| Pin::new(&mut *self).poll_transcode(cx, decoder, map_error, output, output_index, count)).await
589    }
590}
591
592impl<I> AsyncInput for AsyncTranscodeDecodeInput<I>
593where
594    I: AsyncInput,
595    I::Item: Copy + Default,
596{
597    /// Item type read through the retained unit buffer.
598    type Item = I::Item;
599
600    /// Reports that this input retains unread units.
601    ///
602    /// # Returns
603    ///
604    /// Always `true`, because this adapter retains unread units.
605    #[inline]
606    fn is_buffered(&self) -> bool {
607        true
608    }
609
610    /// Polls one read through the retained unit buffer.
611    ///
612    /// # Parameters
613    ///
614    /// - `cx`: Task context used to poll the buffered source.
615    /// - `output`: Destination unit slice.
616    /// - `index`: First destination index.
617    /// - `count`: Maximum number of units to read.
618    ///
619    /// # Returns
620    ///
621    /// `Pending` while awaiting source data, or a ready read count; zero
622    /// indicates EOF.
623    ///
624    /// # Errors
625    ///
626    /// Returns errors from the buffered source or its buffer management.
627    ///
628    /// # Safety
629    ///
630    /// The caller must provide a valid indexed output range.
631    unsafe fn poll_read_unchecked(
632        mut self: Pin<&mut Self>,
633        cx: &mut Context<'_>,
634        output: &mut [Self::Item],
635        index: usize,
636        count: usize,
637    ) -> Poll<Result<usize>> {
638        // SAFETY: Pinning this wrapper pins its buffered input field.
639        let input = unsafe { Pin::new_unchecked(&mut self.as_mut().get_unchecked_mut().input) };
640        // SAFETY: The caller upholds the delegated indexed-read contract.
641        unsafe { input.poll_read_unchecked(cx, output, index, count) }
642    }
643}
644
645impl<I> fmt::Debug for AsyncTranscodeDecodeInput<I>
646where
647    I: AsyncInput,
648    I::Item: Copy + Default,
649    AsyncBufferedInput<I>: fmt::Debug,
650{
651    /// Formats this asynchronous decoder input for debugging.
652    ///
653    /// # Parameters
654    ///
655    /// - `formatter`: Debug output destination.
656    ///
657    /// # Returns
658    ///
659    /// The result of formatting the buffered input.
660    ///
661    /// # Errors
662    ///
663    /// Returns a formatting error if the destination rejects output.
664    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
665        formatter
666            .debug_struct("AsyncTranscodeDecodeInput")
667            .field("input", &self.input)
668            .finish()
669    }
670}
671
672/// Converts decoder capacity failures into invalid stream data.
673///
674/// # Parameters
675///
676/// - `error`: Capacity failure retained as the I/O error payload.
677///
678/// # Returns
679///
680/// An invalid-data error owning the original capacity failure.
681fn capacity_to_io_error(error: CapacityError) -> Error {
682    Error::new(ErrorKind::InvalidData, error)
683}