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