Skip to main content

qubit_io/traits/
async_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
9use std::io::Result;
10use std::pin::Pin;
11use std::task::Context;
12use std::task::Poll;
13
14use crate::ReadExactFuture;
15use crate::ReadFullyFuture;
16use crate::ReadFuture;
17use crate::traits::normalize_async_error;
18use crate::traits::validate_read_count;
19
20/// Minimal runtime-independent asynchronous input interface over items.
21///
22/// `AsyncInput` expresses readiness through [`Poll`] and does not depend on a
23/// particular executor. Implementations may be pinned and therefore are never
24/// moved by the polling methods.
25pub trait AsyncInput {
26    /// The item type read from this input.
27    type Item;
28
29    /// Returns whether this input already buffers items internally.
30    ///
31    /// # Returns
32    ///
33    /// `true` when callers should avoid automatically adding another generic
34    /// item buffer.
35    #[inline(always)]
36    #[must_use]
37    fn is_buffered(&self) -> bool {
38        false
39    }
40
41    /// Polls an indexed read without checking the destination range.
42    ///
43    /// # Parameters
44    ///
45    /// * `cx` - Task context used to register interest when input is pending.
46    /// * `output` - Destination storage.
47    /// * `index` - Start index inside `output`.
48    /// * `count` - Maximum number of items to read.
49    ///
50    /// # Returns
51    ///
52    /// [`Poll::Pending`] when no result is currently available, or a ready I/O
53    /// result containing a count in `0..=count`. A ready zero count denotes
54    /// end of input when `count` is nonzero. A zero `count` must immediately
55    /// return `Poll::Ready(Ok(0))`.
56    ///
57    /// Before returning [`Poll::Pending`], the implementation must arrange for
58    /// `cx`'s waker to be notified when progress may be possible. Neither
59    /// `Poll::Pending` nor `Poll::Ready(Err(_))` may transfer items.
60    /// `WouldBlock` and `Interrupted` must not cross this asynchronous
61    /// boundary; implementations must respectively register readiness or retry
62    /// internally.
63    ///
64    /// # Errors
65    ///
66    /// Returns the input error reported by the implementation.
67    ///
68    /// # Safety
69    ///
70    /// The caller must guarantee that `index..index + count` is a valid range
71    /// inside `output` and that the addition does not overflow.
72    unsafe fn poll_read_unchecked(
73        self: Pin<&mut Self>,
74        cx: &mut Context<'_>,
75        output: &mut [Self::Item],
76        index: usize,
77        count: usize,
78    ) -> Poll<Result<usize>>;
79
80    /// Polls a read into the full destination slice.
81    ///
82    /// # Parameters
83    ///
84    /// * `cx` - Task context used to register interest when input is pending.
85    /// * `output` - Destination storage.
86    ///
87    /// # Returns
88    ///
89    /// [`Poll::Pending`] or a ready result containing the number of items read.
90    ///
91    /// # Errors
92    ///
93    /// Returns the implementation's input error. Returns
94    /// [`std::io::ErrorKind::InvalidData`] if the implementation reports more
95    /// items than requested.
96    #[inline]
97    fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, output: &mut [Self::Item]) -> Poll<Result<usize>> {
98        if output.is_empty() {
99            return Poll::Ready(Ok(0));
100        }
101        let requested = output.len();
102        // SAFETY: The full output slice is a valid destination range.
103        match unsafe { self.poll_read_unchecked(cx, output, 0, requested) } {
104            Poll::Ready(Ok(read)) => Poll::Ready(validate_read_count(read, requested).map(|()| read)),
105            Poll::Ready(Err(error)) => Poll::Ready(Err(normalize_async_error(error))),
106            Poll::Pending => Poll::Pending,
107        }
108    }
109
110    /// Creates a future that performs one asynchronous read operation.
111    ///
112    /// # Type Parameters
113    ///
114    /// * `'a` - Shared lifetime of the input borrow and destination slice.
115    ///
116    /// # Parameters
117    ///
118    /// * `output` - Destination storage.
119    ///
120    /// # Returns
121    ///
122    /// A future that resolves with the number of items read.
123    #[inline(always)]
124    fn read_async<'a>(&'a mut self, output: &'a mut [Self::Item]) -> ReadFuture<'a, Self>
125    where
126        Self: Sized + Unpin,
127    {
128        ReadFuture::new(Pin::new(self), output)
129    }
130
131    /// Creates a future that fills a destination as far as possible.
132    ///
133    /// The returned future stops when the destination is full or the input
134    /// reports EOF.
135    ///
136    /// # Type Parameters
137    ///
138    /// * `'a` - Shared lifetime of the input borrow and destination slice.
139    ///
140    /// # Parameters
141    ///
142    /// * `output` - Destination storage.
143    ///
144    /// # Returns
145    ///
146    /// A future that resolves with the total number of items read.
147    #[inline(always)]
148    fn read_fully_async<'a>(&'a mut self, output: &'a mut [Self::Item]) -> ReadFullyFuture<'a, Self>
149    where
150        Self: Sized + Unpin,
151    {
152        ReadFullyFuture::new(Pin::new(self), output)
153    }
154
155    /// Creates a future that fills the entire destination.
156    ///
157    /// The returned future reports [`std::io::ErrorKind::UnexpectedEof`] if
158    /// the input ends before the destination is full.
159    ///
160    /// # Type Parameters
161    ///
162    /// * `'a` - Shared lifetime of the input borrow and destination slice.
163    ///
164    /// # Parameters
165    ///
166    /// * `output` - Destination storage that must be filled completely.
167    ///
168    /// # Returns
169    ///
170    /// A future that resolves after filling `output` or encountering an error.
171    #[inline(always)]
172    fn read_exactly_async<'a>(&'a mut self, output: &'a mut [Self::Item]) -> ReadExactFuture<'a, Self>
173    where
174        Self: Sized + Unpin,
175    {
176        ReadExactFuture::new(Pin::new(self), output)
177    }
178}