ffmpeg_sidecar/read_until_any.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
//! Internal utility; `BufRead::read_until` with multiple delimiters.
use std::io::{BufRead, ErrorKind, Result};
/// Reads from the provided buffer until any of the delimiter bytes match.
/// The output buffer will include the ending delimiter.
/// Also skips over zero-length reads.
/// See [`BufRead::read_until`](https://doc.rust-lang.org/std/io/trait.BufRead.html#method.read_until).
pub fn read_until_any<R: BufRead + ?Sized>(
r: &mut R,
delims: &[u8],
buf: &mut Vec<u8>,
) -> Result<usize> {
let mut read = 0;
loop {
let (done, used) = {
let available = match r.fill_buf() {
Ok(n) => n,
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
};
let start_delims = if read == 0 {
available
.iter()
.take_while(|&&b| delims.iter().any(|&d| d == b))
.count()
} else {
0
};
// NB: `memchr` crate would be faster, but it's unstable and not worth the dependency.
let first_delim_index = available
.iter()
.skip(start_delims)
.position(|&b| delims.iter().any(|&d| d == b))
.map(|i| i + start_delims);
match first_delim_index {
Some(i) => {
buf.extend_from_slice(&available[..=i]);
(true, i + 1)
}
None => {
buf.extend_from_slice(available);
(false, available.len())
}
}
};
r.consume(used);
read += used;
if done {
return Ok(read);
}
// Discard final trailing delimiters
if used == 0 && buf.iter().all(|&b| delims.iter().any(|&d| d == b)) {
return Ok(0);
}
if used == 0 {
return Ok(read);
}
}
}