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}