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::Error;
10use std::io::ErrorKind;
11use std::io::Result;
12
13use super::validate_write_count;
14use crate::util::SliceRange;
15
16/// Minimal indexed output interface over items.
17///
18/// `Output` is intentionally smaller and lower-level than [`std::io::Write`].
19/// Its unchecked primitive writes up to `count` items from `input[index..index
20/// + count]`, plus an explicit flush operation. The caller owns range
21/// validation for unchecked calls so hot paths can avoid repeated slicing and
22/// bounds checks. Safe full-slice writes are available through
23/// [`Output::write`] and complete writes through [`Output::write_fully`].
24///
25/// # Coherence note
26///
27/// Every [`std::io::Write`] value automatically implements `Output<Item = u8>`
28/// through the standard I/O integration. Because [`Output::Item`] is an
29/// associated type rather than a trait parameter, a concrete type that
30/// implements [`std::io::Write`] cannot also provide any other direct `Output`
31/// implementation for the same type, including one with a different item type.
32///
33/// Use a wrapper/newtype when a type needs item-oriented output semantics that
34/// differ from its byte-oriented [`std::io::Write`] implementation.
35///
36/// # Method name overlap
37///
38/// `Output::write` has the same method name as [`std::io::Write::write`]
39/// because both perform a safe single write for their respective abstraction
40/// layer. In generic code where both traits are in scope for the same value,
41/// use fully qualified syntax to choose the intended operation:
42///
43/// ```
44/// use std::io::{
45///     Result,
46///     Write,
47/// };
48///
49/// use qubit_io::Output;
50///
51/// fn flush_buffered<T>(output: &mut T) -> Result<()>
52/// where
53///     T: Output + Write,
54/// {
55///     <T as Output>::flush(output)
56/// }
57///
58/// fn write_all_items<T>(output: &mut T, input: &[u8]) -> Result<()>
59/// where
60///     T: Output<Item = u8> + Write,
61/// {
62///     unsafe { <T as Output>::write_fully_unchecked(output, input, 0, input.len()) }
63/// }
64///
65/// fn write_output_items<T>(output: &mut T, input: &[u8]) -> Result<usize>
66/// where
67///     T: Output<Item = u8> + Write,
68/// {
69///     Output::write(output, input)
70/// }
71///
72/// fn flush_bytes<T>(output: &mut T) -> Result<()>
73/// where
74///     T: Output + Write,
75/// {
76///     Write::flush(output)
77/// }
78/// ```
79pub trait Output {
80    /// The item type written to this output.
81    type Item;
82
83    /// Returns whether this output already buffers items internally.
84    ///
85    /// # Returns
86    ///
87    /// `true` when callers should avoid wrapping this output in another generic
88    /// item buffer automatically.
89    #[inline(always)]
90    #[must_use]
91    fn is_buffered(&self) -> bool {
92        false
93    }
94
95    /// Writes items from an indexed input range without checking the range.
96    ///
97    /// # Parameters
98    ///
99    /// * `input` - Source storage.
100    /// * `index` - Start index inside `input`.
101    /// * `count` - Maximum number of items to write.
102    ///
103    /// # Returns
104    ///
105    /// The number of items accepted from `input[index..index + count]`. The
106    /// value must be in `0..=count`.
107    ///
108    /// # Errors
109    ///
110    /// Returns the output error reported by the implementation.
111    ///
112    /// # Safety
113    ///
114    /// The caller must guarantee that `index..index + count` is a valid range
115    /// inside `input` and that the addition does not overflow.
116    unsafe fn write_unchecked(&mut self, input: &[Self::Item], index: usize, count: usize) -> Result<usize>;
117
118    /// Writes items from the full input slice.
119    ///
120    /// This method performs at most one write operation and keeps the same
121    /// short-write behavior as [`Output::write_unchecked`].
122    ///
123    /// # Parameters
124    ///
125    /// * `input` - Source items.
126    ///
127    /// # Returns
128    ///
129    /// The number of items accepted from `input`.
130    ///
131    /// # Errors
132    ///
133    /// Returns the output error reported by the implementation. Returns
134    /// [`ErrorKind::InvalidData`] if the implementation reports accepting more
135    /// items than requested.
136    #[inline(always)]
137    fn write(&mut self, input: &[Self::Item]) -> Result<usize> {
138        // SAFETY: The full input slice is a valid source range.
139        let written = unsafe { self.write_unchecked(input, 0, input.len()) }?;
140        validate_write_count(written, input.len())?;
141        Ok(written)
142    }
143
144    /// Writes all items from an indexed input range.
145    ///
146    /// This method repeatedly calls [`Output::write_unchecked`] until all
147    /// `count` items are accepted. Interrupted writes are retried. A zero
148    /// progress report before the range is complete is converted to
149    /// [`ErrorKind::WriteZero`].
150    ///
151    /// # Parameters
152    ///
153    /// * `input` - Source storage.
154    /// * `index` - Start index inside `input`.
155    /// * `count` - Number of items to write.
156    ///
157    /// # Returns
158    ///
159    /// `Ok(())` after the requested range has been written completely.
160    ///
161    /// # Errors
162    ///
163    /// Returns the output error reported by the implementation. Returns
164    /// [`ErrorKind::WriteZero`] if the implementation accepts zero items before
165    /// the requested range is complete. Returns [`ErrorKind::InvalidData`] if
166    /// the implementation reports accepting more items than requested.
167    ///
168    /// # Panics
169    ///
170    /// Panics in debug builds if the requested input range does not fit.
171    ///
172    /// # Safety
173    ///
174    /// The caller must guarantee that `index..index + count` is a valid range
175    /// inside `input` and that the addition does not overflow.
176    unsafe fn write_fully_unchecked(&mut self, input: &[Self::Item], index: usize, count: usize) -> Result<()> {
177        debug_assert!(
178            SliceRange::range_fits(input.len(), index, count),
179            "unchecked write-fully range exceeds input buffer"
180        );
181        let mut written = 0;
182        while written < count {
183            let remaining = count - written;
184            // SAFETY: The caller guarantees the original source range is valid;
185            // `written < count`, so this suffix remains inside it.
186            match unsafe { self.write_unchecked(input, index + written, remaining) } {
187                Ok(0) => {
188                    return Err(Error::new(ErrorKind::WriteZero, "failed to write whole output range"));
189                }
190                Ok(progress) => {
191                    validate_write_count(progress, remaining)?;
192                    written += progress;
193                }
194                Err(error) if error.kind() == ErrorKind::Interrupted => {}
195                Err(error) => return Err(error),
196            }
197        }
198        Ok(())
199    }
200
201    /// Writes all items from the full input slice.
202    ///
203    /// # Parameters
204    /// - `input`: Source items.
205    ///
206    /// # Returns
207    /// `Ok(())` after every item in `input` has been written.
208    ///
209    /// # Errors
210    /// Returns the output error reported by the implementation. Returns
211    /// [`ErrorKind::WriteZero`] if no progress is made before all items are
212    /// accepted, or [`ErrorKind::InvalidData`] for impossible reported counts.
213    #[inline(always)]
214    fn write_fully(&mut self, input: &[Self::Item]) -> Result<()> {
215        // SAFETY: The full input slice is a valid source range.
216        unsafe { self.write_fully_unchecked(input, 0, input.len()) }
217    }
218
219    /// Flushes any internally buffered items.
220    ///
221    /// # Returns
222    ///
223    /// `Ok(())` after all internally buffered items have been flushed.
224    ///
225    /// # Errors
226    ///
227    /// Returns the output error reported by the implementation.
228    fn flush(&mut self) -> Result<()>;
229}