Skip to main content

tokio/io/util/
buf_writer.rs

1use crate::io::util::DEFAULT_BUF_SIZE;
2use crate::io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
3
4use pin_project_lite::pin_project;
5use std::fmt;
6use std::io::{self, IoSlice, SeekFrom, Write};
7use std::pin::Pin;
8use std::task::{ready, Context, Poll};
9
10pin_project! {
11    /// Wraps a writer and buffers its output.
12    ///
13    /// It can be excessively inefficient to work directly with something that
14    /// implements [`AsyncWrite`]. A `BufWriter` keeps an in-memory buffer of data and
15    /// writes it to an underlying writer in large, infrequent batches.
16    ///
17    /// `BufWriter` can improve the speed of programs that make *small* and
18    /// *repeated* write calls to the same file or network socket. It does not
19    /// help when writing very large amounts at once, or writing just one or a few
20    /// times. It also provides no advantage when writing to a destination that is
21    /// in memory, like a `Vec<u8>`.
22    ///
23    /// When the `BufWriter` is dropped, the contents of its buffer will be
24    /// discarded. Creating multiple instances of a `BufWriter` on the same
25    /// stream can cause data loss. If you need to write out the contents of its
26    /// buffer, you must manually call flush before the writer is dropped.
27    ///
28    /// [`AsyncWrite`]: AsyncWrite
29    /// [`flush`]: super::AsyncWriteExt::flush
30    ///
31    #[cfg_attr(docsrs, doc(cfg(feature = "io-util")))]
32    pub struct BufWriter<W> {
33        #[pin]
34        pub(super) inner: W,
35        pub(super) buf: Vec<u8>,
36        pub(super) written: usize,
37        pub(super) seek_state: SeekState,
38    }
39}
40
41impl<W: AsyncWrite> BufWriter<W> {
42    /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB,
43    /// but may change in the future.
44    pub fn new(inner: W) -> Self {
45        Self::with_capacity(DEFAULT_BUF_SIZE, inner)
46    }
47
48    /// Creates a new `BufWriter` with the specified buffer capacity.
49    pub fn with_capacity(cap: usize, inner: W) -> Self {
50        Self {
51            inner,
52            buf: Vec::with_capacity(cap),
53            written: 0,
54            seek_state: SeekState::Init,
55        }
56    }
57
58    fn flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
59        let mut me = self.project();
60
61        let len = me.buf.len();
62        let mut ret = Ok(());
63        while *me.written < len {
64            match ready!(me.inner.as_mut().poll_write(cx, &me.buf[*me.written..])) {
65                Ok(0) => {
66                    ret = Err(io::Error::new(
67                        io::ErrorKind::WriteZero,
68                        "failed to write the buffered data",
69                    ));
70                    break;
71                }
72                Ok(n) => *me.written += n,
73                Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
74                Err(e) => {
75                    ret = Err(e);
76                    break;
77                }
78            }
79        }
80        if *me.written > 0 {
81            me.buf.drain(..*me.written);
82        }
83        *me.written = 0;
84        Poll::Ready(ret)
85    }
86
87    /// Gets a reference to the underlying writer.
88    pub fn get_ref(&self) -> &W {
89        &self.inner
90    }
91
92    /// Gets a mutable reference to the underlying writer.
93    ///
94    /// It is inadvisable to directly write to the underlying writer.
95    pub fn get_mut(&mut self) -> &mut W {
96        &mut self.inner
97    }
98
99    /// Gets a pinned mutable reference to the underlying writer.
100    ///
101    /// It is inadvisable to directly write to the underlying writer.
102    pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut W> {
103        self.project().inner
104    }
105
106    /// Consumes this `BufWriter`, returning the underlying writer.
107    ///
108    /// Note that any leftover data in the internal buffer is lost.
109    pub fn into_inner(self) -> W {
110        self.inner
111    }
112
113    /// Returns a reference to the internally buffered data.
114    pub fn buffer(&self) -> &[u8] {
115        &self.buf
116    }
117}
118
119impl<W: AsyncWrite> AsyncWrite for BufWriter<W> {
120    fn poll_write(
121        mut self: Pin<&mut Self>,
122        cx: &mut Context<'_>,
123        buf: &[u8],
124    ) -> Poll<io::Result<usize>> {
125        if self.buf.len() + buf.len() > self.buf.capacity() {
126            ready!(self.as_mut().flush_buf(cx))?;
127        }
128
129        let me = self.project();
130        if buf.len() >= me.buf.capacity() {
131            me.inner.poll_write(cx, buf)
132        } else {
133            Poll::Ready(me.buf.write(buf))
134        }
135    }
136
137    fn poll_write_vectored(
138        mut self: Pin<&mut Self>,
139        cx: &mut Context<'_>,
140        mut bufs: &[IoSlice<'_>],
141    ) -> Poll<io::Result<usize>> {
142        if self.inner.is_write_vectored() {
143            let total_len = bufs
144                .iter()
145                .fold(0usize, |acc, b| acc.saturating_add(b.len()));
146            if total_len > self.buf.capacity() - self.buf.len() {
147                ready!(self.as_mut().flush_buf(cx))?;
148            }
149            let me = self.as_mut().project();
150            if total_len >= me.buf.capacity() {
151                // It's more efficient to pass the slices directly to the
152                // underlying writer than to buffer them.
153                // The case when the total_len calculation saturates at
154                // usize::MAX is also handled here.
155                me.inner.poll_write_vectored(cx, bufs)
156            } else {
157                bufs.iter().for_each(|b| me.buf.extend_from_slice(b));
158                Poll::Ready(Ok(total_len))
159            }
160        } else {
161            // Remove empty buffers at the beginning of bufs.
162            while bufs.first().map(|buf| buf.len()) == Some(0) {
163                bufs = &bufs[1..];
164            }
165            if bufs.is_empty() {
166                return Poll::Ready(Ok(0));
167            }
168            // Flush if the first buffer doesn't fit.
169            let first_len = bufs[0].len();
170            if first_len > self.buf.capacity() - self.buf.len() {
171                ready!(self.as_mut().flush_buf(cx))?;
172                debug_assert!(self.buf.is_empty());
173            }
174            let me = self.as_mut().project();
175            if first_len >= me.buf.capacity() {
176                // The slice is at least as large as the buffering capacity,
177                // so it's better to write it directly, bypassing the buffer.
178                debug_assert!(me.buf.is_empty());
179                return me.inner.poll_write(cx, &bufs[0]);
180            } else {
181                me.buf.extend_from_slice(&bufs[0]);
182                bufs = &bufs[1..];
183            }
184            let mut total_written = first_len;
185            debug_assert!(total_written != 0);
186            // Append the buffers that fit in the internal buffer.
187            for buf in bufs {
188                if buf.len() > me.buf.capacity() - me.buf.len() {
189                    break;
190                } else {
191                    me.buf.extend_from_slice(buf);
192                    total_written += buf.len();
193                }
194            }
195            Poll::Ready(Ok(total_written))
196        }
197    }
198
199    fn is_write_vectored(&self) -> bool {
200        true
201    }
202
203    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
204        ready!(self.as_mut().flush_buf(cx))?;
205        self.get_pin_mut().poll_flush(cx)
206    }
207
208    fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
209        ready!(self.as_mut().flush_buf(cx))?;
210        self.get_pin_mut().poll_shutdown(cx)
211    }
212}
213
214#[derive(Debug, Clone, Copy)]
215pub(super) enum SeekState {
216    /// `start_seek` has not been called.
217    Init,
218    /// `start_seek` has been called, but `poll_complete` has not yet been called.
219    Start(SeekFrom),
220    /// Waiting for completion of `poll_complete`.
221    Pending,
222}
223
224/// Seek to the offset, in bytes, in the underlying writer.
225///
226/// Seeking always writes out the internal buffer before seeking.
227impl<W: AsyncWrite + AsyncSeek> AsyncSeek for BufWriter<W> {
228    fn start_seek(self: Pin<&mut Self>, pos: SeekFrom) -> io::Result<()> {
229        // We need to flush the internal buffer before seeking.
230        // It receives a `Context` and returns a `Poll`, so it cannot be called
231        // inside `start_seek`.
232        *self.project().seek_state = SeekState::Start(pos);
233        Ok(())
234    }
235
236    fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
237        let pos = match self.seek_state {
238            SeekState::Init => {
239                return self.project().inner.poll_complete(cx);
240            }
241            SeekState::Start(pos) => Some(pos),
242            SeekState::Pending => None,
243        };
244
245        // Flush the internal buffer before seeking.
246        ready!(self.as_mut().flush_buf(cx))?;
247
248        let mut me = self.project();
249        if let Some(pos) = pos {
250            // Ensure previous seeks have finished before starting a new one
251            ready!(me.inner.as_mut().poll_complete(cx))?;
252            if let Err(e) = me.inner.as_mut().start_seek(pos) {
253                *me.seek_state = SeekState::Init;
254                return Poll::Ready(Err(e));
255            }
256        }
257        match me.inner.poll_complete(cx) {
258            Poll::Ready(res) => {
259                *me.seek_state = SeekState::Init;
260                Poll::Ready(res)
261            }
262            Poll::Pending => {
263                *me.seek_state = SeekState::Pending;
264                Poll::Pending
265            }
266        }
267    }
268}
269
270impl<W: AsyncWrite + AsyncRead> AsyncRead for BufWriter<W> {
271    fn poll_read(
272        self: Pin<&mut Self>,
273        cx: &mut Context<'_>,
274        buf: &mut ReadBuf<'_>,
275    ) -> Poll<io::Result<()>> {
276        self.get_pin_mut().poll_read(cx, buf)
277    }
278}
279
280impl<W: AsyncWrite + AsyncBufRead> AsyncBufRead for BufWriter<W> {
281    fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
282        self.get_pin_mut().poll_fill_buf(cx)
283    }
284
285    fn consume(self: Pin<&mut Self>, amt: usize) {
286        self.get_pin_mut().consume(amt);
287    }
288}
289
290impl<W: fmt::Debug> fmt::Debug for BufWriter<W> {
291    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292        f.debug_struct("BufWriter")
293            .field("writer", &self.inner)
294            .field(
295                "buffer",
296                &format_args!("{}/{}", self.buf.len(), self.buf.capacity()),
297            )
298            .field("written", &self.written)
299            .finish()
300    }
301}
302
303#[cfg(test)]
304mod tests {
305    use super::*;
306
307    #[test]
308    fn assert_unpin() {
309        crate::is_unpin::<BufWriter<()>>();
310    }
311}