Skip to content

Commit

Permalink
Implement AsyncWrite for the generic Cursor<T: AsMut<[u8]>>
Browse files Browse the repository at this point in the history
This introduces an unfortunate point of difference between
`futures::io::AsyncWrite` and `std::io::Write`, but I think the
increased ergonomics around writing to statically sized in memory
buffers (presumably just for test purposes) is useful.

`impl<T: AsRef<[u8]>> Read for Cursor<T>` was added in
rust-lang/rust#27197, I'm not sure why `impl<T:
AsMut<[u8]>> Write for Cursor<T>` wasn't added at the same time; I would
propose doing this change in `std` and just piggybacking off it here,
but the breakage is almost certainly not worth it by this point.
  • Loading branch information
Nemo157 committed Jul 21, 2018
1 parent 6dea985 commit 2b675cb
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 33 deletions.
5 changes: 3 additions & 2 deletions futures-io/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ default = ["std"]
futures-core-preview = { path = "../futures-core", version = "0.3.0-alpha.1", default-features = false }
iovec = { version = "0.1", optional = true }

# [dev-dependencies]
# futures = { path = "../futures", version = "0.2.0" }
[dev-dependencies]
futures-preview = { path = "../futures", version = "0.3.0-alpha.1" }
assert_matches = "1.3.0"
33 changes: 24 additions & 9 deletions futures-io/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ macro_rules! if_std {
if_std! {
use futures_core::task::{self, Poll};
use std::boxed::Box;
use std::cmp;
use std::io as StdIo;
use std::ptr;
use std::vec::Vec;

// Re-export IoVec for convenience
pub use iovec::IoVec;
Expand Down Expand Up @@ -354,16 +354,31 @@ if_std! {
}
}

impl<'a> AsyncWrite for StdIo::Cursor<&'a mut [u8]> {
delegate_async_write_to_stdio!();
}
impl<T: AsMut<[u8]>> AsyncWrite for StdIo::Cursor<T> {
fn poll_write(
&mut self,
_: &mut task::Context,
buf: &[u8],
) -> Poll<Result<usize>> {
let position = self.position();
let result = {
let mut out = self.get_mut().as_mut();
let pos = cmp::min(out.len() as u64, position) as usize;
StdIo::Write::write(&mut &mut out[pos..], buf)
};
if let Ok(offset) = result {
self.set_position(position + offset as u64);
}
Poll::Ready(result)
}

impl AsyncWrite for StdIo::Cursor<Vec<u8>> {
delegate_async_write_to_stdio!();
}
fn poll_flush(&mut self, _: &mut task::Context) -> Poll<Result<()>> {
Poll::Ready(StdIo::Write::flush(&mut self.get_mut().as_mut()))
}

impl AsyncWrite for StdIo::Cursor<Box<[u8]>> {
delegate_async_write_to_stdio!();
fn poll_close(&mut self, cx: &mut task::Context) -> Poll<Result<()>> {
self.poll_flush(cx)
}
}

impl AsyncWrite for StdIo::Sink {
Expand Down
19 changes: 19 additions & 0 deletions futures-io/tests/cursor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#![feature(use_extern_macros, futures_api)]

use assert_matches::assert_matches;
use futures::Poll;
use futures::future::lazy;
use futures::io::AsyncWrite;
use std::io::Cursor;

#[test]
fn cursor_asyncwrite_asmut() {
let mut cursor = Cursor::new([0; 5]);
futures::executor::block_on(lazy(|ctx| {
assert_matches!(cursor.poll_write(ctx, &[1, 2]), Poll::Ready(Ok(2)));
assert_matches!(cursor.poll_write(ctx, &[3, 4]), Poll::Ready(Ok(2)));
assert_matches!(cursor.poll_write(ctx, &[5, 6]), Poll::Ready(Ok(1)));
assert_matches!(cursor.poll_write(ctx, &[6, 7]), Poll::Ready(Ok(0)));
}));
assert_eq!(cursor.into_inner(), [1, 2, 3, 4, 5]);
}
37 changes: 15 additions & 22 deletions futures-util/src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,12 @@ pub trait AsyncReadExt: AsyncRead {
/// use std::io::Cursor;
///
/// let mut reader = Cursor::new([1, 2, 3, 4]);
/// let mut output = [0u8; 5];
/// let mut writer = Cursor::new([0u8; 5]);
///
/// let bytes = {
/// let mut writer = Cursor::new(&mut output[..]);
/// await!(reader.copy_into(&mut writer))?
/// };
/// let bytes = await!(reader.copy_into(&mut writer))?;
///
/// assert_eq!(bytes, 4);
/// assert_eq!(output, [1, 2, 3, 4, 0]);
/// assert_eq!(writer.into_inner(), [1, 2, 3, 4, 0]);
/// # Ok::<(), Box<std::error::Error>>(()) }).unwrap();
/// ```
fn copy_into<'a, W>(
Expand Down Expand Up @@ -201,23 +198,22 @@ pub trait AsyncReadExt: AsyncRead {
/// use futures::io::AsyncReadExt;
/// use std::io::Cursor;
///
/// // Note that for `Cursor` the read and write halves share a single
/// // seek position. This may or may not be true for other types that
/// // implement both `AsyncRead` and `AsyncWrite`.
///
/// let mut reader = Cursor::new([1, 2, 3, 4]);
/// let mut buffer = [0, 0, 0, 0, 5, 6, 7, 8];
/// let mut output = [0u8; 5];
/// let mut buffer = Cursor::new([0, 0, 0, 0, 5, 6, 7, 8]);
/// let mut writer = Cursor::new([0u8; 5]);
///
/// {
/// let mut writer = Cursor::new(&mut output[..]);
/// // Note that for `Cursor` the read and write halves share a single
/// // seek position. This may or may not be true for other types that
/// // implement both `AsyncRead` and `AsyncWrite`.
/// let buffer_cursor = Cursor::new(&mut buffer[..]);
/// let (mut buffer_reader, mut buffer_writer) = buffer_cursor.split();
/// let (mut buffer_reader, mut buffer_writer) = (&mut buffer).split();
/// await!(reader.copy_into(&mut buffer_writer))?;
/// await!(buffer_reader.copy_into(&mut writer))?;
/// }
///
/// assert_eq!(buffer, [1, 2, 3, 4, 5, 6, 7, 8]);
/// assert_eq!(output, [5, 6, 7, 8, 0]);
/// assert_eq!(buffer.into_inner(), [1, 2, 3, 4, 5, 6, 7, 8]);
/// assert_eq!(writer.into_inner(), [5, 6, 7, 8, 0]);
/// # Ok::<(), Box<std::error::Error>>(()) }).unwrap();
/// ```
fn split(self) -> (ReadHalf<Self>, WriteHalf<Self>)
Expand Down Expand Up @@ -278,14 +274,11 @@ pub trait AsyncWriteExt: AsyncWrite {
/// use futures::io::AsyncWriteExt;
/// use std::io::Cursor;
///
/// let mut output = [0u8; 5];
/// let mut writer = Cursor::new([0u8; 5]);
///
/// {
/// let mut writer = Cursor::new(&mut output[..]);
/// await!(writer.write_all(&[1, 2, 3, 4]))?;
/// }
/// await!(writer.write_all(&[1, 2, 3, 4]))?;
///
/// assert_eq!(output, [1, 2, 3, 4, 0]);
/// assert_eq!(writer.into_inner(), [1, 2, 3, 4, 0]);
/// # Ok::<(), Box<std::error::Error>>(()) }).unwrap();
/// ```
fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self> {
Expand Down

0 comments on commit 2b675cb

Please sign in to comment.