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