From 2b675cb7a88bc0d9f24e015b6b3b7472192a4e1a Mon Sep 17 00:00:00 2001 From: Wim Looman Date: Sat, 21 Jul 2018 13:53:13 +0200 Subject: [PATCH] Implement AsyncWrite for the generic Cursor> 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> Read for Cursor` was added in https://github.com/rust-lang/rust/pull/27197, I'm not sure why `impl> Write for Cursor` 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. --- futures-io/Cargo.toml | 5 +++-- futures-io/src/lib.rs | 33 ++++++++++++++++++++++++--------- futures-io/tests/cursor.rs | 19 +++++++++++++++++++ futures-util/src/io/mod.rs | 37 +++++++++++++++---------------------- 4 files changed, 61 insertions(+), 33 deletions(-) create mode 100644 futures-io/tests/cursor.rs diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index a74acca989..51aed8a5cb 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -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" diff --git a/futures-io/src/lib.rs b/futures-io/src/lib.rs index b7a2c08696..9d5cf71ae3 100644 --- a/futures-io/src/lib.rs +++ b/futures-io/src/lib.rs @@ -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; @@ -354,16 +354,31 @@ if_std! { } } - impl<'a> AsyncWrite for StdIo::Cursor<&'a mut [u8]> { - delegate_async_write_to_stdio!(); - } + impl> AsyncWrite for StdIo::Cursor { + fn poll_write( + &mut self, + _: &mut task::Context, + buf: &[u8], + ) -> Poll> { + 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> { - delegate_async_write_to_stdio!(); - } + fn poll_flush(&mut self, _: &mut task::Context) -> Poll> { + Poll::Ready(StdIo::Write::flush(&mut self.get_mut().as_mut())) + } - impl AsyncWrite for StdIo::Cursor> { - delegate_async_write_to_stdio!(); + fn poll_close(&mut self, cx: &mut task::Context) -> Poll> { + self.poll_flush(cx) + } } impl AsyncWrite for StdIo::Sink { diff --git a/futures-io/tests/cursor.rs b/futures-io/tests/cursor.rs new file mode 100644 index 0000000000..fb3b71a464 --- /dev/null +++ b/futures-io/tests/cursor.rs @@ -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]); +} diff --git a/futures-util/src/io/mod.rs b/futures-util/src/io/mod.rs index 60d033a020..f0188345a8 100644 --- a/futures-util/src/io/mod.rs +++ b/futures-util/src/io/mod.rs @@ -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>(()) }).unwrap(); /// ``` fn copy_into<'a, W>( @@ -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>(()) }).unwrap(); /// ``` fn split(self) -> (ReadHalf, WriteHalf) @@ -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>(()) }).unwrap(); /// ``` fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self> {