about summary refs log tree commit diff
path: root/library/std/src
diff options
context:
space:
mode:
authorMikhail Zabaluev <mikhail.zabaluev@gmail.com>2020-11-04 23:55:41 +0200
committerMikhail Zabaluev <mikhail.zabaluev@gmail.com>2020-11-22 17:05:13 +0200
commit53196a8bcfe80db551aa9417ce93766c7ee0e46d (patch)
treef0a660951b46a7999dff1b0a837840419b0ee05a /library/std/src
parent5d5ff84130da0d74c6ece368dbe821d8f83fa526 (diff)
downloadrust-53196a8bcfe80db551aa9417ce93766c7ee0e46d.tar.gz
rust-53196a8bcfe80db551aa9417ce93766c7ee0e46d.zip
Optimize write_vectored for BufWriter
If the underlying writer does not support efficient vectored output,
do it differently: always try to coalesce the slices in the buffer
until one comes that does not fit entirely. Flush the buffer before
the first slice if needed.
Diffstat (limited to 'library/std/src')
-rw-r--r--library/std/src/io/buffered/bufwriter.rs62
1 files changed, 50 insertions, 12 deletions
diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs
index 067ed6ba7ff..d8d62c4b314 100644
--- a/library/std/src/io/buffered/bufwriter.rs
+++ b/library/std/src/io/buffered/bufwriter.rs
@@ -328,19 +328,57 @@ impl<W: Write> Write for BufWriter<W> {
     }
 
     fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
-        let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
-        if self.buf.len() + total_len > self.buf.capacity() {
-            self.flush_buf()?;
-        }
-        // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919
-        if total_len >= self.buf.capacity() {
-            self.panicked = true;
-            let r = self.get_mut().write_vectored(bufs);
-            self.panicked = false;
-            r
+        if self.get_ref().is_write_vectored() {
+            let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
+            if self.buf.len() + total_len > self.buf.capacity() {
+                self.flush_buf()?;
+            }
+            if total_len >= self.buf.capacity() {
+                self.panicked = true;
+                let r = self.get_mut().write_vectored(bufs);
+                self.panicked = false;
+                r
+            } else {
+                bufs.iter().for_each(|b| self.buf.extend_from_slice(b));
+                Ok(total_len)
+            }
         } else {
-            bufs.iter().for_each(|b| self.buf.extend_from_slice(b));
-            Ok(total_len)
+            let mut total_written = 0;
+            let mut iter = bufs.iter();
+            if let Some(buf) = iter.by_ref().find(|&buf| !buf.is_empty()) {
+                // This is the first non-empty slice to write, so if it does
+                // not fit in the buffer, we still get to flush and proceed.
+                if self.buf.len() + buf.len() > self.buf.capacity() {
+                    self.flush_buf()?;
+                }
+                if buf.len() >= self.buf.capacity() {
+                    // The slice is at least as large as the buffering capacity,
+                    // so it's better to write it directly, bypassing the buffer.
+                    self.panicked = true;
+                    let r = self.get_mut().write(buf);
+                    self.panicked = false;
+                    return r;
+                } else {
+                    self.buf.extend_from_slice(buf);
+                    total_written += buf.len();
+                }
+                debug_assert!(total_written != 0);
+            }
+            for buf in iter {
+                if buf.len() >= self.buf.capacity() {
+                    // This slice should be written directly, but we have
+                    // already buffered some of the input. Bail out,
+                    // expecting it to be handled as the first slice in the
+                    // next call to write_vectored.
+                    break;
+                } else {
+                    total_written += self.write_to_buf(buf);
+                    if self.buf.capacity() == self.buf.len() {
+                        break;
+                    }
+                }
+            }
+            Ok(total_written)
         }
     }