3

I would like to skip an arbitrary number of bytes when working with a Read instance without doing any allocations. After skipping, I need to continue reading the data that follows.

The number of bytes is not known at compile time, so I cannot create a fixed array. Read also has no skip so I need to read into something, it seems. I do not want to use BufReader and allocate unnecessary buffers and I do not want to read byte-by-byte as this is inefficient.

Any other options?

trent
  • 25,033
  • 7
  • 51
  • 90
puritii
  • 1,069
  • 1
  • 7
  • 18
  • there is none and you should forget it, short version is you need a buffer (stack or heap, doesn't matter but I advice you heap), because you are the user so in the user space, kernel will NEVER allow any user code to run in kernel space so you need a buffer. Also, anyway like you will use your data at some point you will need a buffer, so answer is use a buffer. – Stargateur Jan 20 '20 at 15:30
  • 1
    @Stargateur I've worked with binary protocols where I literally didn't care about parts of the data (but I knew how big it was). This seems to be one of those case. – mcarton Jan 20 '20 at 15:50
  • I would just read into an array on the stack (in a loop if the array isn't big enough). The size of said array would depend on your platform and use case but I'd probably go with 1KB by default unless I have a good reason to tune this better. – mcarton Jan 20 '20 at 15:52
  • How do you get your `Read` instance? If it also implements [`Seek`](https://doc.rust-lang.org/std/io/trait.Seek.html), you can use `r.seek (SeekFrom::Current (n))` to skip `n` bytes. – Jmb Jan 20 '20 at 15:53
  • The link above was a duplicate target for another similar question: https://stackoverflow.com/q/51811064/1233251 – E_net4 Jan 20 '20 at 15:56
  • 1
    Reading byte-by-byte may be inefficient *for streams that already have an internal buffer*. If there is no buffer, byte-by-byte is the only way you *can* read the stream (unless it happens to also implement `Seek`, as Shepmaster's answer points out). – trent Jan 20 '20 at 15:58
  • 1
    @Stargateur "anyway like you will use your data at some point" ... no. This is the precisely the reason for this question, because I absolutely will not need it and want to avoid time and space implications. – puritii Jan 21 '20 at 15:22
  • @mcarton Yes, I am parsing binary protocols. – puritii Jan 21 '20 at 15:22
  • @puritii I wanted to say, to use the data you really want you will need a buffer anyway. – Stargateur Jan 21 '20 at 15:27

1 Answers1

4

Your best bet is to also require Seek:

use std::io::{self, Read, Seek, SeekFrom};

fn example(mut r: impl Read + Seek) -> io::Result<String> {
    r.seek(SeekFrom::Current(5))?;

    let mut s = String::new();
    r.take(5).read_to_string(&mut s)?;

    Ok(s)
}

#[test]
fn it_works() -> io::Result<()> {
    use std::io::Cursor;

    let s = example(Cursor::new("abcdefghijklmnop"))?;
    assert_eq!("fghij", s);
    Ok(())
}

If you cannot use Seek, then see How to advance through data from the std::io::Read trait when Seek isn't implemented?

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366