|
1 | 1 | use super::DEFAULT_BUF_SIZE;
|
| 2 | +use futures_core::future::Future; |
2 | 3 | use futures_core::ready;
|
3 | 4 | use futures_core::task::{Context, Poll};
|
4 | 5 | #[cfg(feature = "read-initializer")]
|
@@ -73,6 +74,40 @@ impl<R: AsyncRead> BufReader<R> {
|
73 | 74 | }
|
74 | 75 | }
|
75 | 76 |
|
| 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 | + |
76 | 111 | impl<R: AsyncRead> AsyncRead for BufReader<R> {
|
77 | 112 | fn poll_read(
|
78 | 113 | mut self: Pin<&mut Self>,
|
@@ -163,6 +198,10 @@ impl<R: AsyncRead + AsyncSeek> AsyncSeek for BufReader<R> {
|
163 | 198 | /// `.into_inner()` immediately after a seek yields the underlying reader
|
164 | 199 | /// at the same position.
|
165 | 200 | ///
|
| 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 | + /// |
166 | 205 | /// See [`AsyncSeek`](futures_io::AsyncSeek) for more details.
|
167 | 206 | ///
|
168 | 207 | /// 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> {
|
200 | 239 | Poll::Ready(Ok(result))
|
201 | 240 | }
|
202 | 241 | }
|
| 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 | +} |
0 commit comments