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}