scuffle_bytes_util/
zero_copy.rs

1//! Zero-copy reader types.
2
3use std::io;
4
5use crate::BytesCow;
6
7/// A trait for zero-copy readers.
8pub trait ZeroCopyReader<'a> {
9    /// Attempts to read a specified number of bytes from the reader without copying.
10    ///
11    /// This function does not guarantee that no copying will occur.
12    /// Some implementations can't avoid copying.
13    fn try_read(&mut self, size: usize) -> Result<BytesCow<'a>, io::Error>;
14
15    /// Returns a standard [`io::Read`] interface for the reader.
16    fn as_std(&mut self) -> impl io::Read;
17}
18
19/// A zero-copy reader that wraps a [`bytes::Buf`].
20pub struct BytesBuf<B>(B);
21
22impl<B: bytes::Buf> From<B> for BytesBuf<B> {
23    fn from(buf: B) -> Self {
24        Self(buf)
25    }
26}
27
28impl<'a, B: bytes::Buf> ZeroCopyReader<'a> for BytesBuf<B> {
29    fn try_read(&mut self, size: usize) -> Result<BytesCow<'a>, io::Error> {
30        if self.0.remaining() < size {
31            return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "Not enough data"));
32        }
33
34        Ok(BytesCow::from_bytes(self.0.copy_to_bytes(size)))
35    }
36
37    fn as_std(&mut self) -> impl io::Read {
38        bytes::Buf::reader(&mut self.0)
39    }
40}
41
42/// A zero-copy reader that wraps a [`std::io::Read`].
43///
44/// This implementation is not zero-copy and will always copy the data into a new buffer.
45/// It is not possible to implement zero-copy reading for [`std::io::Read`]
46/// because it does not provide a way to access the underlying buffer directly.
47pub struct IoRead<R>(R);
48
49impl<R: io::Read> From<R> for IoRead<R> {
50    fn from(reader: R) -> Self {
51        Self(reader)
52    }
53}
54
55impl<'a, R: io::Read> ZeroCopyReader<'a> for IoRead<R> {
56    fn try_read(&mut self, size: usize) -> Result<BytesCow<'a>, io::Error> {
57        let mut buf = vec![0; size];
58        self.0.read_exact(&mut buf)?;
59        Ok(BytesCow::from_vec(buf))
60    }
61
62    fn as_std(&mut self) -> impl io::Read {
63        &mut self.0
64    }
65}
66
67/// A zero-copy reader that wraps a byte slice (`&[u8]`).
68pub struct Slice<'a>(io::Cursor<&'a [u8]>);
69
70impl<'a> From<&'a [u8]> for Slice<'a> {
71    fn from(slice: &'a [u8]) -> Self {
72        Self(io::Cursor::new(slice))
73    }
74}
75
76impl<'a> ZeroCopyReader<'a> for Slice<'a> {
77    fn try_read(&mut self, size: usize) -> Result<BytesCow<'a>, io::Error> {
78        let start = self.0.position() as usize;
79        let end = start + size;
80
81        if end > self.0.get_ref().len() {
82            return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "Not enough data"));
83        }
84
85        let slice = &self.0.get_ref()[start..end];
86        self.0.set_position(end as u64);
87        Ok(BytesCow::from_slice(slice))
88    }
89
90    fn as_std(&mut self) -> impl io::Read {
91        &mut self.0
92    }
93}