Skip to content

Commit 80e2e67

Browse files
committed
Auto merge of #46832 - Diggsey:bufread-cheaper-seek, r=alexcrichton
BufRead: Only flush the internal buffer if seeking outside of it. Fixes #31100 r? @dtolnay
2 parents cf4c3cb + c96f302 commit 80e2e67

File tree

1 file changed

+46
-0
lines changed

1 file changed

+46
-0
lines changed

src/libstd/io/buffered.rs

+46
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,31 @@ impl<R: Read> BufReader<R> {
194194
pub fn into_inner(self) -> R { self.inner }
195195
}
196196

197+
impl<R: Seek> BufReader<R> {
198+
/// Seeks relative to the current position. If the new position lies within the buffer,
199+
/// the buffer will not be flushed, allowing for more efficient seeks.
200+
/// This method does not return the location of the underlying reader, so the caller
201+
/// must track this information themselves if it is required.
202+
#[unstable(feature = "bufreader_seek_relative", issue = "31100")]
203+
pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
204+
let pos = self.pos as u64;
205+
if offset < 0 {
206+
if let Some(new_pos) = pos.checked_sub((-offset) as u64) {
207+
self.pos = new_pos as usize;
208+
return Ok(())
209+
}
210+
} else {
211+
if let Some(new_pos) = pos.checked_add(offset as u64) {
212+
if new_pos <= self.cap as u64 {
213+
self.pos = new_pos as usize;
214+
return Ok(())
215+
}
216+
}
217+
}
218+
self.seek(SeekFrom::Current(offset)).map(|_|())
219+
}
220+
}
221+
197222
#[stable(feature = "rust1", since = "1.0.0")]
198223
impl<R: Read> Read for BufReader<R> {
199224
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
@@ -260,13 +285,17 @@ impl<R: Seek> Seek for BufReader<R> {
260285
/// `.into_inner()` immediately after a seek yields the underlying reader
261286
/// at the same position.
262287
///
288+
/// To seek without discarding the internal buffer, use [`seek_relative`].
289+
///
263290
/// See `std::io::Seek` for more details.
264291
///
265292
/// Note: In the edge case where you're seeking with `SeekFrom::Current(n)`
266293
/// where `n` minus the internal buffer length overflows an `i64`, two
267294
/// seeks will be performed instead of one. If the second seek returns
268295
/// `Err`, the underlying reader will be left at the same position it would
269296
/// have if you seeked to `SeekFrom::Current(0)`.
297+
///
298+
/// [`seek_relative`]: #method.seek_relative
270299
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
271300
let result: u64;
272301
if let SeekFrom::Current(n) = pos {
@@ -953,6 +982,23 @@ mod tests {
953982
assert_eq!(reader.seek(SeekFrom::Current(-2)).ok(), Some(3));
954983
}
955984

985+
#[test]
986+
fn test_buffered_reader_seek_relative() {
987+
let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
988+
let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner));
989+
990+
assert!(reader.seek_relative(3).is_ok());
991+
assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..]));
992+
assert!(reader.seek_relative(0).is_ok());
993+
assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..]));
994+
assert!(reader.seek_relative(1).is_ok());
995+
assert_eq!(reader.fill_buf().ok(), Some(&[1][..]));
996+
assert!(reader.seek_relative(-1).is_ok());
997+
assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..]));
998+
assert!(reader.seek_relative(2).is_ok());
999+
assert_eq!(reader.fill_buf().ok(), Some(&[2, 3][..]));
1000+
}
1001+
9561002
#[test]
9571003
fn test_buffered_reader_seek_underflow() {
9581004
// gimmick reader that yields its position modulo 256 for each byte

0 commit comments

Comments
 (0)