Skip to content

Commit dc975ec

Browse files
committed
Add AsyncBufReadExt::copy_buf_into
1 parent b232653 commit dc975ec

File tree

2 files changed

+91
-0
lines changed

2 files changed

+91
-0
lines changed

futures-util/src/io/copy_buf_into.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use futures_core::future::Future;
2+
use futures_core::task::{Context, Poll};
3+
use futures_io::{AsyncBufRead, AsyncWrite};
4+
use std::io;
5+
use std::pin::Pin;
6+
7+
/// Future for the [`copy_buf_into`](super::AsyncBufReadExt::copy_buf_into) method.
8+
#[derive(Debug)]
9+
#[must_use = "futures do nothing unless you `.await` or poll them"]
10+
pub struct CopyBufInto<R, W> {
11+
reader: R,
12+
writer: W,
13+
amt: u64,
14+
}
15+
16+
impl<R: Unpin, W: Unpin> Unpin for CopyBufInto<R, W> {}
17+
18+
impl<R, W> CopyBufInto<R, W> {
19+
pub(super) fn new(reader: R, writer: W) -> Self {
20+
CopyBufInto {
21+
reader,
22+
writer,
23+
amt: 0,
24+
}
25+
}
26+
27+
fn project<'a>(self: Pin<&'a mut Self>) -> (Pin<&'a mut R>, Pin<&'a mut W>, &'a mut u64) {
28+
unsafe {
29+
let this = self.get_unchecked_mut();
30+
(Pin::new_unchecked(&mut this.reader), Pin::new_unchecked(&mut this.writer), &mut this.amt)
31+
}
32+
}
33+
}
34+
35+
impl<R, W> Future for CopyBufInto<R, W>
36+
where R: AsyncBufRead,
37+
W: AsyncWrite,
38+
{
39+
type Output = io::Result<u64>;
40+
41+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
42+
let (mut reader, mut writer, amt) = self.project();
43+
loop {
44+
let buffer = ready!(reader.as_mut().poll_fill_buf(cx))?;
45+
if buffer.is_empty() {
46+
ready!(writer.as_mut().poll_flush(cx))?;
47+
return Poll::Ready(Ok(*amt));
48+
}
49+
50+
let i = ready!(writer.as_mut().poll_write(cx, buffer))?;
51+
if i == 0 {
52+
return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))
53+
}
54+
*amt += i as u64;
55+
reader.as_mut().consume(i);
56+
}
57+
}
58+
}

futures-util/src/io/mod.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ pub use self::buf_writer::BufWriter;
3131
mod copy_into;
3232
pub use self::copy_into::CopyInto;
3333

34+
mod copy_buf_into;
35+
pub use self::copy_buf_into::CopyBufInto;
36+
3437
mod flush;
3538
pub use self::flush::Flush;
3639

@@ -404,6 +407,36 @@ impl<S: AsyncSeek + ?Sized> AsyncSeekExt for S {}
404407

405408
/// An extension trait which adds utility methods to `AsyncBufRead` types.
406409
pub trait AsyncBufReadExt: AsyncBufRead {
410+
/// Creates a future which copies all the bytes from one object to another.
411+
///
412+
/// The returned future will copy all the bytes read from this `AsyncBufRead` into the
413+
/// `writer` specified. This future will only complete once the `reader` has hit
414+
/// EOF and all bytes have been written to and flushed from the `writer`
415+
/// provided.
416+
///
417+
/// On success the number of bytes is returned.
418+
///
419+
/// # Examples
420+
///
421+
/// ```
422+
/// #![feature(async_await)]
423+
/// # futures::executor::block_on(async {
424+
/// use futures::io::AsyncBufReadExt;
425+
/// use std::io::Cursor;
426+
///
427+
/// let mut reader = Cursor::new([1, 2, 3, 4]);
428+
/// let mut writer = Cursor::new([0u8; 5]);
429+
///
430+
/// let bytes = reader.copy_buf_into(&mut writer).await?;
431+
///
432+
/// assert_eq!(bytes, 4);
433+
/// assert_eq!(writer.into_inner(), [1, 2, 3, 4, 0]);
434+
/// # Ok::<(), Box<std::error::Error>>(()) }).unwrap();
435+
/// ```
436+
fn copy_buf_into<W: AsyncWrite>(self, writer: W) -> CopyBufInto<Self, W> where Self: Sized {
437+
CopyBufInto::new(self, writer)
438+
}
439+
407440
/// Creates a future which will read all the bytes associated with this I/O
408441
/// object into `buf` until the delimiter `byte` or EOF is reached.
409442
/// This method is the async equivalent to [`BufRead::read_until`](std::io::BufRead::read_until).

0 commit comments

Comments
 (0)