chore: migrate from pin-project to pin-project-lite (#1778)

This commit is contained in:
Taiki Endo
2019-11-16 09:14:40 -08:00
committed by Carl Lerche
parent 19f1fc36bd
commit 320c84a433
11 changed files with 226 additions and 263 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ bytes = "0.4.7"
futures-core = "0.3.0" futures-core = "0.3.0"
futures-sink = "0.3.0" futures-sink = "0.3.0"
log = "0.4" log = "0.4"
pin-project = "0.4" pin-project-lite = "0.1.1"
[dev-dependencies] [dev-dependencies]
tokio = { version = "=0.2.0-alpha.6", path = "../tokio" } tokio = { version = "=0.2.0-alpha.6", path = "../tokio" }
+16 -14
View File
@@ -8,27 +8,29 @@ use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite};
use bytes::BytesMut; use bytes::BytesMut;
use futures_core::Stream; use futures_core::Stream;
use futures_sink::Sink; use futures_sink::Sink;
use pin_project::pin_project; use pin_project_lite::pin_project;
use std::fmt; use std::fmt;
use std::io::{self, BufRead, Read, Write}; use std::io::{self, BufRead, Read, Write};
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
/// A unified `Stream` and `Sink` interface to an underlying I/O object, using pin_project! {
/// the `Encoder` and `Decoder` traits to encode and decode frames. /// A unified `Stream` and `Sink` interface to an underlying I/O object, using
/// /// the `Encoder` and `Decoder` traits to encode and decode frames.
/// You can create a `Framed` instance by using the `AsyncRead::framed` adapter. ///
#[pin_project] /// You can create a `Framed` instance by using the `AsyncRead::framed` adapter.
pub struct Framed<T, U> { pub struct Framed<T, U> {
#[pin] #[pin]
inner: FramedRead2<FramedWrite2<Fuse<T, U>>>, inner: FramedRead2<FramedWrite2<Fuse<T, U>>>,
}
} }
#[pin_project] pin_project! {
pub(crate) struct Fuse<T, U> { pub(crate) struct Fuse<T, U> {
#[pin] #[pin]
pub(crate) io: T, pub(crate) io: T,
pub(crate) codec: U, pub(crate) codec: U,
}
} }
/// Abstracts over `FramedRead2` being either `FramedRead2<FramedWrite2<Fuse<T, U>>>` or /// Abstracts over `FramedRead2` being either `FramedRead2<FramedWrite2<Fuse<T, U>>>` or
+15 -13
View File
@@ -7,25 +7,27 @@ use bytes::BytesMut;
use futures_core::Stream; use futures_core::Stream;
use futures_sink::Sink; use futures_sink::Sink;
use log::trace; use log::trace;
use pin_project::pin_project; use pin_project_lite::pin_project;
use std::fmt; use std::fmt;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
/// A `Stream` of messages decoded from an `AsyncRead`. pin_project! {
#[pin_project] /// A `Stream` of messages decoded from an `AsyncRead`.
pub struct FramedRead<T, D> { pub struct FramedRead<T, D> {
#[pin] #[pin]
inner: FramedRead2<Fuse<T, D>>, inner: FramedRead2<Fuse<T, D>>,
}
} }
#[pin_project] pin_project! {
pub(crate) struct FramedRead2<T> { pub(crate) struct FramedRead2<T> {
#[pin] #[pin]
inner: T, inner: T,
eof: bool, eof: bool,
is_readable: bool, is_readable: bool,
buffer: BytesMut, buffer: BytesMut,
}
} }
const INITIAL_CAPACITY: usize = 8 * 1024; const INITIAL_CAPACITY: usize = 8 * 1024;
+13 -11
View File
@@ -8,24 +8,26 @@ use bytes::BytesMut;
use futures_core::{ready, Stream}; use futures_core::{ready, Stream};
use futures_sink::Sink; use futures_sink::Sink;
use log::trace; use log::trace;
use pin_project::pin_project; use pin_project_lite::pin_project;
use std::fmt; use std::fmt;
use std::io::{self, BufRead, Read}; use std::io::{self, BufRead, Read};
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
/// A `Sink` of frames encoded to an `AsyncWrite`. pin_project! {
#[pin_project] /// A `Sink` of frames encoded to an `AsyncWrite`.
pub struct FramedWrite<T, E> { pub struct FramedWrite<T, E> {
#[pin] #[pin]
inner: FramedWrite2<Fuse<T, E>>, inner: FramedWrite2<Fuse<T, E>>,
}
} }
#[pin_project] pin_project! {
pub(crate) struct FramedWrite2<T> { pub(crate) struct FramedWrite2<T> {
#[pin] #[pin]
inner: T, inner: T,
buffer: BytesMut, buffer: BytesMut,
}
} }
const INITIAL_CAPACITY: usize = 8 * 1024; const INITIAL_CAPACITY: usize = 8 * 1024;
+2 -3
View File
@@ -41,7 +41,7 @@ blocking = ["rt-core"]
dns = ["blocking"] dns = ["blocking"]
fs = ["blocking"] fs = ["blocking"]
io-driver = ["mio", "lazy_static", "sync"] # TODO: get rid of sync io-driver = ["mio", "lazy_static", "sync"] # TODO: get rid of sync
io-util = ["pin-project", "pin-project-lite", "memchr"] io-util = ["memchr"]
macros = ["tokio-macros"] macros = ["tokio-macros"]
net = ["dns", "tcp", "udp", "uds"] net = ["dns", "tcp", "udp", "uds"]
process = [ process = [
@@ -88,6 +88,7 @@ tokio-macros = { version = "=0.2.0-alpha.6", optional = true, path = "../tokio-m
bytes = "0.4" bytes = "0.4"
iovec = "0.1" iovec = "0.1"
pin-project-lite = "0.1.1"
# Everything else is optional... # Everything else is optional...
fnv = { version = "1.0.6", optional = true } fnv = { version = "1.0.6", optional = true }
@@ -96,8 +97,6 @@ lazy_static = { version = "1.0.2", optional = true }
memchr = { version = "2.2", optional = true } memchr = { version = "2.2", optional = true }
mio = { version = "0.6.14", optional = true } mio = { version = "0.6.14", optional = true }
num_cpus = { version = "1.8.0", optional = true } num_cpus = { version = "1.8.0", optional = true }
pin-project = { version = "0.4", optional = true }
pin-project-lite = { version = "0.1", optional = true }
# Backs `DelayQueue` # Backs `DelayQueue`
slab = { version = "0.4.1", optional = true } slab = { version = "0.4.1", optional = true }
+39 -72
View File
@@ -1,5 +1,6 @@
use crate::future::{maybe_done, MaybeDone}; use crate::future::{maybe_done, MaybeDone};
use pin_project_lite::pin_project;
use std::future::Future; use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
@@ -21,15 +22,20 @@ where
} }
} }
pub(crate) struct TryJoin3<F1, F2, F3> pin_project! {
where pub(crate) struct TryJoin3<F1, F2, F3>
F1: Future, where
F2: Future, F1: Future,
F3: Future, F2: Future,
{ F3: Future,
future1: MaybeDone<F1>, {
future2: MaybeDone<F2>, #[pin]
future3: MaybeDone<F3>, future1: MaybeDone<F1>,
#[pin]
future2: MaybeDone<F2>,
#[pin]
future3: MaybeDone<F3>,
}
} }
impl<T1, F1, T2, F2, T3, F3, E> Future for TryJoin3<F1, F2, F3> impl<T1, F1, T2, F2, T3, F3, E> Future for TryJoin3<F1, F2, F3>
@@ -43,73 +49,34 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut all_done = true; let mut all_done = true;
// Safety: the fn takes `Pin`, we don't move any data out of `self`. let mut me = self.project();
unsafe {
let me = self.get_unchecked_mut();
if Pin::new_unchecked(&mut me.future1).poll(cx).is_pending() { if me.future1.as_mut().poll(cx).is_pending() {
all_done = false; all_done = false;
} else if Pin::new_unchecked(&mut me.future1) } else if me.future1.as_mut().output_mut().unwrap().is_err() {
.output_mut() return Poll::Ready(Err(me.future1.take_output().unwrap().err().unwrap()));
.unwrap() }
.is_err()
{
return Poll::Ready(Err(Pin::new_unchecked(&mut me.future1)
.take_output()
.unwrap()
.err()
.unwrap()));
}
if Pin::new_unchecked(&mut me.future2).poll(cx).is_pending() { if me.future2.as_mut().poll(cx).is_pending() {
all_done = false; all_done = false;
} else if Pin::new_unchecked(&mut me.future2) } else if me.future2.as_mut().output_mut().unwrap().is_err() {
.output_mut() return Poll::Ready(Err(me.future2.take_output().unwrap().err().unwrap()));
.unwrap() }
.is_err()
{
return Poll::Ready(Err(Pin::new_unchecked(&mut me.future2)
.take_output()
.unwrap()
.err()
.unwrap()));
}
if Pin::new_unchecked(&mut me.future3).poll(cx).is_pending() { if me.future3.as_mut().poll(cx).is_pending() {
all_done = false; all_done = false;
} else if Pin::new_unchecked(&mut me.future3) } else if me.future3.as_mut().output_mut().unwrap().is_err() {
.output_mut() return Poll::Ready(Err(me.future3.take_output().unwrap().err().unwrap()));
.unwrap() }
.is_err()
{
return Poll::Ready(Err(Pin::new_unchecked(&mut me.future3)
.take_output()
.unwrap()
.err()
.unwrap()));
}
if all_done { if all_done {
Poll::Ready(Ok(( Poll::Ready(Ok((
Pin::new_unchecked(&mut me.future1) me.future1.take_output().unwrap().ok().unwrap(),
.take_output() me.future2.take_output().unwrap().ok().unwrap(),
.unwrap() me.future3.take_output().unwrap().ok().unwrap(),
.ok() )))
.unwrap(), } else {
Pin::new_unchecked(&mut me.future2) Poll::Pending
.take_output()
.unwrap()
.ok()
.unwrap(),
Pin::new_unchecked(&mut me.future3)
.take_output()
.unwrap()
.ok()
.unwrap(),
)))
} else {
Poll::Pending
}
} }
} }
} }
+31 -37
View File
@@ -1,35 +1,36 @@
use crate::io::util::DEFAULT_BUF_SIZE; use crate::io::util::DEFAULT_BUF_SIZE;
use crate::io::{AsyncBufRead, AsyncRead, AsyncWrite}; use crate::io::{AsyncBufRead, AsyncRead, AsyncWrite};
use pin_project::{pin_project, project}; use pin_project_lite::pin_project;
use std::io::{self, Read}; use std::io::{self, Read};
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::{cmp, fmt}; use std::{cmp, fmt};
/// The `BufReader` struct adds buffering to any reader. pin_project! {
/// /// The `BufReader` struct adds buffering to any reader.
/// It can be excessively inefficient to work directly with a [`AsyncRead`] ///
/// instance. A `BufReader` performs large, infrequent reads on the underlying /// It can be excessively inefficient to work directly with a [`AsyncRead`]
/// [`AsyncRead`] and maintains an in-memory buffer of the results. /// instance. A `BufReader` performs large, infrequent reads on the underlying
/// /// [`AsyncRead`] and maintains an in-memory buffer of the results.
/// `BufReader` can improve the speed of programs that make *small* and ///
/// *repeated* read calls to the same file or network socket. It does not /// `BufReader` can improve the speed of programs that make *small* and
/// help when reading very large amounts at once, or reading just one or a few /// *repeated* read calls to the same file or network socket. It does not
/// times. It also provides no advantage when reading from a source that is /// help when reading very large amounts at once, or reading just one or a few
/// already in memory, like a `Vec<u8>`. /// times. It also provides no advantage when reading from a source that is
/// /// already in memory, like a `Vec<u8>`.
/// When the `BufReader` is dropped, the contents of its buffer will be ///
/// discarded. Creating multiple instances of a `BufReader` on the same /// When the `BufReader` is dropped, the contents of its buffer will be
/// stream can cause data loss. /// discarded. Creating multiple instances of a `BufReader` on the same
// TODO: Examples /// stream can cause data loss.
#[pin_project] // TODO: Examples
pub struct BufReader<R> { pub struct BufReader<R> {
#[pin] #[pin]
pub(super) inner: R, pub(super) inner: R,
pub(super) buf: Box<[u8]>, pub(super) buf: Box<[u8]>,
pub(super) pos: usize, pub(super) pos: usize,
pub(super) cap: usize, pub(super) cap: usize,
}
} }
impl<R: AsyncRead> BufReader<R> { impl<R: AsyncRead> BufReader<R> {
@@ -125,26 +126,19 @@ impl<R: AsyncRead> AsyncRead for BufReader<R> {
} }
impl<R: AsyncRead> AsyncBufRead for BufReader<R> { impl<R: AsyncRead> AsyncBufRead for BufReader<R> {
#[project]
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
#[project] let me = self.project();
let BufReader {
inner,
buf,
cap,
pos,
} = self.project();
// If we've reached the end of our internal buffer then we need to fetch // If we've reached the end of our internal buffer then we need to fetch
// some more data from the underlying reader. // some more data from the underlying reader.
// Branch using `>=` instead of the more correct `==` // Branch using `>=` instead of the more correct `==`
// to tell the compiler that the pos..cap slice is always valid. // to tell the compiler that the pos..cap slice is always valid.
if *pos >= *cap { if *me.pos >= *me.cap {
debug_assert!(*pos == *cap); debug_assert!(*me.pos == *me.cap);
*cap = ready!(inner.poll_read(cx, buf))?; *me.cap = ready!(me.inner.poll_read(cx, me.buf))?;
*pos = 0; *me.pos = 0;
} }
Poll::Ready(Ok(&buf[*pos..*cap])) Poll::Ready(Ok(&me.buf[*me.pos..*me.cap]))
} }
fn consume(self: Pin<&mut Self>, amt: usize) { fn consume(self: Pin<&mut Self>, amt: usize) {
+41 -33
View File
@@ -1,64 +1,70 @@
use crate::io::util::{BufReader, BufWriter}; use crate::io::util::{BufReader, BufWriter};
use crate::io::{AsyncBufRead, AsyncRead, AsyncWrite}; use crate::io::{AsyncBufRead, AsyncRead, AsyncWrite};
use pin_project::pin_project; use pin_project_lite::pin_project;
use std::io::{self}; use std::io::{self};
use std::{ use std::{
pin::Pin, pin::Pin,
task::{Context, Poll}, task::{Context, Poll},
}; };
/// Wraps a type that is [`AsyncWrite`] and [`AsyncRead`], and buffers its input and output. pin_project! {
/// /// Wraps a type that is [`AsyncWrite`] and [`AsyncRead`], and buffers its input and output.
/// It can be excessively inefficient to work directly with something that implements [`AsyncWrite`] ///
/// and [`AsyncRead`]. For example, every `write`, however small, has to traverse the syscall /// It can be excessively inefficient to work directly with something that implements [`AsyncWrite`]
/// interface, and similarly, every read has to do the same. The [`BufWriter`] and [`BufReader`] /// and [`AsyncRead`]. For example, every `write`, however small, has to traverse the syscall
/// types aid with these problems respectively, but do so in only one direction. `BufStream` wraps /// interface, and similarly, every read has to do the same. The [`BufWriter`] and [`BufReader`]
/// one in the other so that both directions are buffered. See their documentation for details. /// types aid with these problems respectively, but do so in only one direction. `BufStream` wraps
#[pin_project] /// one in the other so that both directions are buffered. See their documentation for details.
#[derive(Debug)] #[derive(Debug)]
pub struct BufStream<RW>(#[pin] BufReader<BufWriter<RW>>); pub struct BufStream<RW> {
#[pin]
inner: BufReader<BufWriter<RW>>,
}
}
impl<RW: AsyncRead + AsyncWrite> BufStream<RW> { impl<RW: AsyncRead + AsyncWrite> BufStream<RW> {
/// Wrap a type in both [`BufWriter`] and [`BufReader`]. /// Wrap a type in both [`BufWriter`] and [`BufReader`].
/// ///
/// See the documentation for those types and [`BufStream`] for details. /// See the documentation for those types and [`BufStream`] for details.
pub fn new(stream: RW) -> BufStream<RW> { pub fn new(stream: RW) -> BufStream<RW> {
BufStream(BufReader::new(BufWriter::new(stream))) BufStream {
inner: BufReader::new(BufWriter::new(stream)),
}
} }
/// Gets a reference to the underlying I/O object. /// Gets a reference to the underlying I/O object.
/// ///
/// It is inadvisable to directly read from the underlying I/O object. /// It is inadvisable to directly read from the underlying I/O object.
pub fn get_ref(&self) -> &RW { pub fn get_ref(&self) -> &RW {
self.0.get_ref().get_ref() self.inner.get_ref().get_ref()
} }
/// Gets a mutable reference to the underlying I/O object. /// Gets a mutable reference to the underlying I/O object.
/// ///
/// It is inadvisable to directly read from the underlying I/O object. /// It is inadvisable to directly read from the underlying I/O object.
pub fn get_mut(&mut self) -> &mut RW { pub fn get_mut(&mut self) -> &mut RW {
self.0.get_mut().get_mut() self.inner.get_mut().get_mut()
} }
/// Gets a pinned mutable reference to the underlying I/O object. /// Gets a pinned mutable reference to the underlying I/O object.
/// ///
/// It is inadvisable to directly read from the underlying I/O object. /// It is inadvisable to directly read from the underlying I/O object.
pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut RW> { pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut RW> {
self.project().0.get_pin_mut().get_pin_mut() self.project().inner.get_pin_mut().get_pin_mut()
} }
/// Consumes this `BufStream`, returning the underlying I/O object. /// Consumes this `BufStream`, returning the underlying I/O object.
/// ///
/// Note that any leftover data in the internal buffer is lost. /// Note that any leftover data in the internal buffer is lost.
pub fn into_inner(self) -> RW { pub fn into_inner(self) -> RW {
self.0.into_inner().into_inner() self.inner.into_inner().into_inner()
} }
} }
impl<RW> From<BufReader<BufWriter<RW>>> for BufStream<RW> { impl<RW> From<BufReader<BufWriter<RW>>> for BufStream<RW> {
fn from(b: BufReader<BufWriter<RW>>) -> Self { fn from(b: BufReader<BufWriter<RW>>) -> Self {
BufStream(b) BufStream { inner: b }
} }
} }
@@ -77,16 +83,18 @@ impl<RW> From<BufWriter<BufReader<RW>>> for BufStream<RW> {
written, written,
} = b; } = b;
BufStream(BufReader { BufStream {
inner: BufWriter { inner: BufReader {
inner, inner: BufWriter {
buf: wbuf, inner,
written, buf: wbuf,
written,
},
buf: rbuf,
pos,
cap,
}, },
buf: rbuf, }
pos,
cap,
})
} }
} }
@@ -96,15 +104,15 @@ impl<RW: AsyncRead + AsyncWrite> AsyncWrite for BufStream<RW> {
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &[u8], buf: &[u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
self.project().0.poll_write(cx, buf) self.project().inner.poll_write(cx, buf)
} }
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.project().0.poll_flush(cx) self.project().inner.poll_flush(cx)
} }
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.project().0.poll_shutdown(cx) self.project().inner.poll_shutdown(cx)
} }
} }
@@ -114,22 +122,22 @@ impl<RW: AsyncRead + AsyncWrite> AsyncRead for BufStream<RW> {
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &mut [u8], buf: &mut [u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
self.project().0.poll_read(cx, buf) self.project().inner.poll_read(cx, buf)
} }
// we can't skip unconditionally because of the large buffer case in read. // we can't skip unconditionally because of the large buffer case in read.
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
self.0.prepare_uninitialized_buffer(buf) self.inner.prepare_uninitialized_buffer(buf)
} }
} }
impl<RW: AsyncBufRead + AsyncRead + AsyncWrite> AsyncBufRead for BufStream<RW> { impl<RW: AsyncBufRead + AsyncRead + AsyncWrite> AsyncBufRead for BufStream<RW> {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
self.project().0.poll_fill_buf(cx) self.project().inner.poll_fill_buf(cx)
} }
fn consume(self: Pin<&mut Self>, amt: usize) { fn consume(self: Pin<&mut Self>, amt: usize) {
self.project().0.consume(amt) self.project().inner.consume(amt)
} }
} }
+37 -42
View File
@@ -1,39 +1,40 @@
use crate::io::util::DEFAULT_BUF_SIZE; use crate::io::util::DEFAULT_BUF_SIZE;
use crate::io::{AsyncBufRead, AsyncRead, AsyncWrite}; use crate::io::{AsyncBufRead, AsyncRead, AsyncWrite};
use pin_project::{pin_project, project}; use pin_project_lite::pin_project;
use std::fmt; use std::fmt;
use std::io::{self, Write}; use std::io::{self, Write};
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
/// Wraps a writer and buffers its output. pin_project! {
/// /// Wraps a writer and buffers its output.
/// It can be excessively inefficient to work directly with something that ///
/// implements [`AsyncWrite`]. A `BufWriter` keeps an in-memory buffer of data and /// It can be excessively inefficient to work directly with something that
/// writes it to an underlying writer in large, infrequent batches. /// implements [`AsyncWrite`]. A `BufWriter` keeps an in-memory buffer of data and
/// /// writes it to an underlying writer in large, infrequent batches.
/// `BufWriter` can improve the speed of programs that make *small* and ///
/// *repeated* write calls to the same file or network socket. It does not /// `BufWriter` can improve the speed of programs that make *small* and
/// help when writing very large amounts at once, or writing just one or a few /// *repeated* write calls to the same file or network socket. It does not
/// times. It also provides no advantage when writing to a destination that is /// help when writing very large amounts at once, or writing just one or a few
/// in memory, like a `Vec<u8>`. /// times. It also provides no advantage when writing to a destination that is
/// /// in memory, like a `Vec<u8>`.
/// When the `BufWriter` is dropped, the contents of its buffer will be ///
/// discarded. Creating multiple instances of a `BufWriter` on the same /// When the `BufWriter` is dropped, the contents of its buffer will be
/// stream can cause data loss. If you need to write out the contents of its /// discarded. Creating multiple instances of a `BufWriter` on the same
/// buffer, you must manually call flush before the writer is dropped. /// stream can cause data loss. If you need to write out the contents of its
/// /// buffer, you must manually call flush before the writer is dropped.
/// [`AsyncWrite`]: AsyncWrite ///
/// [`flush`]: super::AsyncWriteExt::flush /// [`AsyncWrite`]: AsyncWrite
/// /// [`flush`]: super::AsyncWriteExt::flush
// TODO: Examples ///
#[pin_project] // TODO: Examples
pub struct BufWriter<W> { pub struct BufWriter<W> {
#[pin] #[pin]
pub(super) inner: W, pub(super) inner: W,
pub(super) buf: Vec<u8>, pub(super) buf: Vec<u8>,
pub(super) written: usize, pub(super) written: usize,
}
} }
impl<W: AsyncWrite> BufWriter<W> { impl<W: AsyncWrite> BufWriter<W> {
@@ -52,19 +53,13 @@ impl<W: AsyncWrite> BufWriter<W> {
} }
} }
#[project]
fn flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
#[project] let mut me = self.project();
let BufWriter {
mut inner,
buf,
written,
} = self.project();
let len = buf.len(); let len = me.buf.len();
let mut ret = Ok(()); let mut ret = Ok(());
while *written < len { while *me.written < len {
match ready!(inner.as_mut().poll_write(cx, &buf[*written..])) { match ready!(me.inner.as_mut().poll_write(cx, &me.buf[*me.written..])) {
Ok(0) => { Ok(0) => {
ret = Err(io::Error::new( ret = Err(io::Error::new(
io::ErrorKind::WriteZero, io::ErrorKind::WriteZero,
@@ -72,17 +67,17 @@ impl<W: AsyncWrite> BufWriter<W> {
)); ));
break; break;
} }
Ok(n) => *written += n, Ok(n) => *me.written += n,
Err(e) => { Err(e) => {
ret = Err(e); ret = Err(e);
break; break;
} }
} }
} }
if *written > 0 { if *me.written > 0 {
buf.drain(..*written); me.buf.drain(..*me.written);
} }
*written = 0; *me.written = 0;
Poll::Ready(ret) Poll::Ready(ret)
} }
+16 -21
View File
@@ -1,20 +1,21 @@
use crate::io::{AsyncBufRead, AsyncRead}; use crate::io::{AsyncBufRead, AsyncRead};
use pin_project::{pin_project, project}; use pin_project_lite::pin_project;
use std::fmt; use std::fmt;
use std::io; use std::io;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
/// Stream for the [`chain`](super::AsyncReadExt::chain) method. pin_project! {
#[pin_project] /// Stream for the [`chain`](super::AsyncReadExt::chain) method.
#[must_use = "streams do nothing unless polled"] #[must_use = "streams do nothing unless polled"]
pub struct Chain<T, U> { pub struct Chain<T, U> {
#[pin] #[pin]
first: T, first: T,
#[pin] #[pin]
second: U, second: U,
done_first: bool, done_first: bool,
}
} }
pub(super) fn chain<T, U>(first: T, second: U) -> Chain<T, U> pub(super) fn chain<T, U>(first: T, second: U) -> Chain<T, U>
@@ -104,24 +105,18 @@ where
T: AsyncBufRead, T: AsyncBufRead,
U: AsyncBufRead, U: AsyncBufRead,
{ {
#[project]
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
#[project] let me = self.project();
let Chain {
first,
second,
done_first,
} = self.project();
if !*done_first { if !*me.done_first {
match ready!(first.poll_fill_buf(cx)?) { match ready!(me.first.poll_fill_buf(cx)?) {
buf if buf.is_empty() => { buf if buf.is_empty() => {
*done_first = true; *me.done_first = true;
} }
buf => return Poll::Ready(Ok(buf)), buf => return Poll::Ready(Ok(buf)),
} }
} }
second.poll_fill_buf(cx) me.second.poll_fill_buf(cx)
} }
fn consume(self: Pin<&mut Self>, amt: usize) { fn consume(self: Pin<&mut Self>, amt: usize) {
+15 -16
View File
@@ -1,19 +1,20 @@
use crate::io::{AsyncBufRead, AsyncRead}; use crate::io::{AsyncBufRead, AsyncRead};
use pin_project::{pin_project, project}; use pin_project_lite::pin_project;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::{cmp, io}; use std::{cmp, io};
/// Stream for the [`take`](super::AsyncReadExt::take) method. pin_project! {
#[pin_project] /// Stream for the [`take`](super::AsyncReadExt::take) method.
#[derive(Debug)] #[derive(Debug)]
#[must_use = "streams do nothing unless you `.await` or poll them"] #[must_use = "streams do nothing unless you `.await` or poll them"]
pub struct Take<R> { pub struct Take<R> {
#[pin] #[pin]
inner: R, inner: R,
// Add '_' to avoid conflicts with `limit` method. // Add '_' to avoid conflicts with `limit` method.
limit_: u64, limit_: u64,
}
} }
pub(super) fn take<R: AsyncRead>(inner: R, limit: u64) -> Take<R> { pub(super) fn take<R: AsyncRead>(inner: R, limit: u64) -> Take<R> {
@@ -95,18 +96,16 @@ impl<R: AsyncRead> AsyncRead for Take<R> {
} }
impl<R: AsyncBufRead> AsyncBufRead for Take<R> { impl<R: AsyncBufRead> AsyncBufRead for Take<R> {
#[project]
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
#[project] let me = self.project();
let Take { inner, limit_ } = self.project();
// Don't call into inner reader at all at EOF because it may still block // Don't call into inner reader at all at EOF because it may still block
if *limit_ == 0 { if *me.limit_ == 0 {
return Poll::Ready(Ok(&[])); return Poll::Ready(Ok(&[]));
} }
let buf = ready!(inner.poll_fill_buf(cx)?); let buf = ready!(me.inner.poll_fill_buf(cx)?);
let cap = cmp::min(buf.len() as u64, *limit_) as usize; let cap = cmp::min(buf.len() as u64, *me.limit_) as usize;
Poll::Ready(Ok(&buf[..cap])) Poll::Ready(Ok(&buf[..cap]))
} }