Skip to content

File::read with a zero-sized buffer closes the stream #1066

Closed
@abr-egn

Description

@abr-egn

Calling File::read with a zero-sized buffer results in all subsequent reads returning 0 even when more data exists.

Minimal repro:

use futures::AsyncReadExt;

type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

#[async_std::main]
async fn main() -> Result<()> {
    let path = std::path::Path::new("testfile");
    async_std::fs::write(path, [0; 8]).await?;
    let mut file = async_std::fs::File::open(path).await?;
    let mut buf = [0; 2];
    assert_eq!(2, file.read(&mut buf).await?);
    assert_eq!(0, file.read(&mut []).await?);
    assert_eq!(2, file.read(&mut buf).await?);  // this fails

    Ok(())
}

This is easily encountered when reading a stream into fixed-size chunks, e.g. repeatedly calling

async fn read_exact_or_to_end<T>(buf: &mut [u8], source: &mut T) -> Result<usize>
where
    T: AsyncRead + Unpin,
{
    let mut total_bytes_read = 0;
    loop {
        let bytes_read = match dbg!(source.read(&mut buf[total_bytes_read..]).await?) {
            0 => break,
            n => n,
        };
        total_bytes_read += bytes_read;
    }

    Ok(total_bytes_read)
}

This is straightforwardly worked around by checking if the number of bytes read is the size of the buffer, but it's easy to accidentally encounter and a hassle to debug.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions