qubit-io 0.19.3

Runtime-neutral synchronous and asynchronous item-stream I/O for Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
// =============================================================================
//    Copyright (c) 2026 Haixing Hu.
//
//    SPDX-License-Identifier: Apache-2.0
//
//    Licensed under the Apache License, Version 2.0.
// =============================================================================

use std::io::Error;
use std::io::ErrorKind;
use std::io::Result;

use super::validate_write_count;
use crate::ByteOutput;
use crate::StdWrite;
use crate::util::SliceRange;

/// Minimal indexed output interface over items.
///
/// `Output` is intentionally smaller and lower-level than [`std::io::Write`].
/// Its unchecked primitive writes up to `count` items from `input[index..index
/// + count]`, plus an explicit flush operation. The caller owns range
/// validation for unchecked calls so hot paths can avoid repeated slicing and
/// bounds checks. Safe full-slice writes are available through
/// [`Output::write`] and complete writes through [`Output::write_fully`].
///
/// # Examples
///
/// ```
/// use std::io::Cursor;
///
/// use qubit_io::Output;
///
/// let mut output = Cursor::new(Vec::new());
/// output.write_fully(b"hello")?;
/// assert_eq!(output.into_inner(), b"hello");
/// # Ok::<(), std::io::Error>(())
/// ```
///
/// # Coherence note
///
/// Every [`std::io::Write`] value automatically implements `Output<Item = u8>`
/// through the standard I/O integration. Because [`Output::Item`] is an
/// associated type rather than a trait parameter, a concrete type that
/// implements [`std::io::Write`] cannot also provide any other direct `Output`
/// implementation for the same type, including one with a different item type.
///
/// Use a wrapper/newtype when a type needs item-oriented output semantics that
/// differ from its byte-oriented [`std::io::Write`] implementation.
///
/// # Method name overlap
///
/// `Output::write` has the same method name as [`std::io::Write::write`]
/// because both perform a safe single write for their respective abstraction
/// layer. In generic code where both traits are in scope for the same value,
/// use fully qualified syntax to choose the intended operation:
///
/// ```
/// use std::io::{
///     Result,
///     Write,
/// };
///
/// use qubit_io::Output;
///
/// fn flush_buffered<T>(output: &mut T) -> Result<()>
/// where
///     T: Output + Write,
/// {
///     <T as Output>::flush(output)
/// }
///
/// fn write_all_items<T>(output: &mut T, input: &[u8]) -> Result<()>
/// where
///     T: Output<Item = u8> + Write,
/// {
///     unsafe { <T as Output>::write_fully_unchecked(output, input, 0, input.len()) }
/// }
///
/// fn write_output_items<T>(output: &mut T, input: &[u8]) -> Result<usize>
/// where
///     T: Output<Item = u8> + Write,
/// {
///     Output::write(output, input)
/// }
///
/// fn flush_bytes<T>(output: &mut T) -> Result<()>
/// where
///     T: Output + Write,
/// {
///     Write::flush(output)
/// }
/// ```
pub trait Output {
    /// The item type written to this output.
    type Item;

    /// Converts this byte output into a standard library writer.
    ///
    /// The conversion only wraps `self`; it performs no I/O, flush, or
    /// allocation. The wrapped output can be recovered with
    /// [`StdWrite::into_inner`].
    ///
    /// # Examples
    ///
    /// ```
    /// use qubit_io::Output;
    /// use std::io::Write;
    ///
    /// let output = std::io::Cursor::new(Vec::new());
    /// let mut writer = output.into_std_write();
    /// writer.write_all(b"hello").unwrap();
    /// std::io::Write::flush(&mut writer).unwrap();
    /// let output = writer.into_inner();
    /// assert_eq!(output.into_inner(), b"hello");
    /// ```
    ///
    /// Non-byte outputs cannot be converted:
    ///
    /// ```compile_fail
    /// use qubit_io::Output;
    ///
    /// fn convert<O: Output<Item = char>>(output: O) {
    ///     let _writer = output.into_std_write();
    /// }
    /// ```
    #[must_use = "the returned writer is the converted output"]
    #[inline]
    fn into_std_write(self) -> StdWrite<Self>
    where
        Self: Sized + ByteOutput,
    {
        StdWrite::new(self)
    }

    /// Returns whether this output already buffers items internally.
    ///
    /// # Returns
    ///
    /// `true` when callers should avoid wrapping this output in another generic
    /// item buffer automatically.
    #[inline(always)]
    #[must_use]
    fn is_buffered(&self) -> bool {
        false
    }

    /// Writes items from an indexed input range without checking the range.
    ///
    /// # Parameters
    ///
    /// * `input` - Source storage.
    /// * `index` - Start index inside `input`.
    /// * `count` - Maximum number of items to write.
    ///
    /// # Returns
    ///
    /// The number of items accepted from `input[index..index + count]`. The
    /// value must be in `0..=count`. A successful call accepts a prefix of the
    /// requested range and may be short. For a non-empty request, `Ok(0)`
    /// reports that no items were accepted in this call.
    /// [`Output::write_fully`] treats this as [`ErrorKind::WriteZero`];
    /// already accepted prefixes are not rolled back. For a zero-length
    /// request, a successful call returns zero, which does not establish
    /// that later writes cannot make progress. Empty requests are not
    /// guaranteed to suppress implementation errors or avoid an inner call.
    ///
    /// # Errors
    ///
    /// Returns the output error reported by the implementation. An
    /// [`ErrorKind::Interrupted`] result is retried by aggregate write methods
    /// and therefore means this call made no confirmed transfer. If a valid
    /// prefix was accepted, return `Ok(n)` for it and report a later error on a
    /// subsequent call. A later aggregate error does not undo successful
    /// earlier calls. Errors with applied or uncertain side effects must not
    /// be represented as retryable `Interrupted` solely to request a retry;
    /// the adapter or caller must choose a policy for those effects.
    ///
    /// # Safety
    ///
    /// The caller must guarantee that `index..index + count` is a valid range
    /// inside `input` and that the addition does not overflow.
    unsafe fn write_unchecked(&mut self, input: &[Self::Item], index: usize, count: usize) -> Result<usize>;

    /// Writes items from the full input slice.
    ///
    /// This method performs at most one write operation and keeps the same
    /// short-write behavior as [`Output::write_unchecked`].
    ///
    /// # Parameters
    ///
    /// * `input` - Source items.
    ///
    /// # Returns
    ///
    /// The number of items accepted from `input`. A successful call accepts a
    /// prefix and may be short. For a non-empty request, `Ok(0)` reports that
    /// no items were accepted in this call. [`Output::write_fully`] treats
    /// this as [`ErrorKind::WriteZero`]; already accepted prefixes are not
    /// rolled back. For a zero-length request, a successful call returns
    /// zero, which does not establish that later writes cannot make progress.
    /// Empty requests are not guaranteed to suppress implementation errors or
    /// avoid an inner call.
    ///
    /// # Errors
    ///
    /// Returns the output error reported by the implementation. Returns
    /// [`ErrorKind::InvalidData`] if the implementation reports accepting more
    /// items than requested.
    #[inline(always)]
    fn write(&mut self, input: &[Self::Item]) -> Result<usize> {
        // SAFETY: The full input slice is a valid source range.
        let written = unsafe { self.write_unchecked(input, 0, input.len()) }?;
        validate_write_count(written, input.len())?;
        Ok(written)
    }

    /// Writes all items from an indexed input range.
    ///
    /// This method repeatedly calls [`Output::write_unchecked`] until all
    /// `count` items are accepted. Interrupted writes are retried. A zero
    /// progress report before the range is complete is converted to
    /// [`ErrorKind::WriteZero`].
    ///
    /// # Parameters
    ///
    /// * `input` - Source storage.
    /// * `index` - Start index inside `input`.
    /// * `count` - Number of items to write.
    ///
    /// # Returns
    ///
    /// `Ok(())` after the requested range has been written completely.
    ///
    /// # Errors
    ///
    /// Returns the output error reported by the implementation. Returns
    /// [`ErrorKind::WriteZero`] if the implementation accepts zero items before
    /// the requested range is complete. Returns [`ErrorKind::InvalidData`] if
    /// the implementation reports accepting more items than requested.
    ///
    /// # Panics
    ///
    /// Panics in debug builds if the requested input range does not fit.
    ///
    /// # Safety
    ///
    /// The caller must guarantee that `index..index + count` is a valid range
    /// inside `input` and that the addition does not overflow.
    unsafe fn write_fully_unchecked(&mut self, input: &[Self::Item], index: usize, count: usize) -> Result<()> {
        debug_assert!(
            SliceRange::range_fits(input.len(), index, count),
            "unchecked write-fully range exceeds input buffer"
        );
        let mut written = 0;
        while written < count {
            let remaining = count - written;
            // SAFETY: The caller guarantees the original source range is valid;
            // `written < count`, so this suffix remains inside it.
            match unsafe { self.write_unchecked(input, index + written, remaining) } {
                Ok(0) => {
                    return Err(Error::new(ErrorKind::WriteZero, "failed to write whole output range"));
                }
                Ok(progress) => {
                    validate_write_count(progress, remaining)?;
                    written += progress;
                }
                Err(error) if error.kind() == ErrorKind::Interrupted => {}
                Err(error) => return Err(error),
            }
        }
        Ok(())
    }

    /// Writes all items from the full input slice.
    ///
    /// # Parameters
    /// - `input`: Source items.
    ///
    /// # Returns
    /// `Ok(())` after every item in `input` has been written.
    ///
    /// # Errors
    /// Returns the output error reported by the implementation. Returns
    /// [`ErrorKind::WriteZero`] if no progress is made before all items are
    /// accepted, or [`ErrorKind::InvalidData`] for impossible reported counts.
    #[inline(always)]
    fn write_fully(&mut self, input: &[Self::Item]) -> Result<()> {
        // SAFETY: The full input slice is a valid source range.
        unsafe { self.write_fully_unchecked(input, 0, input.len()) }
    }

    /// Writes items from multiple buffers in list order.
    ///
    /// The buffers form one logical sequence in list order. `Ok(n)` means that
    /// the first `n` items of that sequence were written, and `n` must not
    /// exceed the total requested length. A write may make short progress and
    /// need not consume every buffer; `n` may cross a buffer boundary.
    ///
    /// The default implementation skips empty buffers and calls
    /// [`Output::write`] once on the first non-empty buffer. It does not
    /// continue to later buffers after a short write. This is only the default
    /// strategy, not a limit on overrides. Empty lists and lists whose buffers
    /// are all empty return `Ok(0)` without performing I/O. Interrupted errors
    /// are not retried. An error does not undo progress from earlier
    /// independent calls. An override must uphold the sequence and count
    /// contract itself; this trait does not validate counts returned by
    /// arbitrary overrides. Use UFCS when a standard I/O method with the same
    /// name is also in scope.
    ///
    /// # Parameters
    ///
    /// - `buffers`: Immutable source slices, in the order to write them.
    ///
    /// # Returns
    ///
    /// The number of items written from the start of the logical sequence.
    ///
    /// # Errors
    ///
    /// Returns an error from the output operation. The default implementation
    /// returns [`ErrorKind::InvalidData`] if the selected buffer reports more
    /// items than it contains. The standard I/O blanket implementation also
    /// returns [`ErrorKind::InvalidInput`] if the total length overflows,
    /// [`ErrorKind::OutOfMemory`] if preparing descriptors fails, or
    /// [`ErrorKind::InvalidData`] if the standard writer reports more than the
    /// total capacity. A custom override is responsible for its own checks.
    ///
    /// # Examples
    ///
    /// A byte-output trait object can use the generic interface. The default
    /// method need not consume every buffer; an override may use them all.
    ///
    /// ```
    /// use qubit_io::ByteOutput;
    /// use qubit_io::Output;
    ///
    /// let mut bytes = Vec::new();
    /// let output: &mut dyn ByteOutput = &mut bytes;
    /// let count = Output::write_vectored(output, &[&b"ab"[..], &b"cd"[..]])?;
    /// assert!(count <= 4);
    /// assert_eq!(bytes.as_slice(), &b"abcd"[..count]);
    /// # Ok::<(), std::io::Error>(())
    /// ```
    ///
    /// A vectored write can stop across a buffer boundary. The returned count
    /// describes a prefix of the concatenated buffers, so the next request
    /// starts at the remaining suffix.
    ///
    /// ```
    /// use std::io;
    /// use std::io::IoSlice;
    /// use std::io::Write;
    ///
    /// use qubit_io::Output;
    /// use qubit_io::StdWrite;
    ///
    /// #[derive(Default)]
    /// struct PrefixOutput(Vec<u8>);
    ///
    /// impl Output for PrefixOutput {
    ///     type Item = u8;
    ///
    ///     unsafe fn write_unchecked(
    ///         &mut self,
    ///         _input: &[u8],
    ///         _index: usize,
    ///         _count: usize,
    ///     ) -> io::Result<usize> {
    ///         panic!("this example must use the vectored override")
    ///     }
    ///
    ///     fn write_vectored(&mut self, buffers: &[&[u8]]) -> io::Result<usize> {
    ///         let mut remaining = 3_usize;
    ///         let mut written = 0;
    ///         for buffer in buffers {
    ///             let count = remaining.min(buffer.len());
    ///             self.0.extend_from_slice(&buffer[..count]);
    ///             written += count;
    ///             remaining -= count;
    ///             if remaining == 0 {
    ///                 break;
    ///             }
    ///         }
    ///         Ok(written)
    ///     }
    ///
    ///     fn flush(&mut self) -> io::Result<()> {
    ///         Ok(())
    ///     }
    /// }
    ///
    /// let mut writer = StdWrite::new(PrefixOutput::default());
    /// let count = Write::write_vectored(
    ///     &mut writer,
    ///     &[IoSlice::new(b"ab"), IoSlice::new(b"cd")],
    /// )?;
    /// assert_eq!(count, 3);
    /// assert_eq!(writer.into_inner().0, b"abc");
    /// // The next request must start at the remaining suffix, b"d".
    /// # Ok::<(), io::Error>(())
    /// ```
    fn write_vectored(&mut self, buffers: &[&[Self::Item]]) -> Result<usize> {
        let Some(buffer) = buffers.iter().find(|buffer| !buffer.is_empty()) else {
            return Ok(0);
        };
        let count = self.write(buffer)?;
        validate_write_count(count, buffer.len())?;
        Ok(count)
    }

    /// Flushes any internally buffered items.
    ///
    /// # Returns
    ///
    /// `Ok(())` after all internally buffered items have been flushed.
    ///
    /// # Errors
    ///
    /// Returns the output error reported by the implementation.
    fn flush(&mut self) -> Result<()>;
}