ffmpeg_sidecar/
read_until_any.rs

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