I've a situation where I have a destination Vec to which I want to push exactly n bytes I read from a Read object.
Read, however, generally deals with slices. The only method which uses Vec is read_to_end, but I need to "bound" the reading to only n bytes.
So far I can see 3 ways to implement this, but I'm not sure if I'm missing something, or which one is the best (or at least the least bad): playground
use read_to_end anyway
This is the shortest by far, but it also involves the most stdlib understanding, and I'm not sure I grasp the semantics completely / correctly:
fn read1<T: Read>(mut reader: T, n: usize, sink: &mut Vec<u8>) -> Result<()> {
reader.take(n as u64).read_to_end(sink)?;
Ok(())
}
If this were not a utility function I think by_ref would also be useful in order to not consume the source reader?
extend the Vec and read into that buffer
This is way more involved and has the issue that we're adding completely invalid data to the Vec in order to have a proper "slice" to read into:
fn read2<T: Read>(mut reader: T, n: usize, sink: &mut Vec<u8>) -> Result<()> {
let before = sink.len();
let after = sink.len() + n;
sink.resize(after, 0);
if let Err(e) = reader.read_exact(&mut sink[before..after]) {
sink.resize(before, 0);
return Err(e.into());
}
Ok(())
}
This assumes read_exact and resize (down) don't panic.
use a local buffer
This is the most simplistic and complicated: repeatedly read data into a local buffer, then copy from the local buffer into the output vec.
fn read3<T: Read>(mut reader: T, mut n: usize, sink: &mut Vec<u8>) -> Result<()> {
let before = sink.len();
let mut buf = [0;64];
while n > 0 {
let b = &mut buf[0..min(64, n)];
if let Err(e) = reader.read_exact(b) {
sink.resize(before, 0);
return Err(e.into());
}
sink.extend(&*b);
n -= b.len();
}
Ok(())
}
It's what I'd do in a lower level language but it fells icky. Compared to version 2 it does add data to the output vec which we may need to strip out (I elected to do this here but there may be case where that's unnecessary), but unlike solution 2 while it adds partial data to the vec it's valid data.