diff --git a/.travis.yml b/.travis.yml index 5bcf70a559..bb049a06bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -136,14 +136,23 @@ matrix: --no-default-features --features async-await - - name: cargo check (futures-util) + - name: cargo check (sub crates) rust: nightly script: - cargo run --manifest-path ci/remove-dev-dependencies/Cargo.toml */Cargo.toml + # futures-io + # Check default-features, all-features + - cargo check --manifest-path futures-io/Cargo.toml + - cargo check --manifest-path futures-io/Cargo.toml --all-features + # Check each features + - cargo check --manifest-path futures-io/Cargo.toml --features read_initializer,unstable + + # futures-util + # Check default-features, all-features - cargo check --manifest-path futures-util/Cargo.toml - cargo check --manifest-path futures-util/Cargo.toml --all-features - + # Check each features - cargo check --manifest-path futures-util/Cargo.toml --features sink - cargo check --manifest-path futures-util/Cargo.toml --features io - cargo check --manifest-path futures-util/Cargo.toml --features channel @@ -158,6 +167,7 @@ matrix: - cargo check --manifest-path futures-util/Cargo.toml --features sink,bilock,unstable - cargo check --manifest-path futures-util/Cargo.toml --features io,bilock,unstable - cargo check --manifest-path futures-util/Cargo.toml --features sink,io + - cargo check --manifest-path futures-util/Cargo.toml --features read_initializer,unstable - cargo check --manifest-path futures-util/Cargo.toml --no-default-features - cargo check --manifest-path futures-util/Cargo.toml --no-default-features --features sink diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 8b5c4a1ff5..3b6be90c27 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -18,6 +18,12 @@ name = "futures_io" default = ["std"] std = [] +# Unstable features +# These features are outside of the normal semver guarantees and require the +# `unstable` feature as an explicit opt-in to unstable API. +unstable = [] +read_initializer = [] + [dependencies] [dev-dependencies] diff --git a/futures-io/src/lib.rs b/futures-io/src/lib.rs index ff2aff9f46..551ee0700a 100644 --- a/futures-io/src/lib.rs +++ b/futures-io/src/lib.rs @@ -8,6 +8,8 @@ //! All items of this library are only available when the `std` feature of this //! library is activated, and it is activated by default. +#![cfg_attr(feature = "read_initializer", feature(read_initializer))] + #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] @@ -19,13 +21,15 @@ #![doc(html_root_url = "https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.18/futures_io")] +#[cfg(all(feature = "read_initializer", not(feature = "unstable")))] +compile_error!("The `read_initializer` feature requires the `unstable` feature as an explicit opt-in to unstable features"); + #[cfg(feature = "std")] mod if_std { use std::cmp; use std::io; use std::ops::DerefMut; use std::pin::Pin; - use std::ptr; use std::task::{Context, Poll}; // Re-export some types from `std::io` so that users don't have to deal @@ -40,45 +44,9 @@ mod if_std { SeekFrom as SeekFrom, }; - /// A type used to conditionally initialize buffers passed to `AsyncRead` - /// methods, modeled after `std`. - #[derive(Debug)] - pub struct Initializer(bool); - - impl Initializer { - /// Returns a new `Initializer` which will zero out buffers. - #[inline] - pub fn zeroing() -> Initializer { - Initializer(true) - } - - /// Returns a new `Initializer` which will not zero out buffers. - /// - /// # Safety - /// - /// This method may only be called by `AsyncRead`ers which guarantee - /// that they will not read from the buffers passed to `AsyncRead` - /// methods, and that the return value of the method accurately reflects - /// the number of bytes that have been written to the head of the buffer. - #[inline] - pub unsafe fn nop() -> Initializer { - Initializer(false) - } - - /// Indicates if a buffer should be initialized. - #[inline] - pub fn should_initialize(&self) -> bool { - self.0 - } - - /// Initializes a buffer if necessary. - #[inline] - pub fn initialize(&self, buf: &mut [u8]) { - if self.should_initialize() { - unsafe { ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) } - } - } - } + #[cfg(feature = "read_initializer")] + #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 + pub use io::Initializer as Initializer; /// Read bytes asynchronously. /// @@ -99,6 +67,7 @@ mod if_std { /// This method is `unsafe` because and `AsyncRead`er could otherwise /// return a non-zeroing `Initializer` from another `AsyncRead` type /// without an `unsafe` block. + #[cfg(feature = "read_initializer")] #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::zeroing() @@ -342,6 +311,7 @@ mod if_std { macro_rules! deref_async_read { () => { + #[cfg(feature = "read_initializer")] unsafe fn initializer(&self) -> Initializer { (**self).initializer() } @@ -373,6 +343,7 @@ mod if_std { P: DerefMut + Unpin, P::Target: AsyncRead, { + #[cfg(feature = "read_initializer")] unsafe fn initializer(&self) -> Initializer { (**self).initializer() } @@ -390,12 +361,11 @@ mod if_std { } } - /// `unsafe` because the `io::Read` type must not access the buffer - /// before reading data into it. - macro_rules! unsafe_delegate_async_read_to_stdio { + macro_rules! delegate_async_read_to_stdio { () => { + #[cfg(feature = "read_initializer")] unsafe fn initializer(&self) -> Initializer { - Initializer::nop() + io::Read::initializer(self) } fn poll_read(mut self: Pin<&mut Self>, _: &mut Context<'_>, buf: &mut [u8]) @@ -413,11 +383,11 @@ mod if_std { } impl AsyncRead for &[u8] { - unsafe_delegate_async_read_to_stdio!(); + delegate_async_read_to_stdio!(); } impl + Unpin> AsyncRead for io::Cursor { - unsafe_delegate_async_read_to_stdio!(); + delegate_async_read_to_stdio!(); } macro_rules! deref_async_write { diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 33f0428c50..1825ea83f3 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -33,6 +33,7 @@ select-macro = ["async-await", "futures-select-macro-preview", "proc-macro-hack" unstable = ["futures-core-preview/unstable"] cfg-target-has-atomic = ["futures-core-preview/cfg-target-has-atomic"] bilock = [] +read_initializer = ["io", "futures-io-preview/read_initializer", "futures-io-preview/unstable"] [dependencies] futures-core-preview = { path = "../futures-core", version = "=0.3.0-alpha.18", default-features = false } diff --git a/futures-util/src/compat/compat01as03.rs b/futures-util/src/compat/compat01as03.rs index f9471031cc..dde42a4b54 100644 --- a/futures-util/src/compat/compat01as03.rs +++ b/futures-util/src/compat/compat01as03.rs @@ -368,9 +368,9 @@ unsafe impl UnsafeNotify01 for NotifyWaker { #[cfg(feature = "io-compat")] mod io { use super::*; - use futures_io::{ - AsyncRead as AsyncRead03, AsyncWrite as AsyncWrite03, Initializer, - }; + #[cfg(feature = "read_initializer")] + use futures_io::Initializer; + use futures_io::{AsyncRead as AsyncRead03, AsyncWrite as AsyncWrite03}; use std::io::Error; use tokio_io::{AsyncRead as AsyncRead01, AsyncWrite as AsyncWrite01}; @@ -433,6 +433,7 @@ mod io { impl AsyncWrite01CompatExt for W {} impl AsyncRead03 for Compat01As03 { + #[cfg(feature = "read_initializer")] unsafe fn initializer(&self) -> Initializer { // check if `prepare_uninitialized_buffer` needs zeroing if self.inner.get_ref().prepare_uninitialized_buffer(&mut [1]) { diff --git a/futures-util/src/compat/compat03as01.rs b/futures-util/src/compat/compat03as01.rs index 763d19b2be..b9632cede8 100644 --- a/futures-util/src/compat/compat03as01.rs +++ b/futures-util/src/compat/compat03as01.rs @@ -262,6 +262,7 @@ mod io { } impl AsyncRead01 for Compat { + #[cfg(feature = "read_initializer")] unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { let initializer = self.inner.initializer(); let does_init = initializer.should_initialize(); diff --git a/futures-util/src/future/either.rs b/futures-util/src/future/either.rs index 24ff16da4f..12bdce623c 100644 --- a/futures-util/src/future/either.rs +++ b/futures-util/src/future/either.rs @@ -160,9 +160,10 @@ mod if_std { use super::Either; use core::pin::Pin; use core::task::{Context, Poll}; + #[cfg(feature = "read_initializer")] + use futures_io::Initializer; use futures_io::{ - AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, Initializer, IoSlice, IoSliceMut, Result, - SeekFrom, + AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, Result, SeekFrom, }; impl AsyncRead for Either @@ -170,6 +171,7 @@ mod if_std { A: AsyncRead, B: AsyncRead, { + #[cfg(feature = "read_initializer")] unsafe fn initializer(&self) -> Initializer { match self { Either::Left(x) => x.initializer(), diff --git a/futures-util/src/io/allow_std.rs b/futures-util/src/io/allow_std.rs index 63cda335e2..f44d5b251c 100644 --- a/futures-util/src/io/allow_std.rs +++ b/futures-util/src/io/allow_std.rs @@ -1,4 +1,6 @@ use futures_core::task::{Context, Poll}; +#[cfg(feature = "read_initializer")] +use futures_io::Initializer; use futures_io::{AsyncRead, AsyncWrite, AsyncSeek, AsyncBufRead, IoSlice, IoSliceMut, SeekFrom}; use std::{fmt, io}; use std::pin::Pin; @@ -106,8 +108,10 @@ impl io::Read for AllowStdIo where T: io::Read { fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { self.0.read_vectored(bufs) } - // TODO: implement the `initializer` fn when it stabilizes. - // See rust-lang/rust #42788 + #[cfg(feature = "read_initializer")] + unsafe fn initializer(&self) -> Initializer { + self.0.initializer() + } fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { self.0.read_to_end(buf) } @@ -131,6 +135,11 @@ impl AsyncRead for AllowStdIo where T: io::Read { { Poll::Ready(Ok(try_with_interrupt!(self.0.read_vectored(bufs)))) } + + #[cfg(feature = "read_initializer")] + unsafe fn initializer(&self) -> Initializer { + self.0.initializer() + } } impl io::Seek for AllowStdIo where T: io::Seek { diff --git a/futures-util/src/io/buf_reader.rs b/futures-util/src/io/buf_reader.rs index 6f7a026c1c..e6df6abe8c 100644 --- a/futures-util/src/io/buf_reader.rs +++ b/futures-util/src/io/buf_reader.rs @@ -1,5 +1,7 @@ use futures_core::task::{Context, Poll}; -use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, Initializer, IoSliceMut, SeekFrom}; +#[cfg(feature = "read_initializer")] +use futures_io::Initializer; +use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, IoSliceMut, SeekFrom}; use pin_utils::{unsafe_pinned, unsafe_unpinned}; use std::io::{self, Read}; use std::pin::Pin; @@ -48,7 +50,7 @@ impl BufReader { unsafe { let mut buffer = Vec::with_capacity(capacity); buffer.set_len(capacity); - inner.initializer().initialize(&mut buffer); + super::initialize(&inner, &mut buffer); Self { inner, buf: buffer.into_boxed_slice(), @@ -166,6 +168,7 @@ impl AsyncRead for BufReader { } // we can't skip unconditionally because of the large buffer case in read. + #[cfg(feature = "read_initializer")] unsafe fn initializer(&self) -> Initializer { self.inner.initializer() } diff --git a/futures-util/src/io/chain.rs b/futures-util/src/io/chain.rs index 35adfdbbc4..ec6bbff760 100644 --- a/futures-util/src/io/chain.rs +++ b/futures-util/src/io/chain.rs @@ -1,5 +1,7 @@ use futures_core::task::{Context, Poll}; -use futures_io::{AsyncBufRead, AsyncRead, Initializer, IoSliceMut}; +#[cfg(feature = "read_initializer")] +use futures_io::Initializer; +use futures_io::{AsyncBufRead, AsyncRead, IoSliceMut}; use pin_utils::{unsafe_pinned, unsafe_unpinned}; use std::fmt; use std::io; @@ -117,6 +119,7 @@ where self.second().poll_read_vectored(cx, bufs) } + #[cfg(feature = "read_initializer")] unsafe fn initializer(&self) -> Initializer { let initializer = self.first.initializer(); if initializer.should_initialize() { diff --git a/futures-util/src/io/empty.rs b/futures-util/src/io/empty.rs index 58e0d52fd6..60fa08a913 100644 --- a/futures-util/src/io/empty.rs +++ b/futures-util/src/io/empty.rs @@ -1,5 +1,7 @@ use futures_core::task::{Context, Poll}; -use futures_io::{AsyncBufRead, AsyncRead, Initializer}; +#[cfg(feature = "read_initializer")] +use futures_io::Initializer; +use futures_io::{AsyncBufRead, AsyncRead}; use std::fmt; use std::io; use std::pin::Pin; @@ -42,6 +44,7 @@ impl AsyncRead for Empty { Poll::Ready(Ok(0)) } + #[cfg(feature = "read_initializer")] #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() diff --git a/futures-util/src/io/mod.rs b/futures-util/src/io/mod.rs index 3f2e6ba271..db8bd134d4 100644 --- a/futures-util/src/io/mod.rs +++ b/futures-util/src/io/mod.rs @@ -9,17 +9,35 @@ //! This module is only available when the `io` and `std` features of this //! library is activated, and it is activated by default. +#[cfg(feature = "io-compat")] +use crate::compat::Compat; +use std::ptr; + pub use futures_io::{ AsyncRead, AsyncWrite, AsyncSeek, AsyncBufRead, Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom, }; - -#[cfg(feature = "io-compat")] use crate::compat::Compat; +#[cfg(feature = "read_initializer")] +pub use futures_io::Initializer; // used by `BufReader` and `BufWriter` // https://github.com/rust-lang/rust/blob/master/src/libstd/sys_common/io.rs#L1 const DEFAULT_BUF_SIZE: usize = 8 * 1024; +/// Initializes a buffer if necessary. +/// +/// A buffer is always initialized if `read_initializer` feature is disabled. +#[inline] +unsafe fn initialize(_reader: &R, buf: &mut [u8]) { + #[cfg(feature = "read_initializer")] + { + if !_reader.initializer().should_initialize() { + return; + } + } + ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) +} + mod allow_std; pub use self::allow_std::AllowStdIo; diff --git a/futures-util/src/io/read_to_end.rs b/futures-util/src/io/read_to_end.rs index 4e342e0574..c4dab99e0c 100644 --- a/futures-util/src/io/read_to_end.rs +++ b/futures-util/src/io/read_to_end.rs @@ -58,7 +58,7 @@ pub(super) fn read_to_end_internal( g.buf.reserve(32); let capacity = g.buf.capacity(); g.buf.set_len(capacity); - rd.initializer().initialize(&mut g.buf[g.len..]); + super::initialize(&rd, &mut g.buf[g.len..]); } } diff --git a/futures-util/src/io/repeat.rs b/futures-util/src/io/repeat.rs index 0821ec9b04..9f493d1a49 100644 --- a/futures-util/src/io/repeat.rs +++ b/futures-util/src/io/repeat.rs @@ -1,5 +1,7 @@ use futures_core::task::{Context, Poll}; -use futures_io::{AsyncRead, Initializer, IoSliceMut}; +#[cfg(feature = "read_initializer")] +use futures_io::Initializer; +use futures_io::{AsyncRead, IoSliceMut}; use std::fmt; use std::io; use std::pin::Pin; @@ -57,6 +59,7 @@ impl AsyncRead for Repeat { Poll::Ready(Ok(nwritten)) } + #[cfg(feature = "read_initializer")] #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() diff --git a/futures-util/src/io/take.rs b/futures-util/src/io/take.rs index 3951371458..3eaa5f5ced 100644 --- a/futures-util/src/io/take.rs +++ b/futures-util/src/io/take.rs @@ -1,5 +1,7 @@ use futures_core::task::{Context, Poll}; -use futures_io::{AsyncRead, AsyncBufRead, Initializer}; +#[cfg(feature = "read_initializer")] +use futures_io::Initializer; +use futures_io::{AsyncRead, AsyncBufRead}; use pin_utils::{unsafe_pinned, unsafe_unpinned}; use std::{cmp, io}; use std::pin::Pin; @@ -183,6 +185,7 @@ impl AsyncRead for Take { Poll::Ready(Ok(n)) } + #[cfg(feature = "read_initializer")] unsafe fn initializer(&self) -> Initializer { self.inner.initializer() } diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 4446c1476d..3f91a9eb96 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -2,6 +2,7 @@ //! and the `AsyncRead` and `AsyncWrite` traits. #![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] +#![cfg_attr(feature = "read_initializer", feature(read_initializer))] #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] @@ -19,6 +20,9 @@ compile_error!("The `cfg-target-has-atomic` feature requires the `unstable` feat #[cfg(all(feature = "bilock", not(feature = "unstable")))] compile_error!("The `bilock` feature requires the `unstable` feature as an explicit opt-in to unstable features"); +#[cfg(all(feature = "read_initializer", not(feature = "unstable")))] +compile_error!("The `read_initializer` feature requires the `unstable` feature as an explicit opt-in to unstable features"); + #[cfg(feature = "alloc")] extern crate alloc; diff --git a/futures/Cargo.toml b/futures/Cargo.toml index e17f4833d1..71e99ea63e 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -46,9 +46,10 @@ io-compat = ["compat", "futures-util-preview/io-compat"] # Unstable features # These features are outside of the normal semver guarantees and require the # `unstable` feature as an explicit opt-in to unstable API. -unstable = ["futures-core-preview/unstable", "futures-channel-preview/unstable", "futures-util-preview/unstable"] +unstable = ["futures-core-preview/unstable", "futures-channel-preview/unstable", "futures-io-preview/unstable", "futures-util-preview/unstable"] cfg-target-has-atomic = ["futures-core-preview/cfg-target-has-atomic", "futures-channel-preview/cfg-target-has-atomic", "futures-util-preview/cfg-target-has-atomic"] bilock = ["futures-util-preview/bilock"] +read_initializer = ["futures-io-preview/read_initializer", "futures-util-preview/read_initializer"] [package.metadata.docs.rs] all-features = true diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 33f6e79a0a..f4973ff7ab 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -22,6 +22,7 @@ //! completion, but *do not block* the thread running them. #![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] +#![cfg_attr(feature = "read_initializer", feature(read_initializer))] #![cfg_attr(not(feature = "std"), no_std)] @@ -40,6 +41,9 @@ compile_error!("The `cfg-target-has-atomic` feature requires the `unstable` feat #[cfg(all(feature = "bilock", not(feature = "unstable")))] compile_error!("The `bilock` feature requires the `unstable` feature as an explicit opt-in to unstable features"); +#[cfg(all(feature = "read_initializer", not(feature = "unstable")))] +compile_error!("The `read_initializer` feature requires the `unstable` feature as an explicit opt-in to unstable features"); + #[doc(hidden)] pub use futures_core::core_reexport; #[doc(hidden)] pub use futures_core::future::Future; @@ -298,9 +302,12 @@ pub mod io { pub use futures_io::{ AsyncRead, AsyncWrite, AsyncSeek, AsyncBufRead, Error, ErrorKind, - Initializer, IoSlice, IoSliceMut, Result, SeekFrom, + IoSlice, IoSliceMut, Result, SeekFrom, }; + #[cfg(feature = "read_initializer")] + pub use futures_io::Initializer; + pub use futures_util::io::{ AsyncReadExt, AsyncWriteExt, AsyncSeekExt, AsyncBufReadExt, AllowStdIo, BufReader, BufWriter, Chain, Close, CopyInto, CopyBufInto, empty, Empty,