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}