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