Skip to main content

qubit_io/traits/
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::{
10    Error,
11    ErrorKind,
12    Read,
13    Result,
14};
15
16use crate::util::UncheckedSlice;
17
18/// Minimal indexed input interface over items.
19///
20/// `Input` is intentionally smaller and lower-level than [`Read`]. It only
21/// states that an implementor can read up to `count` items into
22/// `output[index..index + count]`. The caller owns range validation so hot
23/// paths can avoid repeated slicing and bounds checks.
24///
25/// # Coherence note
26///
27/// Every [`Read`] value automatically implements `Input<Item = u8>` through the
28/// blanket impl below. Because [`Input::Item`] is an associated type rather
29/// than a trait parameter, a concrete type that implements [`Read`] cannot also
30/// provide any other direct `Input` implementation for the same type, including
31/// one with a different item type.
32///
33/// Use a wrapper/newtype when a type needs item-oriented input semantics that
34/// differ from its byte-oriented [`Read`] implementation.
35pub trait Input {
36    /// The item type read from this input.
37    type Item;
38
39    /// Returns whether this input already buffers items internally.
40    ///
41    /// # Returns
42    ///
43    /// `true` when callers should avoid wrapping this input in another generic
44    /// item buffer automatically.
45    #[inline(always)]
46    #[must_use]
47    fn is_buffered(&self) -> bool {
48        false
49    }
50
51    /// Reads items into an indexed output range without checking the range.
52    ///
53    /// # Parameters
54    ///
55    /// * `output` - Destination storage.
56    /// * `index` - Start index inside `output`.
57    /// * `count` - Maximum number of items to read.
58    ///
59    /// # Returns
60    ///
61    /// The number of items written into `output[index..index + count]`. The
62    /// value must be in `0..=count`.
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 read_unchecked(
73        &mut self,
74        output: &mut [Self::Item],
75        index: usize,
76        count: usize,
77    ) -> Result<usize>;
78
79    /// Reads items into the full output slice.
80    ///
81    /// # Parameters
82    /// - `output`: Destination storage.
83    ///
84    /// # Returns
85    /// The number of items read into `output`.
86    ///
87    /// # Errors
88    ///
89    /// Returns the input error reported by the implementation. Returns
90    /// [`ErrorKind::InvalidData`] if the implementation reports reading more
91    /// items than requested.
92    #[inline(always)]
93    fn read(&mut self, output: &mut [Self::Item]) -> Result<usize> {
94        // SAFETY: The caller ensured the destination slice is valid.
95        let read = unsafe { self.read_unchecked(output, 0, output.len()) }?;
96        validate_read_count(read, output.len())?;
97        Ok(read)
98    }
99
100    /// Reads items into an indexed output range until it is full or EOF is
101    /// reached.
102    ///
103    /// This method retries interrupted reads and treats EOF as a successful
104    /// partial result.
105    ///
106    /// # Parameters
107    ///
108    /// * `output` - Destination storage.
109    /// * `index` - Start index inside `output`.
110    /// * `count` - Maximum number of items to read.
111    ///
112    /// # Returns
113    ///
114    /// The number of items written into `output[index..index + count]`.
115    ///
116    /// # Errors
117    ///
118    /// Returns the first non-[`ErrorKind::Interrupted`] input error. Returns
119    /// [`ErrorKind::InvalidData`] if the implementation reports more items than
120    /// requested.
121    ///
122    /// # Safety
123    ///
124    /// The caller must guarantee that `index..index + count` is a valid range
125    /// inside `output` and that the addition does not overflow.
126    unsafe fn read_fully_unchecked(
127        &mut self,
128        output: &mut [Self::Item],
129        index: usize,
130        count: usize,
131    ) -> Result<usize> {
132        debug_assert!(
133            UncheckedSlice::range_fits(output.len(), index, count),
134            "unchecked read-fully range exceeds output buffer"
135        );
136        let mut total = 0;
137        while total < count {
138            let remaining = count - total;
139            // SAFETY: The caller guarantees the original destination range is
140            // valid; `total < count`, so this suffix remains inside it.
141            match unsafe {
142                self.read_unchecked(output, index + total, remaining)
143            } {
144                Ok(0) => break,
145                Ok(read) => {
146                    validate_read_count(read, remaining)?;
147                    total += read;
148                }
149                Err(error) if error.kind() == ErrorKind::Interrupted => {}
150                Err(error) => return Err(error),
151            }
152        }
153        Ok(total)
154    }
155
156    /// Reads items into the full output slice until it is full or EOF is
157    /// reached.
158    ///
159    /// # Parameters
160    /// - `output`: Destination storage to fill as far as possible.
161    ///
162    /// # Returns
163    /// The number of items written into `output`.
164    ///
165    /// # Errors
166    /// Returns the first non-interrupted input error, or
167    /// [`ErrorKind::InvalidData`] if the implementation reports an impossible
168    /// item count.
169    #[inline(always)]
170    fn read_fully(&mut self, output: &mut [Self::Item]) -> Result<usize> {
171        // SAFETY: The full output slice is a valid destination range.
172        unsafe { self.read_fully_unchecked(output, 0, output.len()) }
173    }
174}
175
176impl<R> Input for R
177where
178    R: Read + ?Sized,
179{
180    type Item = u8;
181
182    /// Reads bytes from a standard [`Read`] value into an indexed range.
183    #[inline(always)]
184    unsafe fn read_unchecked(
185        &mut self,
186        output: &mut [u8],
187        index: usize,
188        count: usize,
189    ) -> Result<usize> {
190        debug_assert!(
191            UncheckedSlice::range_fits(output.len(), index, count),
192            "unchecked read range exceeds output buffer"
193        );
194        // SAFETY: The caller guarantees that the range is valid inside
195        // `output`.
196        let target =
197            unsafe { UncheckedSlice::subslice_mut(output, index, count) };
198        Read::read(self, target)
199    }
200
201    /// Reads items into the full output slice.
202    #[inline(always)]
203    fn read(&mut self, output: &mut [Self::Item]) -> Result<usize> {
204        Read::read(self, output)
205    }
206}
207
208/// Validates an item count returned by an input.
209///
210/// # Parameters
211///
212/// * `read` - Item count reported by the input.
213/// * `requested` - Maximum item count requested from the input.
214///
215/// # Errors
216///
217/// Returns [`ErrorKind::InvalidData`] when the input reports more items than
218/// the destination range could hold.
219#[inline(always)]
220pub fn validate_read_count(read: usize, requested: usize) -> Result<()> {
221    if read > requested {
222        return Err(Error::new(
223            ErrorKind::InvalidData,
224            format!(
225                "reader reported {read} items for a {requested}-item buffer"
226            ),
227        ));
228    }
229    Ok(())
230}