Skip to main content

qubit_io/ext/
read_ext.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// =============================================================================
8use std::io::{
9    ErrorKind,
10    Read,
11    Result,
12    Write,
13    copy as copy_all,
14};
15
16use crate::Streams;
17use crate::ext::internal::read_ext_impl;
18use crate::util::{
19    UncheckedSlice,
20    try_reserve_string,
21};
22
23/// Default stack buffer size used by discard operations.
24const DISCARD_BUFFER_SIZE: usize = 8 * 1024;
25
26/// Extension methods for [`Read`] values.
27///
28/// `ReadExt` fills small semantic gaps in the standard [`Read`] trait while
29/// keeping the same blocking and error model. The methods are implemented for
30/// every type that implements [`Read`], including `dyn Read` trait objects.
31///
32/// # Examples
33/// ```
34/// use qubit_io::ReadExt;
35/// use std::io::Cursor;
36///
37/// let mut input = Cursor::new(b"abcdef".to_vec());
38/// let header = input.read_exact_array::<2>()?;
39/// let payload = input.read_exact_vec_limited(4, 16)?;
40///
41/// assert_eq!(*b"ab", header);
42/// assert_eq!(b"cdef", payload.as_slice());
43/// # Ok::<(), std::io::Error>(())
44/// ```
45pub trait ReadExt: Read {
46    /// Reads bytes into a range of `buffer` without checking the range bounds
47    /// in release builds.
48    ///
49    /// This method delegates to [`Read::read`] after creating the target slice
50    /// with raw pointer arithmetic. It performs at most one read operation and
51    /// returns the number of bytes read, keeping the same short-read and error
52    /// behavior as [`Read::read`].
53    ///
54    /// # Parameters
55    /// - `buffer`: Destination buffer.
56    /// - `start_index`: Start offset inside `buffer`.
57    /// - `count`: Maximum number of bytes to read.
58    ///
59    /// # Returns
60    /// The number of bytes written into `buffer[start_index..start_index +
61    /// count]`. The value is in `0..=count`.
62    ///
63    /// # Errors
64    /// Returns the error reported by [`Read::read`].
65    ///
66    /// # Safety
67    /// The caller must guarantee that `start_index..start_index + count` is a
68    /// valid range within `buffer` and that `start_index + count` does not
69    /// overflow `usize`.
70    unsafe fn read_unchecked(
71        &mut self,
72        buffer: &mut [u8],
73        start_index: usize,
74        count: usize,
75    ) -> Result<usize>;
76
77    /// Reads exactly `count` bytes into a range of `buffer` without checking
78    /// the range bounds in release builds.
79    ///
80    /// This method delegates to [`Read::read_exact`] after creating the target
81    /// slice with raw pointer arithmetic. It keeps the same blocking and error
82    /// behavior as [`Read::read_exact`].
83    ///
84    /// # Parameters
85    /// - `buffer`: Destination buffer.
86    /// - `start_index`: Start offset inside `buffer`.
87    /// - `count`: Number of bytes to read.
88    ///
89    /// # Errors
90    /// Returns the error reported by [`Read::read_exact`], including
91    /// [`ErrorKind::UnexpectedEof`] when EOF is reached before `count` bytes
92    /// are read.
93    ///
94    /// # Safety
95    /// The caller must guarantee that `start_index..start_index + count` is a
96    /// valid range within `buffer` and that `start_index + count` does not
97    /// overflow `usize`.
98    unsafe fn read_exact_unchecked(
99        &mut self,
100        buffer: &mut [u8],
101        start_index: usize,
102        count: usize,
103    ) -> Result<()>;
104
105    /// Reads bytes into a range of `buffer` until that range is full or EOF is
106    /// reached, without checking the range bounds in release builds.
107    ///
108    /// This method has the same EOF and retry behavior as
109    /// [`ReadExt::read_exact_or_eof`], but writes into
110    /// `buffer[start_index..start_index + count]` using raw pointer
111    /// arithmetic.
112    ///
113    /// # Parameters
114    /// - `buffer`: Destination buffer.
115    /// - `start_index`: Start offset inside `buffer`.
116    /// - `count`: Number of bytes to try to read.
117    ///
118    /// # Returns
119    /// The number of bytes written into `buffer[start_index..start_index +
120    /// count]`. The value is in `0..=count`.
121    ///
122    /// # Errors
123    /// Returns the first non-[`ErrorKind::Interrupted`] error reported by the
124    /// underlying reader. Interrupted reads are retried.
125    ///
126    /// # Safety
127    /// The caller must guarantee that `start_index..start_index + count` is a
128    /// valid range within `buffer` and that `start_index + count` does not
129    /// overflow `usize`.
130    unsafe fn read_exact_or_eof_unchecked(
131        &mut self,
132        buffer: &mut [u8],
133        start_index: usize,
134        count: usize,
135    ) -> Result<usize>;
136
137    /// Reads bytes until `buffer` is full or EOF is reached.
138    ///
139    /// This method differs from [`Read::read_exact`] by treating EOF as a
140    /// successful partial result. It keeps retrying short reads until the
141    /// caller-provided buffer is full, EOF is reached, or a non-interrupted
142    /// I/O error occurs.
143    ///
144    /// # Parameters
145    /// - `buffer`: Destination buffer to fill.
146    ///
147    /// # Returns
148    /// The number of bytes written into `buffer`. The value is in
149    /// `0..=buffer.len()`.
150    ///
151    /// # Errors
152    /// Returns the first non-[`ErrorKind::Interrupted`] error reported by the
153    /// underlying reader. Interrupted reads are retried.
154    fn read_exact_or_eof(&mut self, buffer: &mut [u8]) -> Result<usize>;
155
156    /// Reads exactly `N` bytes into a stack-allocated array.
157    ///
158    /// This method uses [`Read::read_exact`] and therefore requires the reader
159    /// to provide exactly `N` bytes before EOF.
160    ///
161    /// # Returns
162    /// An array containing exactly `N` bytes read from this reader.
163    ///
164    /// # Errors
165    /// Returns the error reported by [`Read::read_exact`], including
166    /// [`ErrorKind::UnexpectedEof`] when EOF is reached before the array is
167    /// full.
168    fn read_exact_array<const N: usize>(&mut self) -> Result<[u8; N]>;
169
170    /// Reads exactly `len` bytes into a new vector after checking a limit.
171    ///
172    /// If `len` is greater than `max_len`, this method returns
173    /// [`ErrorKind::InvalidData`] before reading any bytes.
174    ///
175    /// # Parameters
176    /// - `len`: Exact number of bytes to read.
177    /// - `max_len`: Maximum accepted exact read length.
178    ///
179    /// # Returns
180    /// A vector containing exactly `len` bytes.
181    ///
182    /// # Errors
183    /// Returns [`ErrorKind::InvalidData`] when `len > max_len`. Returns the
184    /// error reported by [`Read::read_exact`], including
185    /// [`ErrorKind::UnexpectedEof`] when EOF is reached before `len` bytes are
186    /// read.
187    fn read_exact_vec_limited(
188        &mut self,
189        len: usize,
190        max_len: usize,
191    ) -> Result<Vec<u8>>;
192
193    /// Reads exactly `len` bytes and appends them to `output`.
194    ///
195    /// If `len` is greater than `max_len`, this method returns
196    /// [`ErrorKind::InvalidData`] before reading any bytes and leaves `output`
197    /// unchanged. On a read error, `output` is truncated back to its original
198    /// length. The underlying reader may still have consumed bytes before the
199    /// error because [`Read`] does not provide rollback.
200    ///
201    /// # Parameters
202    /// - `output`: Destination vector to append to.
203    /// - `len`: Exact number of bytes to read.
204    /// - `max_len`: Maximum accepted exact read length.
205    ///
206    /// # Errors
207    /// Returns [`ErrorKind::InvalidData`] when `len > max_len`. Returns the
208    /// error reported by [`Read::read_exact`], including
209    /// [`ErrorKind::UnexpectedEof`] when EOF is reached before `len` bytes are
210    /// read.
211    fn read_exact_vec_limited_into(
212        &mut self,
213        output: &mut Vec<u8>,
214        len: usize,
215        max_len: usize,
216    ) -> Result<()>;
217
218    /// Discards up to `bytes` bytes from this reader.
219    ///
220    /// The method repeatedly reads into an internal stack buffer until the
221    /// requested number of bytes has been consumed or EOF is reached. It does
222    /// not allocate and does not require seeking support.
223    ///
224    /// # Parameters
225    /// - `bytes`: Maximum number of bytes to discard.
226    ///
227    /// # Returns
228    /// The number of bytes actually discarded. The value may be smaller than
229    /// `bytes` when EOF is reached first.
230    ///
231    /// # Errors
232    /// Returns the first non-[`ErrorKind::Interrupted`] error reported by the
233    /// underlying reader. Interrupted reads are retried.
234    fn discard_exact_or_eof(&mut self, bytes: u64) -> Result<u64>;
235
236    /// Copies all remaining bytes from this reader into `writer`.
237    ///
238    /// This method is a method-style wrapper around [`std::io::copy`]. It
239    /// copies from the current reader position until EOF and does not close or
240    /// flush either stream.
241    ///
242    /// # Parameters
243    /// - `writer`: Destination writer.
244    ///
245    /// # Returns
246    /// The number of bytes copied.
247    ///
248    /// # Errors
249    /// Returns the first read or write error reported by the underlying
250    /// streams, using the same error behavior as [`std::io::copy`].
251    fn copy_to(&mut self, writer: &mut dyn Write) -> Result<u64>;
252
253    /// Copies at most `max_bytes` bytes from this reader into `writer`.
254    ///
255    /// This method stops successfully when either EOF is reached or
256    /// `max_bytes` bytes have been copied. It does not close or flush either
257    /// stream.
258    ///
259    /// # Parameters
260    /// - `writer`: Destination writer.
261    /// - `max_bytes`: Maximum number of bytes to copy.
262    ///
263    /// # Returns
264    /// The number of bytes copied.
265    ///
266    /// # Errors
267    /// Returns the first non-[`ErrorKind::Interrupted`] read error or write
268    /// error reported by the underlying streams. Interrupted reads are retried.
269    fn copy_to_at_most(
270        &mut self,
271        writer: &mut dyn Write,
272        max_bytes: u64,
273    ) -> Result<u64>;
274
275    /// Copies the remaining input if its total length is at most `max_bytes`.
276    ///
277    /// This method copies from the current reader position until EOF. If EOF is
278    /// not reached within `max_bytes` bytes, it returns
279    /// [`ErrorKind::InvalidData`]. Detecting oversized input consumes one
280    /// excess byte from this reader; that excess byte is not written to
281    /// `writer`.
282    ///
283    /// Unlike bounded reads into in-memory collections, this method cannot roll
284    /// back bytes already accepted by `writer` when the limit is exceeded
285    /// because [`Write`] does not provide truncation. On
286    /// [`ErrorKind::InvalidData`], up to `max_bytes` bytes may remain in
287    /// `writer`.
288    ///
289    /// # Parameters
290    /// - `writer`: Destination writer.
291    /// - `max_bytes`: Maximum accepted number of bytes in the remaining input.
292    ///
293    /// # Returns
294    /// The number of bytes copied when EOF is reached within the limit.
295    ///
296    /// # Errors
297    /// Returns [`ErrorKind::InvalidData`] when the remaining input is longer
298    /// than `max_bytes`. Returns the first non-[`ErrorKind::Interrupted`] read
299    /// error or write error reported by the underlying streams. Interrupted
300    /// reads are retried.
301    fn copy_to_end_limited(
302        &mut self,
303        writer: &mut dyn Write,
304        max_bytes: u64,
305    ) -> Result<u64>;
306
307    /// Reads the remaining bytes into a vector with a maximum accepted length.
308    ///
309    /// This method consumes bytes from the current reader position until EOF is
310    /// reached. If the stream contains more than `max_len` bytes, it returns
311    /// [`ErrorKind::InvalidData`] after detecting the first excess byte. No
312    /// vector is returned on failure.
313    ///
314    /// # Parameters
315    /// - `max_len`: Maximum number of bytes accepted in the returned vector.
316    ///
317    /// # Returns
318    /// A vector containing all remaining bytes when the stream length is within
319    /// the limit.
320    ///
321    /// # Errors
322    /// Returns [`ErrorKind::InvalidData`] when the stream contains more than
323    /// `max_len` bytes. Returns the first non-[`ErrorKind::Interrupted`] error
324    /// reported by the underlying reader; interrupted reads are retried.
325    fn read_to_end_limited(&mut self, max_len: usize) -> Result<Vec<u8>>;
326
327    /// Reads the remaining bytes into `output` with a maximum accepted length.
328    ///
329    /// This method appends at most `max_len` bytes from the current reader
330    /// position to `output`. If the stream contains more than `max_len` bytes,
331    /// it returns [`ErrorKind::InvalidData`] after detecting the first excess
332    /// byte and truncates `output` back to its original length. The underlying
333    /// reader may still have consumed bytes before the error because [`Read`]
334    /// does not provide rollback.
335    ///
336    /// # Parameters
337    /// - `output`: Destination vector to append to.
338    /// - `max_len`: Maximum number of bytes accepted from this reader.
339    ///
340    /// # Returns
341    /// The number of bytes appended to `output`.
342    ///
343    /// # Errors
344    /// Returns [`ErrorKind::InvalidData`] when the stream contains more than
345    /// `max_len` bytes. Returns the first non-[`ErrorKind::Interrupted`] error
346    /// reported by the underlying reader; interrupted reads are retried.
347    fn read_to_end_limited_into(
348        &mut self,
349        output: &mut Vec<u8>,
350        max_len: usize,
351    ) -> Result<usize>;
352
353    /// Reads the remaining bytes as UTF-8 text with a maximum accepted length.
354    ///
355    /// This method has the same size limit and read semantics as
356    /// [`ReadExt::read_to_end_limited`], then validates the collected bytes as
357    /// UTF-8.
358    ///
359    /// # Parameters
360    /// - `max_len`: Maximum number of bytes accepted before UTF-8 decoding.
361    ///
362    /// # Returns
363    /// The decoded UTF-8 string.
364    ///
365    /// # Errors
366    /// Returns [`ErrorKind::InvalidData`] when the stream contains more than
367    /// `max_len` bytes or when the collected bytes are not valid UTF-8. Returns
368    /// the first non-[`ErrorKind::Interrupted`] error reported by the
369    /// underlying reader; interrupted reads are retried.
370    fn read_to_string_limited(&mut self, max_len: usize) -> Result<String>;
371
372    /// Reads the remaining bytes as UTF-8 text and appends to `output`.
373    ///
374    /// This method accepts at most `max_len` bytes from the current reader
375    /// position, validates them as UTF-8, and appends the decoded text to
376    /// `output`. If the input is oversized or invalid UTF-8, `output` is
377    /// truncated back to its original length. Oversized input may still consume
378    /// up to `max_len + 1` bytes from the reader while detecting the limit
379    /// violation.
380    ///
381    /// # Parameters
382    /// - `output`: Destination string to append to.
383    /// - `max_len`: Maximum number of bytes accepted before UTF-8 decoding.
384    ///
385    /// # Returns
386    /// The number of bytes appended to `output`.
387    ///
388    /// # Errors
389    /// Returns [`ErrorKind::InvalidData`] when the stream contains more than
390    /// `max_len` bytes or when the collected bytes are not valid UTF-8. Returns
391    /// the first non-[`ErrorKind::Interrupted`] error reported by the
392    /// underlying reader; interrupted reads are retried.
393    fn read_to_string_limited_into(
394        &mut self,
395        output: &mut String,
396        max_len: usize,
397    ) -> Result<usize>;
398}
399
400impl<T> ReadExt for T
401where
402    T: Read + ?Sized,
403{
404    #[inline(always)]
405    unsafe fn read_unchecked(
406        &mut self,
407        buffer: &mut [u8],
408        start_index: usize,
409        count: usize,
410    ) -> Result<usize> {
411        debug_assert!(
412            UncheckedSlice::range_fits(buffer.len(), start_index, count),
413            "unchecked read range exceeds buffer"
414        );
415        // SAFETY: The caller guarantees that the requested range is valid for
416        // `buffer`.
417        let target =
418            unsafe { UncheckedSlice::subslice_mut(buffer, start_index, count) };
419        self.read(target)
420    }
421
422    unsafe fn read_exact_or_eof_unchecked(
423        &mut self,
424        buffer: &mut [u8],
425        start_index: usize,
426        count: usize,
427    ) -> Result<usize> {
428        debug_assert!(
429            UncheckedSlice::range_fits(buffer.len(), start_index, count),
430            "unchecked read range exceeds buffer"
431        );
432        let mut total = 0;
433        while total < count {
434            // SAFETY: The caller guarantees that `start_index..start_index +
435            // count` is valid for `buffer`; `total < count`, so this remaining
436            // suffix is also a valid mutable subslice.
437            let target = unsafe {
438                UncheckedSlice::subslice_mut(
439                    buffer,
440                    start_index + total,
441                    count - total,
442                )
443            };
444            match self.read(target) {
445                Ok(0) => break,
446                Ok(read) => total += read,
447                Err(error) => {
448                    if error.kind() == ErrorKind::Interrupted {
449                        continue;
450                    }
451                    return Err(error);
452                }
453            }
454        }
455        Ok(total)
456    }
457
458    unsafe fn read_exact_unchecked(
459        &mut self,
460        buffer: &mut [u8],
461        start_index: usize,
462        count: usize,
463    ) -> Result<()> {
464        debug_assert!(
465            UncheckedSlice::range_fits(buffer.len(), start_index, count),
466            "unchecked read range exceeds buffer"
467        );
468        // SAFETY: The caller guarantees that the requested range is valid for
469        // `buffer`.
470        let target =
471            unsafe { UncheckedSlice::subslice_mut(buffer, start_index, count) };
472        self.read_exact(target)
473    }
474
475    fn read_exact_or_eof(&mut self, buffer: &mut [u8]) -> Result<usize> {
476        let mut reader = self;
477        read_ext_impl::read_exact_or_eof(&mut reader, buffer)
478    }
479
480    #[inline(always)]
481    fn read_exact_array<const N: usize>(&mut self) -> Result<[u8; N]> {
482        let mut buffer = [0; N];
483        self.read_exact(&mut buffer)?;
484        Ok(buffer)
485    }
486
487    fn read_exact_vec_limited(
488        &mut self,
489        len: usize,
490        max_len: usize,
491    ) -> Result<Vec<u8>> {
492        let mut output = Vec::new();
493        let mut reader = self;
494        read_ext_impl::read_exact_vec_limited_into(
495            &mut reader,
496            &mut output,
497            len,
498            max_len,
499        )?;
500        Ok(output)
501    }
502
503    #[inline(always)]
504    fn read_exact_vec_limited_into(
505        &mut self,
506        output: &mut Vec<u8>,
507        len: usize,
508        max_len: usize,
509    ) -> Result<()> {
510        let mut reader = self;
511        read_ext_impl::read_exact_vec_limited_into(
512            &mut reader,
513            output,
514            len,
515            max_len,
516        )
517    }
518
519    fn discard_exact_or_eof(&mut self, bytes: u64) -> Result<u64> {
520        let mut buffer = [0; DISCARD_BUFFER_SIZE];
521        let mut remaining = bytes;
522        let mut discarded = 0;
523        while remaining > 0 {
524            let requested = remaining.min(DISCARD_BUFFER_SIZE as u64) as usize;
525            match self.read(&mut buffer[..requested]) {
526                Ok(0) => break,
527                Ok(count) => {
528                    let count = count as u64;
529                    remaining -= count;
530                    discarded += count;
531                }
532                Err(error) => {
533                    if error.kind() == ErrorKind::Interrupted {
534                        continue;
535                    }
536                    return Err(error);
537                }
538            }
539        }
540        Ok(discarded)
541    }
542
543    #[inline(always)]
544    fn copy_to(&mut self, writer: &mut dyn Write) -> Result<u64> {
545        copy_all(self, writer)
546    }
547
548    #[inline(always)]
549    fn copy_to_at_most(
550        &mut self,
551        writer: &mut dyn Write,
552        max_bytes: u64,
553    ) -> Result<u64> {
554        let mut reader = self;
555        Streams::copy_at_most(&mut reader, writer, max_bytes)
556    }
557
558    #[inline(always)]
559    fn copy_to_end_limited(
560        &mut self,
561        writer: &mut dyn Write,
562        max_bytes: u64,
563    ) -> Result<u64> {
564        let mut reader = self;
565        Streams::copy_to_end_limited(&mut reader, writer, max_bytes)
566    }
567
568    #[inline(always)]
569    fn read_to_end_limited(&mut self, max_len: usize) -> Result<Vec<u8>> {
570        let mut reader = self;
571        read_ext_impl::read_to_end_limited(&mut reader, max_len)
572    }
573
574    #[inline(always)]
575    fn read_to_end_limited_into(
576        &mut self,
577        output: &mut Vec<u8>,
578        max_len: usize,
579    ) -> Result<usize> {
580        let mut reader = self;
581        read_ext_impl::read_to_end_limited_into(&mut reader, output, max_len)
582    }
583
584    fn read_to_string_limited(&mut self, max_len: usize) -> Result<String> {
585        let mut reader = self;
586        let bytes = read_ext_impl::read_to_end_limited(&mut reader, max_len)?;
587        String::from_utf8(bytes).map_err(read_ext_impl::invalid_utf8_error)
588    }
589
590    fn read_to_string_limited_into(
591        &mut self,
592        output: &mut String,
593        max_len: usize,
594    ) -> Result<usize> {
595        let mut reader = self;
596        let bytes = read_ext_impl::read_to_end_limited(&mut reader, max_len)?;
597        let text = String::from_utf8(bytes)
598            .map_err(read_ext_impl::invalid_utf8_error)?;
599        let count = text.len();
600        try_reserve_string(output, count)?;
601        output.push_str(&text);
602        Ok(count)
603    }
604}