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.
33pub trait Input {
34    /// The item type read from this input.
35    type Item;
36
37    /// Returns whether this input already buffers items internally.
38    ///
39    /// # Returns
40    ///
41    /// `true` when callers should avoid wrapping this input in another generic
42    /// item buffer automatically.
43    #[inline(always)]
44    #[must_use]
45    fn is_buffered(&self) -> bool {
46        false
47    }
48
49    /// Reads items into an indexed output range without checking the range.
50    ///
51    /// # Parameters
52    ///
53    /// * `output` - Destination storage.
54    /// * `index` - Start index inside `output`.
55    /// * `count` - Maximum number of items to read.
56    ///
57    /// # Returns
58    ///
59    /// The number of items written into `output[index..index + count]`. The
60    /// value must be in `0..=count`.
61    ///
62    /// # Errors
63    ///
64    /// Returns the input error reported by the implementation.
65    ///
66    /// # Safety
67    ///
68    /// The caller must guarantee that `index..index + count` is a valid range
69    /// inside `output` and that the addition does not overflow.
70    unsafe fn read_unchecked(&mut self, output: &mut [Self::Item], index: usize, count: usize) -> Result<usize>;
71
72    /// Reads items into the full output slice.
73    ///
74    /// # Parameters
75    /// - `output`: Destination storage.
76    ///
77    /// # Returns
78    /// The number of items read into `output`.
79    ///
80    /// # Errors
81    ///
82    /// Returns the input error reported by the implementation. Returns
83    /// [`ErrorKind::InvalidData`] if the implementation reports reading more
84    /// items than requested.
85    #[inline(always)]
86    fn read(&mut self, output: &mut [Self::Item]) -> Result<usize> {
87        // SAFETY: The caller ensured the destination slice is valid.
88        let read = unsafe { self.read_unchecked(output, 0, output.len()) }?;
89        validate_read_count(read, output.len())?;
90        Ok(read)
91    }
92
93    /// Reads items into an indexed output range until it is full or EOF is
94    /// reached.
95    ///
96    /// This method retries interrupted reads and treats EOF as a successful
97    /// partial result.
98    ///
99    /// # Parameters
100    ///
101    /// * `output` - Destination storage.
102    /// * `index` - Start index inside `output`.
103    /// * `count` - Maximum number of items to read.
104    ///
105    /// # Returns
106    ///
107    /// The number of items written into `output[index..index + count]`.
108    ///
109    /// # Errors
110    ///
111    /// Returns the first non-[`ErrorKind::Interrupted`] input error. Returns
112    /// [`ErrorKind::InvalidData`] if the implementation reports more items than
113    /// requested.
114    ///
115    /// # Panics
116    ///
117    /// Panics in debug builds if the requested output range does not fit.
118    ///
119    /// # Safety
120    ///
121    /// The caller must guarantee that `index..index + count` is a valid range
122    /// inside `output` and that the addition does not overflow.
123    unsafe fn read_fully_unchecked(&mut self, output: &mut [Self::Item], index: usize, count: usize) -> Result<usize> {
124        debug_assert!(
125            SliceRange::range_fits(output.len(), index, count),
126            "unchecked read-fully range exceeds output buffer"
127        );
128        let mut total = 0;
129        while total < count {
130            let remaining = count - total;
131            // SAFETY: The caller guarantees the original destination range is
132            // valid; `total < count`, so this suffix remains inside it.
133            match unsafe { self.read_unchecked(output, index + total, remaining) } {
134                Ok(0) => break,
135                Ok(read) => {
136                    validate_read_count(read, remaining)?;
137                    total += read;
138                }
139                Err(error) if error.kind() == ErrorKind::Interrupted => {}
140                Err(error) => return Err(error),
141            }
142        }
143        Ok(total)
144    }
145
146    /// Reads items into the full output slice until it is full or EOF is
147    /// reached.
148    ///
149    /// # Parameters
150    /// - `output`: Destination storage to fill as far as possible.
151    ///
152    /// # Returns
153    /// The number of items written into `output`.
154    ///
155    /// # Errors
156    /// Returns the first non-interrupted input error, or
157    /// [`ErrorKind::InvalidData`] if the implementation reports an impossible
158    /// item count.
159    #[inline(always)]
160    fn read_fully(&mut self, output: &mut [Self::Item]) -> Result<usize> {
161        // SAFETY: The full output slice is a valid destination range.
162        unsafe { self.read_fully_unchecked(output, 0, output.len()) }
163    }
164
165    /// Reads items until the full output slice is filled.
166    ///
167    /// # Parameters
168    ///
169    /// * `output` - Destination storage that must be filled completely.
170    ///
171    /// # Returns
172    ///
173    /// `Ok(())` after the destination has been filled completely.
174    ///
175    /// # Errors
176    ///
177    /// Returns [`ErrorKind::UnexpectedEof`] if the input ends before filling
178    /// `output`. Returns the first non-interrupted input error, or
179    /// [`ErrorKind::InvalidData`] if the implementation reports an impossible
180    /// item count.
181    #[inline]
182    fn read_exactly(&mut self, output: &mut [Self::Item]) -> Result<()> {
183        if self.read_fully(output)? == output.len() {
184            Ok(())
185        } else {
186            Err(Error::new(
187                ErrorKind::UnexpectedEof,
188                "failed to fill whole input buffer",
189            ))
190        }
191    }
192}