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::ByteOutput;
15use crate::StdWrite;
16use crate::util::SliceRange;
17
18/// Minimal indexed output interface over items.
19///
20/// `Output` is intentionally smaller and lower-level than [`std::io::Write`].
21/// Its unchecked primitive writes up to `count` items from `input[index..index
22/// + count]`, plus an explicit flush operation. The caller owns range
23/// validation for unchecked calls so hot paths can avoid repeated slicing and
24/// bounds checks. Safe full-slice writes are available through
25/// [`Output::write`] and complete writes through [`Output::write_fully`].
26///
27/// # Examples
28///
29/// ```
30/// use std::io::Cursor;
31///
32/// use qubit_io::Output;
33///
34/// let mut output = Cursor::new(Vec::new());
35/// output.write_fully(b"hello")?;
36/// assert_eq!(output.into_inner(), b"hello");
37/// # Ok::<(), std::io::Error>(())
38/// ```
39///
40/// # Coherence note
41///
42/// Every [`std::io::Write`] value automatically implements `Output<Item = u8>`
43/// through the standard I/O integration. Because [`Output::Item`] is an
44/// associated type rather than a trait parameter, a concrete type that
45/// implements [`std::io::Write`] cannot also provide any other direct `Output`
46/// implementation for the same type, including one with a different item type.
47///
48/// Use a wrapper/newtype when a type needs item-oriented output semantics that
49/// differ from its byte-oriented [`std::io::Write`] implementation.
50///
51/// # Method name overlap
52///
53/// `Output::write` has the same method name as [`std::io::Write::write`]
54/// because both perform a safe single write for their respective abstraction
55/// layer. In generic code where both traits are in scope for the same value,
56/// use fully qualified syntax to choose the intended operation:
57///
58/// ```
59/// use std::io::{
60///     Result,
61///     Write,
62/// };
63///
64/// use qubit_io::Output;
65///
66/// fn flush_buffered<T>(output: &mut T) -> Result<()>
67/// where
68///     T: Output + Write,
69/// {
70///     <T as Output>::flush(output)
71/// }
72///
73/// fn write_all_items<T>(output: &mut T, input: &[u8]) -> Result<()>
74/// where
75///     T: Output<Item = u8> + Write,
76/// {
77///     unsafe { <T as Output>::write_fully_unchecked(output, input, 0, input.len()) }
78/// }
79///
80/// fn write_output_items<T>(output: &mut T, input: &[u8]) -> Result<usize>
81/// where
82///     T: Output<Item = u8> + Write,
83/// {
84///     Output::write(output, input)
85/// }
86///
87/// fn flush_bytes<T>(output: &mut T) -> Result<()>
88/// where
89///     T: Output + Write,
90/// {
91///     Write::flush(output)
92/// }
93/// ```
94pub trait Output {
95    /// The item type written to this output.
96    type Item;
97
98    /// Converts this byte output into a standard library writer.
99    ///
100    /// The conversion only wraps `self`; it performs no I/O, flush, or
101    /// allocation. The wrapped output can be recovered with
102    /// [`StdWrite::into_inner`].
103    ///
104    /// # Examples
105    ///
106    /// ```
107    /// use qubit_io::Output;
108    /// use std::io::Write;
109    ///
110    /// let output = std::io::Cursor::new(Vec::new());
111    /// let mut writer = output.into_std_write();
112    /// writer.write_all(b"hello").unwrap();
113    /// std::io::Write::flush(&mut writer).unwrap();
114    /// let output = writer.into_inner();
115    /// assert_eq!(output.into_inner(), b"hello");
116    /// ```
117    ///
118    /// Non-byte outputs cannot be converted:
119    ///
120    /// ```compile_fail
121    /// use qubit_io::Output;
122    ///
123    /// fn convert<O: Output<Item = char>>(output: O) {
124    ///     let _writer = output.into_std_write();
125    /// }
126    /// ```
127    #[must_use = "the returned writer is the converted output"]
128    #[inline]
129    fn into_std_write(self) -> StdWrite<Self>
130    where
131        Self: Sized + ByteOutput,
132    {
133        StdWrite::new(self)
134    }
135
136    /// Returns whether this output already buffers items internally.
137    ///
138    /// # Returns
139    ///
140    /// `true` when callers should avoid wrapping this output in another generic
141    /// item buffer automatically.
142    #[inline(always)]
143    #[must_use]
144    fn is_buffered(&self) -> bool {
145        false
146    }
147
148    /// Writes items from an indexed input range without checking the range.
149    ///
150    /// # Parameters
151    ///
152    /// * `input` - Source storage.
153    /// * `index` - Start index inside `input`.
154    /// * `count` - Maximum number of items to write.
155    ///
156    /// # Returns
157    ///
158    /// The number of items accepted from `input[index..index + count]`. The
159    /// value must be in `0..=count`. A successful call accepts a prefix of the
160    /// requested range and may be short. For a non-empty request, `Ok(0)`
161    /// reports that no items were accepted in this call.
162    /// [`Output::write_fully`] treats this as [`ErrorKind::WriteZero`];
163    /// already accepted prefixes are not rolled back. For a zero-length
164    /// request, a successful call returns zero, which does not establish
165    /// that later writes cannot make progress. Empty requests are not
166    /// guaranteed to suppress implementation errors or avoid an inner call.
167    ///
168    /// # Errors
169    ///
170    /// Returns the output error reported by the implementation. An
171    /// [`ErrorKind::Interrupted`] result is retried by aggregate write methods
172    /// and therefore means this call made no confirmed transfer. If a valid
173    /// prefix was accepted, return `Ok(n)` for it and report a later error on a
174    /// subsequent call. A later aggregate error does not undo successful
175    /// earlier calls. Errors with applied or uncertain side effects must not
176    /// be represented as retryable `Interrupted` solely to request a retry;
177    /// the adapter or caller must choose a policy for those effects.
178    ///
179    /// # Safety
180    ///
181    /// The caller must guarantee that `index..index + count` is a valid range
182    /// inside `input` and that the addition does not overflow.
183    unsafe fn write_unchecked(&mut self, input: &[Self::Item], index: usize, count: usize) -> Result<usize>;
184
185    /// Writes items from the full input slice.
186    ///
187    /// This method performs at most one write operation and keeps the same
188    /// short-write behavior as [`Output::write_unchecked`].
189    ///
190    /// # Parameters
191    ///
192    /// * `input` - Source items.
193    ///
194    /// # Returns
195    ///
196    /// The number of items accepted from `input`. A successful call accepts a
197    /// prefix and may be short. For a non-empty request, `Ok(0)` reports that
198    /// no items were accepted in this call. [`Output::write_fully`] treats
199    /// this as [`ErrorKind::WriteZero`]; already accepted prefixes are not
200    /// rolled back. For a zero-length request, a successful call returns
201    /// zero, which does not establish that later writes cannot make progress.
202    /// Empty requests are not guaranteed to suppress implementation errors or
203    /// avoid an inner call.
204    ///
205    /// # Errors
206    ///
207    /// Returns the output error reported by the implementation. Returns
208    /// [`ErrorKind::InvalidData`] if the implementation reports accepting more
209    /// items than requested.
210    #[inline(always)]
211    fn write(&mut self, input: &[Self::Item]) -> Result<usize> {
212        // SAFETY: The full input slice is a valid source range.
213        let written = unsafe { self.write_unchecked(input, 0, input.len()) }?;
214        validate_write_count(written, input.len())?;
215        Ok(written)
216    }
217
218    /// Writes all items from an indexed input range.
219    ///
220    /// This method repeatedly calls [`Output::write_unchecked`] until all
221    /// `count` items are accepted. Interrupted writes are retried. A zero
222    /// progress report before the range is complete is converted to
223    /// [`ErrorKind::WriteZero`].
224    ///
225    /// # Parameters
226    ///
227    /// * `input` - Source storage.
228    /// * `index` - Start index inside `input`.
229    /// * `count` - Number of items to write.
230    ///
231    /// # Returns
232    ///
233    /// `Ok(())` after the requested range has been written completely.
234    ///
235    /// # Errors
236    ///
237    /// Returns the output error reported by the implementation. Returns
238    /// [`ErrorKind::WriteZero`] if the implementation accepts zero items before
239    /// the requested range is complete. Returns [`ErrorKind::InvalidData`] if
240    /// the implementation reports accepting more items than requested.
241    ///
242    /// # Panics
243    ///
244    /// Panics in debug builds if the requested input range does not fit.
245    ///
246    /// # Safety
247    ///
248    /// The caller must guarantee that `index..index + count` is a valid range
249    /// inside `input` and that the addition does not overflow.
250    unsafe fn write_fully_unchecked(&mut self, input: &[Self::Item], index: usize, count: usize) -> Result<()> {
251        debug_assert!(
252            SliceRange::range_fits(input.len(), index, count),
253            "unchecked write-fully range exceeds input buffer"
254        );
255        let mut written = 0;
256        while written < count {
257            let remaining = count - written;
258            // SAFETY: The caller guarantees the original source range is valid;
259            // `written < count`, so this suffix remains inside it.
260            match unsafe { self.write_unchecked(input, index + written, remaining) } {
261                Ok(0) => {
262                    return Err(Error::new(ErrorKind::WriteZero, "failed to write whole output range"));
263                }
264                Ok(progress) => {
265                    validate_write_count(progress, remaining)?;
266                    written += progress;
267                }
268                Err(error) if error.kind() == ErrorKind::Interrupted => {}
269                Err(error) => return Err(error),
270            }
271        }
272        Ok(())
273    }
274
275    /// Writes all items from the full input slice.
276    ///
277    /// # Parameters
278    /// - `input`: Source items.
279    ///
280    /// # Returns
281    /// `Ok(())` after every item in `input` has been written.
282    ///
283    /// # Errors
284    /// Returns the output error reported by the implementation. Returns
285    /// [`ErrorKind::WriteZero`] if no progress is made before all items are
286    /// accepted, or [`ErrorKind::InvalidData`] for impossible reported counts.
287    #[inline(always)]
288    fn write_fully(&mut self, input: &[Self::Item]) -> Result<()> {
289        // SAFETY: The full input slice is a valid source range.
290        unsafe { self.write_fully_unchecked(input, 0, input.len()) }
291    }
292
293    /// Writes items from multiple buffers in list order.
294    ///
295    /// The buffers form one logical sequence in list order. `Ok(n)` means that
296    /// the first `n` items of that sequence were written, and `n` must not
297    /// exceed the total requested length. A write may make short progress and
298    /// need not consume every buffer; `n` may cross a buffer boundary.
299    ///
300    /// The default implementation skips empty buffers and calls
301    /// [`Output::write`] once on the first non-empty buffer. It does not
302    /// continue to later buffers after a short write. This is only the default
303    /// strategy, not a limit on overrides. Empty lists and lists whose buffers
304    /// are all empty return `Ok(0)` without performing I/O. Interrupted errors
305    /// are not retried. An error does not undo progress from earlier
306    /// independent calls. An override must uphold the sequence and count
307    /// contract itself; this trait does not validate counts returned by
308    /// arbitrary overrides. Use UFCS when a standard I/O method with the same
309    /// name is also in scope.
310    ///
311    /// # Parameters
312    ///
313    /// - `buffers`: Immutable source slices, in the order to write them.
314    ///
315    /// # Returns
316    ///
317    /// The number of items written from the start of the logical sequence.
318    ///
319    /// # Errors
320    ///
321    /// Returns an error from the output operation. The default implementation
322    /// returns [`ErrorKind::InvalidData`] if the selected buffer reports more
323    /// items than it contains. The standard I/O blanket implementation also
324    /// returns [`ErrorKind::InvalidInput`] if the total length overflows,
325    /// [`ErrorKind::OutOfMemory`] if preparing descriptors fails, or
326    /// [`ErrorKind::InvalidData`] if the standard writer reports more than the
327    /// total capacity. A custom override is responsible for its own checks.
328    ///
329    /// # Examples
330    ///
331    /// A byte-output trait object can use the generic interface. The default
332    /// method need not consume every buffer; an override may use them all.
333    ///
334    /// ```
335    /// use qubit_io::ByteOutput;
336    /// use qubit_io::Output;
337    ///
338    /// let mut bytes = Vec::new();
339    /// let output: &mut dyn ByteOutput = &mut bytes;
340    /// let count = Output::write_vectored(output, &[&b"ab"[..], &b"cd"[..]])?;
341    /// assert!(count <= 4);
342    /// assert_eq!(bytes.as_slice(), &b"abcd"[..count]);
343    /// # Ok::<(), std::io::Error>(())
344    /// ```
345    ///
346    /// A vectored write can stop across a buffer boundary. The returned count
347    /// describes a prefix of the concatenated buffers, so the next request
348    /// starts at the remaining suffix.
349    ///
350    /// ```
351    /// use std::io;
352    /// use std::io::IoSlice;
353    /// use std::io::Write;
354    ///
355    /// use qubit_io::Output;
356    /// use qubit_io::StdWrite;
357    ///
358    /// #[derive(Default)]
359    /// struct PrefixOutput(Vec<u8>);
360    ///
361    /// impl Output for PrefixOutput {
362    ///     type Item = u8;
363    ///
364    ///     unsafe fn write_unchecked(
365    ///         &mut self,
366    ///         _input: &[u8],
367    ///         _index: usize,
368    ///         _count: usize,
369    ///     ) -> io::Result<usize> {
370    ///         panic!("this example must use the vectored override")
371    ///     }
372    ///
373    ///     fn write_vectored(&mut self, buffers: &[&[u8]]) -> io::Result<usize> {
374    ///         let mut remaining = 3_usize;
375    ///         let mut written = 0;
376    ///         for buffer in buffers {
377    ///             let count = remaining.min(buffer.len());
378    ///             self.0.extend_from_slice(&buffer[..count]);
379    ///             written += count;
380    ///             remaining -= count;
381    ///             if remaining == 0 {
382    ///                 break;
383    ///             }
384    ///         }
385    ///         Ok(written)
386    ///     }
387    ///
388    ///     fn flush(&mut self) -> io::Result<()> {
389    ///         Ok(())
390    ///     }
391    /// }
392    ///
393    /// let mut writer = StdWrite::new(PrefixOutput::default());
394    /// let count = Write::write_vectored(
395    ///     &mut writer,
396    ///     &[IoSlice::new(b"ab"), IoSlice::new(b"cd")],
397    /// )?;
398    /// assert_eq!(count, 3);
399    /// assert_eq!(writer.into_inner().0, b"abc");
400    /// // The next request must start at the remaining suffix, b"d".
401    /// # Ok::<(), io::Error>(())
402    /// ```
403    fn write_vectored(&mut self, buffers: &[&[Self::Item]]) -> Result<usize> {
404        let Some(buffer) = buffers.iter().find(|buffer| !buffer.is_empty()) else {
405            return Ok(0);
406        };
407        let count = self.write(buffer)?;
408        validate_write_count(count, buffer.len())?;
409        Ok(count)
410    }
411
412    /// Flushes any internally buffered items.
413    ///
414    /// # Returns
415    ///
416    /// `Ok(())` after all internally buffered items have been flushed.
417    ///
418    /// # Errors
419    ///
420    /// Returns the output error reported by the implementation.
421    fn flush(&mut self) -> Result<()>;
422}