Skip to main content

Read

Trait Read 

Source
pub trait Read:
    HasMemory
    + Memory
    + Debug
    + Send {
    type Error: Error + Send + Sync + 'static;

    // Required methods
    fn read_at_most_into(
        &mut self,
        len: usize,
        into: BytesBuf,
    ) -> impl Future<Output = Result<(usize, BytesBuf), Self::Error>> + Send;
    fn read_more_into(
        &mut self,
        into: BytesBuf,
    ) -> impl Future<Output = Result<(usize, BytesBuf), Self::Error>> + Send;
    fn read_any(
        &mut self,
    ) -> impl Future<Output = Result<BytesBuf, Self::Error>> + Send;
}
Expand description

Allows for reading of bytes.

Only supports asynchronous access, exposing the read byte sequences as bytesbuf::BytesBuf.

§Ownership

The methods on this trait accept &mut self and take an exclusive reference to the source for the duration of the operation. This implies that only one read operation can be concurrently executed on the object.

§Memory management for efficient I/O

For optimal efficiency when performing I/O, reads should be performed into memory optimized for the underlying I/O endpoint. This is achieved by reserving memory from the implementation’s memory provider before performing the read operation.

There are three ways to ensure you are using memory suitable for optimally efficient I/O:

  1. If you call methods that do not accept a BytesBuf (such as read_any()), the implementation will reserve memory from its memory provider internally. This is the simplest way to perform reads but only a limited API surface is available in this mode.
  2. You may call Memory::reserve() on the implementation to reserve memory from its memory provider explicitly. This allows you to control the memory allocation more finely and potentially reuse existing buffers, improving efficiency.
  3. You may sometimes want to call reserve() at certain times when Rust borrowing rules do not allow you to call it directly on the implementation because it has already been borrowed. In this case, you can obtain an independent reference to the memory provider first via HasMemory::memory(), which allows you to bypass the need to borrow the implementing object itself.

Some implementations do not perform real I/O and only move data around in memory. Such implementations typically do not have any special memory requirements and will operate with the same efficiency regardless of which buffers the data is in. Any relaxed behaviors like this will typically be described in the implementation’s API documentation.

§Thread safety

This trait requires Send from both the implementation and any returned futures.

Required Associated Types§

Source

type Error: Error + Send + Sync + 'static

Type used to signal errors by the implementation of this trait.

Required Methods§

Source

fn read_at_most_into( &mut self, len: usize, into: BytesBuf, ) -> impl Future<Output = Result<(usize, BytesBuf), Self::Error>> + Send

Reads at most len bytes into the provided buffer.

It is not necessary for into to be empty - the buffer may already have some bytes of data in it (e.g. from a previous read).

The buffer will be extended with additional memory capacity if it does not have enough remaining capacity to fit len additional bytes.

Returns a tuple of the number of bytes read and the updated buffer.

The returned BytesBuf will have 0 or more bytes of data appended to it on success, with 0 appended bytes indicating that no more bytes can be read from this source. Any data that was already in the buffer will remain untouched.

§Example
use bytesbuf::mem::Memory;
use bytesbuf_io::Read;

let mut source = get_source();

let buf = source.reserve(100);

let (bytes_read, mut buf) = source.read_at_most_into(10, buf).await.unwrap();

assert!(bytes_read <= 10);

let bytes = buf.consume_all();
Source

fn read_more_into( &mut self, into: BytesBuf, ) -> impl Future<Output = Result<(usize, BytesBuf), Self::Error>> + Send

Reads an unspecified number of bytes into the provided buffer.

The implementation will decide how many bytes to read based on its internal understanding of what is optimal for sustained throughput at high efficiency. This may be a fixed size, or it may be a variable size based on the current state of the source.

It is not necessary for into to be empty - the buffer may already have some bytes of data in it (e.g. from a previous read).

The buffer will be extended with additional memory capacity if it does not have enough remaining capacity to fit len additional bytes.

Returns a tuple of the number of bytes read and the updated buffer.

The returned BytesBuf will have 0 or more bytes of data appended to it on success, with 0 appended bytes indicating that no more bytes can be read from this source. Any data that was already in the buffer will remain untouched.

§Example
use bytesbuf_io::Read;

let mut source = get_source();

let mut buf = source.read_any().await.unwrap();

if buf.len() == 0 {
    println!("Source ended immediately - nothing we can do.");
    return;
}

loop {
    // We want at least 1 MB of data, read in whatever sized pieces the I/O endpoint
    // considers optimal.
    if buf.len() >= 1024 * 1024 {
        println!("Got 1 MB of data");
        break;
    }

    let (bytes_read, new_buf) = source.read_more_into(buf).await.unwrap();
    buf = new_buf;

    if bytes_read == 0 {
        println!("Source ended - no more data before we reached 1 MB");
        break;
    }
}
Source

fn read_any( &mut self, ) -> impl Future<Output = Result<BytesBuf, Self::Error>> + Send

Reads an unspecified number of bytes as a new buffer.

The implementation will decide how many bytes to read based on its internal understanding of what is optimal for sustained throughput at high efficiency. This may be a fixed size, or it may be a variable size based on the current state of the source.

The returned BytesBuf will have 0 or more bytes of data appended to it on success, with 0 appended bytes indicating that no more bytes can be read from this source.

§Security

This method is insecure if the side producing the bytes is not trusted. An attacker may trickle data byte-by-byte, consuming a large amount of I/O resources.

Robust code working with untrusted sources should take precautions such as only processing read data when either a time or length threshold is reached and reusing buffers that have remaining capacity, appending additional data to existing buffers using read_more_into() instead of reserving new buffers for each read operation.

§Example
use bytesbuf_io::Read;

let mut source = get_source();

let buf = source.read_any().await.unwrap();

println!("first read produced {} bytes of data", buf.len());

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§

Source§

impl Read for FakeRead

Available on crate features test-util only.
Source§

impl Read for Null

Available on crate features test-util only.
Source§

impl Read for Pending

Available on crate features test-util only.