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.
24pub trait Input {
25 /// The item type read from this input.
26 type Item;
27
28 /// Reads items into an indexed output range without checking the range.
29 ///
30 /// # Parameters
31 ///
32 /// * `output` - Destination storage.
33 /// * `index` - Start index inside `output`.
34 /// * `count` - Maximum number of items to read.
35 ///
36 /// # Returns
37 ///
38 /// The number of items written into `output[index..index + count]`. The
39 /// value must be in `0..=count`.
40 ///
41 /// # Errors
42 ///
43 /// Returns the input error reported by the implementation.
44 ///
45 /// # Safety
46 ///
47 /// The caller must guarantee that `index..index + count` is a valid range
48 /// inside `output` and that the addition does not overflow.
49 unsafe fn read_unchecked(
50 &mut self,
51 output: &mut [Self::Item],
52 index: usize,
53 count: usize,
54 ) -> Result<usize>;
55
56 /// Reads items into the full output slice.
57 ///
58 /// # Parameters
59 /// - `output`: Destination storage.
60 ///
61 /// # Returns
62 /// The number of items read into `output`.
63 ///
64 /// # Errors
65 ///
66 /// Returns the input error reported by the implementation. Returns
67 /// [`ErrorKind::InvalidData`] if the implementation reports reading more
68 /// items than requested.
69 #[inline(always)]
70 fn read(&mut self, output: &mut [Self::Item]) -> Result<usize> {
71 // SAFETY: The caller ensured the destination slice is valid.
72 let read = unsafe { self.read_unchecked(output, 0, output.len()) }?;
73 validate_read_count(read, output.len())?;
74 Ok(read)
75 }
76}
77
78impl<R> Input for R
79where
80 R: Read + ?Sized,
81{
82 type Item = u8;
83
84 /// Reads bytes from a standard [`Read`] value into an indexed range.
85 #[inline(always)]
86 unsafe fn read_unchecked(
87 &mut self,
88 output: &mut [u8],
89 index: usize,
90 count: usize,
91 ) -> Result<usize> {
92 debug_assert!(
93 UncheckedSlice::range_fits(output.len(), index, count),
94 "unchecked read range exceeds output buffer"
95 );
96 // SAFETY: The caller guarantees that the range is valid inside
97 // `output`.
98 let target =
99 unsafe { UncheckedSlice::subslice_mut(output, index, count) };
100 Read::read(self, target)
101 }
102
103 /// Reads items into the full output slice.
104 #[inline(always)]
105 fn read(&mut self, output: &mut [Self::Item]) -> Result<usize> {
106 Read::read(self, output)
107 }
108}
109
110/// Validates an item count returned by an input.
111///
112/// # Parameters
113///
114/// * `read` - Item count reported by the input.
115/// * `requested` - Maximum item count requested from the input.
116///
117/// # Errors
118///
119/// Returns [`ErrorKind::InvalidData`] when the input reports more items than
120/// the destination range could hold.
121#[inline(always)]
122pub fn validate_read_count(read: usize, requested: usize) -> Result<()> {
123 if read > requested {
124 return Err(Error::new(
125 ErrorKind::InvalidData,
126 format!(
127 "reader reported {read} items for a {requested}-item buffer"
128 ),
129 ));
130 }
131 Ok(())
132}