Skip to content

Commit 1a6f7ec

Browse files
authored
Add BufReader::seek_relative (#2489)
1 parent 4480042 commit 1a6f7ec

File tree

5 files changed

+279
-95
lines changed

5 files changed

+279
-95
lines changed

futures-util/src/io/buf_reader.rs

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

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
@@ -817,6 +817,12 @@ pub mod io {
817817
assert_impl!(Seek<'_, ()>: Unpin);
818818
assert_not_impl!(Seek<'_, PhantomPinned>: Unpin);
819819

820+
assert_impl!(SeeKRelative<'_, ()>: Send);
821+
assert_not_impl!(SeeKRelative<'_, *const ()>: Send);
822+
assert_impl!(SeeKRelative<'_, ()>: Sync);
823+
assert_not_impl!(SeeKRelative<'_, *const ()>: Sync);
824+
assert_impl!(SeeKRelative<'_, PhantomPinned>: Unpin);
825+
820826
assert_impl!(Sink: Send);
821827
assert_impl!(Sink: Sync);
822828
assert_impl!(Sink: Unpin);

0 commit comments

Comments
 (0)