Skip to main content

qubit_io/traits/
output.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    Result,
13    Write,
14};
15
16use crate::util::UncheckedSlice;
17
18/// Minimal indexed output interface over items.
19///
20/// `Output` is intentionally smaller and lower-level than [`Write`]. Its
21/// unchecked primitive writes up to `count` items from `input[index..index +
22/// count]`, plus an explicit flush operation. The caller owns range validation
23/// for unchecked calls so hot paths can avoid repeated slicing and bounds
24/// checks. Safe full-slice writes are available through [`Output::write`].
25///
26/// # Method name overlap
27///
28/// `Output::write` has the same method name as [`Write::write`] because both
29/// perform a safe single write for their respective abstraction layer. In
30/// generic code where both traits are in scope for the same value, use fully
31/// qualified syntax to choose the intended operation:
32///
33/// ```
34/// use std::io::{
35///     Result,
36///     Write,
37/// };
38///
39/// use qubit_io::{
40///     Output,
41///     OutputExt,
42/// };
43///
44/// fn flush_buffered<T>(output: &mut T) -> Result<()>
45/// where
46///     T: Output + Write,
47/// {
48///     <T as Output>::flush(output)
49/// }
50///
51/// fn write_all_items<T>(output: &mut T, input: &[u8]) -> Result<()>
52/// where
53///     T: Output<Item = u8> + Write,
54/// {
55///     unsafe { <T as OutputExt>::write_all_unchecked(output, input, 0, input.len()) }
56/// }
57///
58/// fn write_output_items<T>(output: &mut T, input: &[u8]) -> Result<usize>
59/// where
60///     T: Output<Item = u8> + Write,
61/// {
62///     Output::write(output, input)
63/// }
64///
65/// fn flush_bytes<T>(output: &mut T) -> Result<()>
66/// where
67///     T: Output + Write,
68/// {
69///     Write::flush(output)
70/// }
71/// ```
72pub trait Output {
73    /// The item type written to this output.
74    type Item;
75
76    /// Writes items from an indexed input range without checking the range.
77    ///
78    /// # Parameters
79    ///
80    /// * `input` - Source storage.
81    /// * `index` - Start index inside `input`.
82    /// * `count` - Maximum number of items to write.
83    ///
84    /// # Returns
85    ///
86    /// The number of items accepted from `input[index..index + count]`. The
87    /// value must be in `0..=count`.
88    ///
89    /// # Errors
90    ///
91    /// Returns the output error reported by the implementation.
92    ///
93    /// # Safety
94    ///
95    /// The caller must guarantee that `index..index + count` is a valid range
96    /// inside `input` and that the addition does not overflow.
97    unsafe fn write_unchecked(
98        &mut self,
99        input: &[Self::Item],
100        index: usize,
101        count: usize,
102    ) -> Result<usize>;
103
104    /// Writes items from the full input slice.
105    ///
106    /// This method performs at most one write operation and keeps the same
107    /// short-write behavior as [`Output::write_unchecked`].
108    ///
109    /// # Parameters
110    ///
111    /// * `input` - Source items.
112    ///
113    /// # Returns
114    ///
115    /// The number of items accepted from `input`.
116    ///
117    /// # Errors
118    ///
119    /// Returns the output error reported by the implementation. Returns
120    /// [`ErrorKind::InvalidData`] if the implementation reports accepting more
121    /// items than requested.
122    #[inline(always)]
123    fn write(&mut self, input: &[Self::Item]) -> Result<usize> {
124        // SAFETY: The full input slice is a valid source range.
125        let written = unsafe { self.write_unchecked(input, 0, input.len()) }?;
126        validate_write_count(written, input.len())?;
127        Ok(written)
128    }
129
130    /// Flushes any internally buffered items.
131    ///
132    /// # Errors
133    ///
134    /// Returns the output error reported by the implementation.
135    fn flush(&mut self) -> Result<()>;
136}
137
138impl<W> Output for W
139where
140    W: Write + ?Sized,
141{
142    type Item = u8;
143
144    /// Writes bytes to a standard [`Write`] value from an indexed range.
145    #[inline(always)]
146    unsafe fn write_unchecked(
147        &mut self,
148        input: &[u8],
149        index: usize,
150        count: usize,
151    ) -> Result<usize> {
152        debug_assert!(
153            UncheckedSlice::range_fits(input.len(), index, count),
154            "unchecked write range exceeds input buffer"
155        );
156        // SAFETY: The caller guarantees that the range is valid inside
157        // `input`.
158        let source = unsafe { UncheckedSlice::subslice(input, index, count) };
159        Write::write(self, source)
160    }
161
162    /// Writes items into the full output slice.
163    #[inline(always)]
164    fn write(&mut self, input: &[Self::Item]) -> Result<usize> {
165        Write::write(self, input)
166    }
167
168    /// Flushes a standard [`Write`] value.
169    #[inline(always)]
170    fn flush(&mut self) -> Result<()> {
171        Write::flush(self)
172    }
173}
174
175/// Validates an item count returned by an output.
176///
177/// # Parameters
178///
179/// * `written` - Item count reported by the output.
180/// * `requested` - Maximum item count requested from the output.
181///
182/// # Errors
183///
184/// Returns [`ErrorKind::InvalidData`] when the output reports more items than
185/// the source range contained.
186#[inline(always)]
187pub fn validate_write_count(written: usize, requested: usize) -> Result<()> {
188    if written > requested {
189        return Err(Error::new(
190            ErrorKind::InvalidData,
191            format!(
192                "writer reported {written} items for a {requested}-item buffer"
193            ),
194        ));
195    }
196    Ok(())
197}