Skip to content

Commit 7a17957

Browse files
committed
Add BufReader::seek_relative
1 parent 0e3e48c commit 7a17957

File tree

4 files changed

+129
-1
lines changed

4 files changed

+129
-1
lines changed

futures-util/src/io/buf_reader.rs

+71
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::DEFAULT_BUF_SIZE;
2+
use futures_core::future::Future;
23
use futures_core::ready;
34
use futures_core::task::{Context, Poll};
45
#[cfg(feature = "read-initializer")]
@@ -73,6 +74,43 @@ impl<R: AsyncRead> BufReader<R> {
7374
}
7475
}
7576

77+
impl<R: AsyncRead + AsyncSeek> BufReader<R> {
78+
/// Seeks relative to the current position. If the new position lies within the buffer,
79+
/// the buffer will not be flushed, allowing for more efficient seeks.
80+
/// This method does not return the location of the underlying reader, so the caller
81+
/// must track this information themselves if it is required.
82+
pub fn seek_relative(&mut self, offset: i64) -> SeeKRelative<'_, R>
83+
where
84+
R: Unpin,
85+
{
86+
SeeKRelative { inner: self, offset, first: true }
87+
}
88+
89+
/// Attempts to seek relative to the current position. If the new position lies within the buffer,
90+
/// the buffer will not be flushed, allowing for more efficient seeks.
91+
/// This method does not return the location of the underlying reader, so the caller
92+
/// must track this information themselves if it is required.
93+
pub fn poll_seek_relative(
94+
self: Pin<&mut Self>,
95+
cx: &mut Context<'_>,
96+
offset: i64,
97+
) -> Poll<io::Result<()>> {
98+
let pos = self.pos as u64;
99+
if offset < 0 {
100+
if let Some(new_pos) = pos.checked_sub((-offset) as u64) {
101+
*self.project().pos = new_pos as usize;
102+
return Poll::Ready(Ok(()));
103+
}
104+
} else if let Some(new_pos) = pos.checked_add(offset as u64) {
105+
if new_pos <= self.cap as u64 {
106+
*self.project().pos = new_pos as usize;
107+
return Poll::Ready(Ok(()));
108+
}
109+
}
110+
self.poll_seek(cx, SeekFrom::Current(offset)).map(|res| res.map(|_| ()))
111+
}
112+
}
113+
76114
impl<R: AsyncRead> AsyncRead for BufReader<R> {
77115
fn poll_read(
78116
mut self: Pin<&mut Self>,
@@ -163,6 +201,10 @@ impl<R: AsyncRead + AsyncSeek> AsyncSeek for BufReader<R> {
163201
/// `.into_inner()` immediately after a seek yields the underlying reader
164202
/// at the same position.
165203
///
204+
/// To seek without discarding the internal buffer, use
205+
/// [`BufReader::seek_relative`](BufReader::seek_relative) or
206+
/// [`BufReader::poll_seek_relative`](BufReader::poll_seek_relative).
207+
///
166208
/// See [`AsyncSeek`](futures_io::AsyncSeek) for more details.
167209
///
168210
/// Note: In the edge case where you're seeking with `SeekFrom::Current(n)`
@@ -200,3 +242,32 @@ impl<R: AsyncRead + AsyncSeek> AsyncSeek for BufReader<R> {
200242
Poll::Ready(Ok(result))
201243
}
202244
}
245+
246+
/// Future for the [`BufReader::seek_relative`](self::BufReader::seek_relative) method.
247+
#[derive(Debug)]
248+
#[must_use = "futures do nothing unless polled"]
249+
pub struct SeeKRelative<'a, R> {
250+
inner: &'a mut BufReader<R>,
251+
offset: i64,
252+
first: bool,
253+
}
254+
255+
impl<R> Future for SeeKRelative<'_, R>
256+
where
257+
R: AsyncRead + AsyncSeek + Unpin,
258+
{
259+
type Output = io::Result<()>;
260+
261+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
262+
let offset = self.offset;
263+
if self.first {
264+
self.first = false;
265+
Pin::new(&mut *self.inner).as_mut().poll_seek_relative(cx, offset)
266+
} else {
267+
Pin::new(&mut *self.inner)
268+
.as_mut()
269+
.poll_seek(cx, SeekFrom::Current(offset))
270+
.map(|res| res.map(|_| ()))
271+
}
272+
}
273+
}

futures-util/src/io/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ mod allow_std;
5656
pub use self::allow_std::AllowStdIo;
5757

5858
mod buf_reader;
59-
pub use self::buf_reader::BufReader;
59+
pub use self::buf_reader::{BufReader, SeeKRelative};
6060

6161
mod buf_writer;
6262
pub use self::buf_writer::BufWriter;

futures/tests/auto_traits.rs

+6
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,12 @@ pub mod io {
810810
assert_impl!(Seek<'_, ()>: Unpin);
811811
assert_not_impl!(Seek<'_, PhantomPinned>: Unpin);
812812

813+
assert_impl!(SeeKRelative<'_, ()>: Send);
814+
assert_not_impl!(SeeKRelative<'_, *const ()>: Send);
815+
assert_impl!(SeeKRelative<'_, ()>: Sync);
816+
assert_not_impl!(SeeKRelative<'_, *const ()>: Sync);
817+
assert_impl!(SeeKRelative<'_, PhantomPinned>: Unpin);
818+
813819
assert_impl!(Sink: Send);
814820
assert_impl!(Sink: Sync);
815821
assert_impl!(Sink: Unpin);

futures/tests/io_buf_reader.rs

+51
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,57 @@ fn test_buffered_reader_seek() {
130130
assert_eq!(block_on(reader.seek(SeekFrom::Current(-2))).ok(), Some(3));
131131
}
132132

133+
#[test]
134+
fn test_buffered_reader_seek_relative() {
135+
let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
136+
let mut reader = BufReader::with_capacity(2, Cursor::new(inner));
137+
138+
assert!(block_on(reader.seek_relative(3)).is_ok());
139+
assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..]));
140+
assert!(block_on(reader.seek_relative(0)).is_ok());
141+
assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..]));
142+
assert!(block_on(reader.seek_relative(1)).is_ok());
143+
assert_eq!(run_fill_buf!(reader).ok(), Some(&[1][..]));
144+
assert!(block_on(reader.seek_relative(-1)).is_ok());
145+
assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..]));
146+
assert!(block_on(reader.seek_relative(2)).is_ok());
147+
assert_eq!(run_fill_buf!(reader).ok(), Some(&[2, 3][..]));
148+
}
149+
150+
#[test]
151+
fn test_buffered_reader_invalidated_after_read() {
152+
let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
153+
let mut reader = BufReader::with_capacity(3, Cursor::new(inner));
154+
155+
assert_eq!(run_fill_buf!(reader).ok(), Some(&[5, 6, 7][..]));
156+
Pin::new(&mut reader).consume(3);
157+
158+
let mut buffer = [0, 0, 0, 0, 0];
159+
assert_eq!(block_on(reader.read(&mut buffer)).ok(), Some(5));
160+
assert_eq!(buffer, [0, 1, 2, 3, 4]);
161+
162+
assert!(block_on(reader.seek_relative(-2)).is_ok());
163+
let mut buffer = [0, 0];
164+
assert_eq!(block_on(reader.read(&mut buffer)).ok(), Some(2));
165+
assert_eq!(buffer, [3, 4]);
166+
}
167+
168+
#[test]
169+
fn test_buffered_reader_invalidated_after_seek() {
170+
let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
171+
let mut reader = BufReader::with_capacity(3, Cursor::new(inner));
172+
173+
assert_eq!(run_fill_buf!(reader).ok(), Some(&[5, 6, 7][..]));
174+
Pin::new(&mut reader).consume(3);
175+
176+
assert!(block_on(reader.seek(SeekFrom::Current(5))).is_ok());
177+
178+
assert!(block_on(reader.seek_relative(-2)).is_ok());
179+
let mut buffer = [0, 0];
180+
assert_eq!(block_on(reader.read(&mut buffer)).ok(), Some(2));
181+
assert_eq!(buffer, [3, 4]);
182+
}
183+
133184
#[test]
134185
fn test_buffered_reader_seek_underflow() {
135186
// gimmick reader that yields its position modulo 256 for each byte

0 commit comments

Comments
 (0)