about summary refs log tree commit diff
path: root/src/libstd/io
diff options
context:
space:
mode:
authorSteven Fackler <sfackler@gmail.com>2020-01-03 11:26:05 -0800
committerSteven Fackler <sfackler@gmail.com>2020-04-26 04:23:39 -0700
commit15262ec6be6fcfc9f27e174a0714d5a62e775fb0 (patch)
treea962ae80ff3cbdb331c76426c200cdec0d82219c /src/libstd/io
parent019ab732ce63a117cbb446db1488916c5c0bd2a7 (diff)
downloadrust-15262ec6be6fcfc9f27e174a0714d5a62e775fb0.tar.gz
rust-15262ec6be6fcfc9f27e174a0714d5a62e775fb0.zip
Add Read/Write::can_read/write_vectored
When working with an arbitrary reader or writer, code that uses vectored
operations may end up being slower than code that copies into a single
buffer when the underlying reader or writer doesn't actually support
vectored operations. These new methods allow you to ask the reader or
witer up front if vectored operations are efficiently supported.

Currently, you have to use some heuristics to guess by e.g. checking if
the read or write only accessed the first buffer. Hyper is one concrete
example of a library that has to do this dynamically:
https://github.com/hyperium/hyper/blob/0eaf304644a396895a4ce1f0146e596640bb666a/src/proto/h1/io.rs#L582-L594
Diffstat (limited to 'src/libstd/io')
-rw-r--r--src/libstd/io/buffered.rs8
-rw-r--r--src/libstd/io/cursor.rs24
-rw-r--r--src/libstd/io/impls.rs35
-rw-r--r--src/libstd/io/mod.rs26
-rw-r--r--src/libstd/io/stdio.rs56
-rw-r--r--src/libstd/io/util.rs10
6 files changed, 159 insertions, 0 deletions
diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs
index 16ca539b3c1..cabeaf4ae77 100644
--- a/src/libstd/io/buffered.rs
+++ b/src/libstd/io/buffered.rs
@@ -292,6 +292,10 @@ impl<R: Read> Read for BufReader<R> {
         Ok(nread)
     }
 
+    fn can_read_vectored(&self) -> bool {
+        self.inner.can_read_vectored()
+    }
+
     // we can't skip unconditionally because of the large buffer case in read.
     unsafe fn initializer(&self) -> Initializer {
         self.inner.initializer()
@@ -680,6 +684,10 @@ impl<W: Write> Write for BufWriter<W> {
         }
     }
 
+    fn can_write_vectored(&self) -> bool {
+        self.get_ref().can_write_vectored()
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         self.flush_buf().and_then(|()| self.get_mut().flush())
     }
diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs
index f36aa1846a1..859431ea0ef 100644
--- a/src/libstd/io/cursor.rs
+++ b/src/libstd/io/cursor.rs
@@ -266,6 +266,10 @@ where
         Ok(nread)
     }
 
+    fn can_read_vectored(&self) -> bool {
+        true
+    }
+
     fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
         let n = buf.len();
         Read::read_exact(&mut self.fill_buf()?, buf)?;
@@ -373,6 +377,11 @@ impl Write for Cursor<&mut [u8]> {
     }
 
     #[inline]
+    fn can_write_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
@@ -389,6 +398,11 @@ impl Write for Cursor<&mut Vec<u8>> {
     }
 
     #[inline]
+    fn can_write_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
@@ -405,6 +419,11 @@ impl Write for Cursor<Vec<u8>> {
     }
 
     #[inline]
+    fn can_write_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
@@ -423,6 +442,11 @@ impl Write for Cursor<Box<[u8]>> {
     }
 
     #[inline]
+    fn can_write_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
diff --git a/src/libstd/io/impls.rs b/src/libstd/io/impls.rs
index b7f82e65299..1fb9f12dd90 100644
--- a/src/libstd/io/impls.rs
+++ b/src/libstd/io/impls.rs
@@ -21,6 +21,11 @@ impl<R: Read + ?Sized> Read for &mut R {
     }
 
     #[inline]
+    fn can_read_vectored(&self) -> bool {
+        (**self).can_read_vectored()
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         (**self).initializer()
     }
@@ -53,6 +58,11 @@ impl<W: Write + ?Sized> Write for &mut W {
     }
 
     #[inline]
+    fn can_write_vectored(&self) -> bool {
+        (**self).can_write_vectored()
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> {
         (**self).flush()
     }
@@ -110,6 +120,11 @@ impl<R: Read + ?Sized> Read for Box<R> {
     }
 
     #[inline]
+    fn can_read_vectored(&self) -> bool {
+        (**self).can_read_vectored()
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         (**self).initializer()
     }
@@ -142,6 +157,11 @@ impl<W: Write + ?Sized> Write for Box<W> {
     }
 
     #[inline]
+    fn can_write_vectored(&self) -> bool {
+        (**self).can_write_vectored()
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> {
         (**self).flush()
     }
@@ -241,6 +261,11 @@ impl Read for &[u8] {
     }
 
     #[inline]
+    fn can_read_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -317,6 +342,11 @@ impl Write for &mut [u8] {
     }
 
     #[inline]
+    fn can_write_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     fn write_all(&mut self, data: &[u8]) -> io::Result<()> {
         if self.write(data)? == data.len() {
             Ok(())
@@ -352,6 +382,11 @@ impl Write for Vec<u8> {
     }
 
     #[inline]
+    fn can_write_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
         self.extend_from_slice(buf);
         Ok(())
diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs
index 5ab88260d6a..c6229fb39e0 100644
--- a/src/libstd/io/mod.rs
+++ b/src/libstd/io/mod.rs
@@ -580,6 +580,19 @@ pub trait Read {
         default_read_vectored(|b| self.read(b), bufs)
     }
 
+    /// Determines if this `Read`er has an efficient `read_vectored`
+    /// implementation.
+    ///
+    /// If a `Read`er does not override the default `read_vectored`
+    /// implementation, code using it may want to avoid the method all together
+    /// and coalesce writes into a single buffer for higher performance.
+    ///
+    /// The default implementation returns `false`.
+    #[unstable(feature = "can_vector", issue = "none")]
+    fn can_read_vectored(&self) -> bool {
+        false
+    }
+
     /// Determines if this `Read`er can work with buffers of uninitialized
     /// memory.
     ///
@@ -1304,6 +1317,19 @@ pub trait Write {
         default_write_vectored(|b| self.write(b), bufs)
     }
 
+    /// Determines if this `Write`er has an efficient `write_vectored`
+    /// implementation.
+    ///
+    /// If a `Write`er does not override the default `write_vectored`
+    /// implementation, code using it may want to avoid the method all together
+    /// and coalesce writes into a single buffer for higher performance.
+    ///
+    /// The default implementation returns `false`.
+    #[unstable(feature = "can_vector", issue = "none")]
+    fn can_write_vectored(&self) -> bool {
+        false
+    }
+
     /// Flush this output stream, ensuring that all intermediately buffered
     /// contents reach their destination.
     ///
diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs
index a064c552c84..fd5a1291785 100644
--- a/src/libstd/io/stdio.rs
+++ b/src/libstd/io/stdio.rs
@@ -88,6 +88,11 @@ impl Read for StdinRaw {
     }
 
     #[inline]
+    fn can_read_vectored(&self) -> bool {
+        self.0.can_read_vectored()
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -101,6 +106,11 @@ impl Write for StdoutRaw {
         self.0.write_vectored(bufs)
     }
 
+    #[inline]
+    fn can_write_vectored(&self) -> bool {
+        self.0.can_write_vectored()
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         self.0.flush()
     }
@@ -114,6 +124,11 @@ impl Write for StderrRaw {
         self.0.write_vectored(bufs)
     }
 
+    #[inline]
+    fn can_write_vectored(&self) -> bool {
+        self.0.can_write_vectored()
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         self.0.flush()
     }
@@ -140,6 +155,14 @@ impl<W: io::Write> io::Write for Maybe<W> {
         }
     }
 
+    #[inline]
+    fn can_write_vectored(&self) -> bool {
+        match self {
+            Maybe::Real(w) => w.can_write_vectored(),
+            Maybe::Fake => true,
+        }
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         match *self {
             Maybe::Real(ref mut w) => handle_ebadf(w.flush(), ()),
@@ -162,6 +185,14 @@ impl<R: io::Read> io::Read for Maybe<R> {
             Maybe::Fake => Ok(0),
         }
     }
+
+    #[inline]
+    fn can_read_vectored(&self) -> bool {
+        match self {
+            Maybe::Real(w) => w.can_read_vectored(),
+            Maybe::Fake => true,
+        }
+    }
 }
 
 fn handle_ebadf<T>(r: io::Result<T>, default: T) -> io::Result<T> {
@@ -352,6 +383,10 @@ impl Read for Stdin {
         self.lock().read_vectored(bufs)
     }
     #[inline]
+    fn can_read_vectored(&self) -> bool {
+        self.lock().can_read_vectored()
+    }
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -377,6 +412,11 @@ impl Read for StdinLock<'_> {
     }
 
     #[inline]
+    fn can_read_vectored(&self) -> bool {
+        self.inner.can_read_vectored()
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -543,6 +583,10 @@ impl Write for Stdout {
     fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
         self.lock().write_vectored(bufs)
     }
+    #[inline]
+    fn can_write_vectored(&self) -> bool {
+        self.lock().can_write_vectored()
+    }
     fn flush(&mut self) -> io::Result<()> {
         self.lock().flush()
     }
@@ -561,6 +605,10 @@ impl Write for StdoutLock<'_> {
     fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
         self.inner.borrow_mut().write_vectored(bufs)
     }
+    #[inline]
+    fn can_write_vectored(&self) -> bool {
+        self.inner.borrow_mut().can_write_vectored()
+    }
     fn flush(&mut self) -> io::Result<()> {
         self.inner.borrow_mut().flush()
     }
@@ -709,6 +757,10 @@ impl Write for Stderr {
     fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
         self.lock().write_vectored(bufs)
     }
+    #[inline]
+    fn can_write_vectored(&self) -> bool {
+        self.lock().can_write_vectored()
+    }
     fn flush(&mut self) -> io::Result<()> {
         self.lock().flush()
     }
@@ -727,6 +779,10 @@ impl Write for StderrLock<'_> {
     fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
         self.inner.borrow_mut().write_vectored(bufs)
     }
+    #[inline]
+    fn can_write_vectored(&self) -> bool {
+        self.inner.borrow_mut().can_write_vectored()
+    }
     fn flush(&mut self) -> io::Result<()> {
         self.inner.borrow_mut().flush()
     }
diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs
index b09161b97aa..01947cd8b89 100644
--- a/src/libstd/io/util.rs
+++ b/src/libstd/io/util.rs
@@ -180,6 +180,11 @@ impl Read for Repeat {
     }
 
     #[inline]
+    fn can_read_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -236,6 +241,11 @@ impl Write for Sink {
     }
 
     #[inline]
+    fn can_write_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }