Skip to content

Commit 7d91039

Browse files
committed
Add BufRead::skip_until
1 parent 53792b9 commit 7d91039

File tree

1 file changed

+84
-0
lines changed

1 file changed

+84
-0
lines changed

library/std/src/io/mod.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1923,6 +1923,28 @@ fn read_until<R: BufRead + ?Sized>(r: &mut R, delim: u8, buf: &mut Vec<u8>) -> R
19231923
}
19241924
}
19251925

1926+
fn skip_until<R: BufRead + ?Sized>(r: &mut R, delim: u8) -> Result<usize> {
1927+
let mut read = 0;
1928+
loop {
1929+
let (done, used) = {
1930+
let available = match r.fill_buf() {
1931+
Ok(n) => n,
1932+
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
1933+
Err(e) => return Err(e),
1934+
};
1935+
match memchr::memchr(delim, available) {
1936+
Some(i) => (true, i + 1),
1937+
None => (false, available.len()),
1938+
}
1939+
};
1940+
r.consume(used);
1941+
read += used;
1942+
if done || used == 0 {
1943+
return Ok(read);
1944+
}
1945+
}
1946+
}
1947+
19261948
/// A `BufRead` is a type of `Read`er which has an internal buffer, allowing it
19271949
/// to perform extra ways of reading.
19281950
///
@@ -2126,6 +2148,68 @@ pub trait BufRead: Read {
21262148
read_until(self, byte, buf)
21272149
}
21282150

2151+
/// Skip all bytes until the delimiter `byte` or EOF is reached.
2152+
///
2153+
/// This function will read (and discard) bytes from the underlying stream until the
2154+
/// delimiter or EOF is found.
2155+
///
2156+
/// If successful, this function will return the total number of bytes read,
2157+
/// including the delimiter byte.
2158+
///
2159+
/// This is useful for efficiently skipping data such as NUL-terminated strings
2160+
/// in binary file formats without buffering.
2161+
///
2162+
/// This function is blocking and should be used carefully: it is possible for
2163+
/// an attacker to continuously send bytes without ever sending the delimiter
2164+
/// or EOF.
2165+
///
2166+
/// # Errors
2167+
///
2168+
/// This function will ignore all instances of [`ErrorKind::Interrupted`] and
2169+
/// will otherwise return any errors returned by [`fill_buf`].
2170+
///
2171+
/// If an I/O error is encountered then all bytes read so far will be
2172+
/// present in `buf` and its length will have been adjusted appropriately.
2173+
///
2174+
/// [`fill_buf`]: BufRead::fill_buf
2175+
///
2176+
/// # Examples
2177+
///
2178+
/// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In
2179+
/// this example, we use [`Cursor`] to read some NUL-terminated information
2180+
/// about Ferris from a binary string, skipping the fun fact:
2181+
///
2182+
/// ```
2183+
/// #![feature(bufread_skip_until)]
2184+
///
2185+
/// use std::io::{self, BufRead};
2186+
///
2187+
/// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0");
2188+
///
2189+
/// // read name
2190+
/// let mut name = Vec::new();
2191+
/// let num_bytes = cursor.read_until(b'\0', &mut name)
2192+
/// .expect("reading from cursor won't fail");
2193+
/// assert_eq!(num_bytes, 7);
2194+
/// assert_eq!(name, b"Ferris\0");
2195+
///
2196+
/// // skip fun fact
2197+
/// let num_bytes = cursor.skip_until(b'\0')
2198+
/// .expect("reading from cursor won't fail");
2199+
/// assert_eq!(num_bytes, 30);
2200+
///
2201+
/// // read animal type
2202+
/// let mut animal = Vec::new();
2203+
/// let num_bytes = cursor.read_until(b'\0', &mut animal)
2204+
/// .expect("reading from cursor won't fail");
2205+
/// assert_eq!(num_bytes, 11);
2206+
/// assert_eq!(animal, b"Crustacean\0");
2207+
/// ```
2208+
#[unstable(feature = "bufread_skip_until", issue = "none")]
2209+
fn skip_until(&mut self, byte: u8) -> Result<usize> {
2210+
skip_until(self, byte)
2211+
}
2212+
21292213
/// Read all bytes until a newline (the `0xA` byte) is reached, and append
21302214
/// them to the provided buffer. You do not need to clear the buffer before
21312215
/// appending.

0 commit comments

Comments
 (0)