Skip to content

Commit acb6872

Browse files
taiki-ecramertj
authored andcommitted
Add AsyncSeek trait
1 parent bbbfbb7 commit acb6872

File tree

5 files changed

+139
-6
lines changed

5 files changed

+139
-6
lines changed

futures-io/src/lib.rs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ mod if_std {
2323
// Re-export IoVec for convenience
2424
pub use iovec::IoVec;
2525

26-
// Re-export io::Error so that users don't have to deal
26+
// Re-export some types from `std::io` so that users don't have to deal
2727
// with conflicts when `use`ing `futures::io` and `std::io`.
2828
pub use self::StdIo::Error as Error;
2929
pub use self::StdIo::ErrorKind as ErrorKind;
3030
pub use self::StdIo::Result as Result;
31+
pub use self::StdIo::SeekFrom as SeekFrom;
3132

3233
/// A type used to conditionally initialize buffers passed to `AsyncRead`
3334
/// methods, modeled after `std`.
@@ -241,6 +242,30 @@ mod if_std {
241242
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>>;
242243
}
243244

245+
/// Seek bytes asynchronously.
246+
///
247+
/// This trait is analogous to the `std::io::Seek` trait, but integrates
248+
/// with the asynchronous task system. In particular, the `poll_seek`
249+
/// method, unlike `Seek::seek`, will automatically queue the current task
250+
/// for wakeup and return if data is not yet available, rather than blocking
251+
/// the calling thread.
252+
pub trait AsyncSeek {
253+
/// Attempt to seek to an offset, in bytes, in a stream.
254+
///
255+
/// A seek beyond the end of a stream is allowed, but behavior is defined
256+
/// by the implementation.
257+
///
258+
/// If the seek operation completed successfully,
259+
/// this method returns the new position from the start of the stream.
260+
/// That position can be used later with [`SeekFrom::Start`].
261+
///
262+
/// # Errors
263+
///
264+
/// Seeking to a negative offset is considered an error.
265+
fn poll_seek(self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom)
266+
-> Poll<Result<u64>>;
267+
}
268+
244269
macro_rules! deref_async_read {
245270
() => {
246271
unsafe fn initializer(&self) -> Initializer {
@@ -429,6 +454,51 @@ mod if_std {
429454
impl AsyncWrite for StdIo::Sink {
430455
delegate_async_write_to_stdio!();
431456
}
457+
458+
macro_rules! deref_async_seek {
459+
() => {
460+
fn poll_seek(mut self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom)
461+
-> Poll<Result<u64>>
462+
{
463+
Pin::new(&mut **self).poll_seek(cx, pos)
464+
}
465+
}
466+
}
467+
468+
impl<T: ?Sized + AsyncSeek + Unpin> AsyncSeek for Box<T> {
469+
deref_async_seek!();
470+
}
471+
472+
impl<T: ?Sized + AsyncSeek + Unpin> AsyncSeek for &mut T {
473+
deref_async_seek!();
474+
}
475+
476+
477+
impl<P> AsyncSeek for Pin<P>
478+
where
479+
P: DerefMut + Unpin,
480+
P::Target: AsyncSeek,
481+
{
482+
fn poll_seek(self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom)
483+
-> Poll<Result<u64>>
484+
{
485+
self.get_mut().as_mut().poll_seek(cx, pos)
486+
}
487+
}
488+
489+
macro_rules! delegate_async_seek_to_stdio {
490+
() => {
491+
fn poll_seek(mut self: Pin<&mut Self>, _: &mut Context<'_>, pos: SeekFrom)
492+
-> Poll<Result<u64>>
493+
{
494+
Poll::Ready(StdIo::Seek::seek(&mut *self, pos))
495+
}
496+
}
497+
}
498+
499+
impl<T: AsRef<[u8]> + Unpin> AsyncSeek for StdIo::Cursor<T> {
500+
delegate_async_seek_to_stdio!();
501+
}
432502
}
433503

434504
#[cfg(feature = "std")]

futures-util/src/io/allow_std.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use futures_core::task::{Context, Poll};
2-
use futures_io::{AsyncRead, AsyncWrite};
2+
use futures_io::{AsyncRead, AsyncWrite, AsyncSeek};
33
use std::{fmt, io};
44
use std::pin::Pin;
55
use std::string::String;
@@ -116,3 +116,17 @@ impl<T> AsyncRead for AllowStdIo<T> where T: io::Read {
116116
Poll::Ready(Ok(try_with_interrupt!(self.0.read(buf))))
117117
}
118118
}
119+
120+
impl<T> io::Seek for AllowStdIo<T> where T: io::Seek {
121+
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
122+
self.0.seek(pos)
123+
}
124+
}
125+
126+
impl<T> AsyncSeek for AllowStdIo<T> where T: io::Seek {
127+
fn poll_seek(mut self: Pin<&mut Self>, _: &mut Context<'_>, pos: io::SeekFrom)
128+
-> Poll<io::Result<u64>>
129+
{
130+
Poll::Ready(Ok(try_with_interrupt!(self.0.seek(pos))))
131+
}
132+
}

futures-util/src/io/mod.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
88
use std::vec::Vec;
99

10-
pub use futures_io::{AsyncRead, AsyncWrite, IoVec};
10+
pub use futures_io::{AsyncRead, AsyncWrite, AsyncSeek, IoVec, SeekFrom};
1111

1212
#[cfg(feature = "io-compat")] use crate::compat::Compat;
1313

@@ -38,6 +38,9 @@ pub use self::read_to_end::ReadToEnd;
3838
mod close;
3939
pub use self::close::Close;
4040

41+
mod seek;
42+
pub use self::seek::Seek;
43+
4144
mod split;
4245
pub use self::split::{ReadHalf, WriteHalf};
4346

@@ -324,3 +327,19 @@ pub trait AsyncWriteExt: AsyncWrite {
324327
}
325328

326329
impl<W: AsyncWrite + ?Sized> AsyncWriteExt for W {}
330+
331+
/// An extension trait which adds utility methods to `AsyncSeek` types.
332+
pub trait AsyncSeekExt: AsyncSeek {
333+
/// Creates a future which will seek an IO object, and then yield the
334+
/// new position in the object and the object itself.
335+
///
336+
/// In the case of an error the buffer and the object will be discarded, with
337+
/// the error yielded.
338+
fn seek(&mut self, pos: SeekFrom) -> Seek<'_, Self>
339+
where Self: Unpin,
340+
{
341+
Seek::new(self, pos)
342+
}
343+
}
344+
345+
impl<S: AsyncSeek + ?Sized> AsyncSeekExt for S {}

futures-util/src/io/seek.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use crate::io::{AsyncSeek, SeekFrom};
2+
use futures_core::future::Future;
3+
use futures_core::task::{Context, Poll};
4+
use std::io;
5+
use std::pin::Pin;
6+
7+
/// Future for the [`seek`](super::SeekExt::seek) method.
8+
#[derive(Debug)]
9+
pub struct Seek<'a, S: ?Sized + Unpin> {
10+
seek: &'a mut S,
11+
pos: SeekFrom,
12+
}
13+
14+
impl<S: ?Sized + Unpin> Unpin for Seek<'_, S> {}
15+
16+
impl<'a, S: AsyncSeek + ?Sized + Unpin> Seek<'a, S> {
17+
pub(super) fn new(seek: &'a mut S, pos: SeekFrom) -> Self {
18+
Self { seek, pos }
19+
}
20+
}
21+
22+
impl<S: AsyncSeek + ?Sized + Unpin> Future for Seek<'_, S> {
23+
type Output = io::Result<u64>;
24+
25+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
26+
let this = &mut *self;
27+
Pin::new(&mut this.seek).poll_seek(cx, this.pos)
28+
}
29+
}

futures/src/lib.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,12 @@ pub mod io {
268268
//! sinks.
269269
270270
pub use futures_io::{
271-
Error, Initializer, IoVec, ErrorKind, AsyncRead, AsyncWrite, Result
271+
Error, Initializer, IoVec, ErrorKind, AsyncRead, AsyncWrite, AsyncSeek,
272+
Result, SeekFrom,
272273
};
273274
pub use futures_util::io::{
274-
AsyncReadExt, AsyncWriteExt, AllowStdIo, Close, CopyInto, Flush,
275-
Read, ReadExact, ReadHalf, ReadToEnd, Window, WriteAll, WriteHalf,
275+
AsyncReadExt, AsyncWriteExt, AsyncSeekExt, AllowStdIo, Close, CopyInto, Flush,
276+
Read, ReadExact, ReadHalf, ReadToEnd, Seek, Window, WriteAll, WriteHalf,
276277
};
277278
}
278279

0 commit comments

Comments
 (0)