Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read a specific number of bytes from a stream?

Tags:

rust

I have a struct with a BufStream<T> where T: Read+Write. The BufStream can be a TcpStream and I'd like to read n bytes from it. Not a fixed amount of bytes in a predefined buffer, but I have a string/stream which indicates the number of bytes to read next.

Is there a nice way to do that?

like image 754
buster Avatar asked May 23 '15 12:05

buster


2 Answers

Since Rust 1.6, Read::read_exact can be used to do this. If bytes_to_read is the number of bytes you need to read, possibly determined at runtime, and reader is the stream to read from:

let mut buf = vec![0u8; bytes_to_read];
reader.read_exact(&mut buf)?;

The part that wasn't clear to me from the read_exact documentation was that the target buffer can be a dynamically-allocated Vec.

Thanks to the Rust Gitter community for pointing me to this solution.

like image 50
rspeer Avatar answered Oct 21 '22 18:10

rspeer


It sounds like you want Read::take and Read::read_to_end.

This will allow you to read data into a &mut Vec<u8>, which is useful when you want to reuse an existing buffer or don't have an appropriately sized slice already. This allows you to avoid initializing the data with dummy values before overwriting them with the newly-read information:

use std::{
    io::{prelude::*, BufReader},
    str,
};

fn read_n<R>(reader: R, bytes_to_read: u64) -> Vec<u8>
where
    R: Read,
{
    let mut buf = vec![];
    let mut chunk = reader.take(bytes_to_read);
    // Do appropriate error handling for your situation
    // Maybe it's OK if you didn't read enough bytes?
    let n = chunk.read_to_end(&mut buf).expect("Didn't read enough");
    assert_eq!(bytes_to_read as usize, n);
    buf
}

fn main() {
    let input_data = b"hello world";
    let mut reader = BufReader::new(&input_data[..]);

    let first = read_n(&mut reader, 5);
    let _ = read_n(&mut reader, 1);
    let second = read_n(&mut reader, 5);

    println!(
        "{:?}, {:?}",
        str::from_utf8(&first),
        str::from_utf8(&second)
    );
}

If you are worried that Read::take consumes the reader by reference, note that take comes from Read and Read is implemented for any mutable reference to a type that implements Read. You can also use Read::by_ref to create this mutable reference.

See also:

  • Whats the idiomatic way to reference BufReader/BufWriter when passing it between functions?
  • Why does Iterator::take_while take ownership of the iterator?
like image 38
Shepmaster Avatar answered Oct 21 '22 16:10

Shepmaster