summary refs log tree commit diff
path: root/src/libstd/io
diff options
context:
space:
mode:
authorSteven Fackler <sfackler@gmail.com>2019-02-08 20:42:34 +0100
committerSteven Fackler <sfackler@gmail.com>2019-02-13 19:40:17 -0800
commit31bcec648aa57391115f877a2ca022d7ff6415aa (patch)
tree2e9ba98a3146cb8eebf72d469a81615389edd4fd /src/libstd/io
parent4772dc8087b1d0f2bab6d064fd930e596c82d439 (diff)
downloadrust-31bcec648aa57391115f877a2ca022d7ff6415aa.tar.gz
rust-31bcec648aa57391115f877a2ca022d7ff6415aa.zip
Add vectored read and write support
This functionality has lived for a while in the tokio ecosystem, where
it can improve performance by minimizing copies.
Diffstat (limited to 'src/libstd/io')
-rw-r--r--src/libstd/io/buffered.rs33
-rw-r--r--src/libstd/io/cursor.rs217
-rw-r--r--src/libstd/io/impls.rs59
-rw-r--r--src/libstd/io/mod.rs124
-rw-r--r--src/libstd/io/util.rs18
5 files changed, 441 insertions, 10 deletions
diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs
index 0615cd59db4..a5edc4360ca 100644
--- a/src/libstd/io/buffered.rs
+++ b/src/libstd/io/buffered.rs
@@ -5,7 +5,7 @@ use io::prelude::*;
 use cmp;
 use error;
 use fmt;
-use io::{self, Initializer, DEFAULT_BUF_SIZE, Error, ErrorKind, SeekFrom};
+use io::{self, Initializer, DEFAULT_BUF_SIZE, Error, ErrorKind, SeekFrom, IoVec, IoVecMut};
 use memchr;
 
 /// The `BufReader` struct adds buffering to any reader.
@@ -235,6 +235,19 @@ impl<R: Read> Read for BufReader<R> {
         Ok(nread)
     }
 
+    fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        let total_len = bufs.iter().map(|b| b.as_slice().len()).sum::<usize>();
+        if self.pos == self.cap && total_len >= self.buf.len() {
+            return self.inner.read_vectored(bufs);
+        }
+        let nread = {
+            let mut rem = self.fill_buf()?;
+            rem.read_vectored(bufs)?
+        };
+        self.consume(nread);
+        Ok(nread)
+    }
+
     // we can't skip unconditionally because of the large buffer case in read.
     unsafe fn initializer(&self) -> Initializer {
         self.inner.initializer()
@@ -577,9 +590,25 @@ impl<W: Write> Write for BufWriter<W> {
             self.panicked = false;
             r
         } else {
-            Write::write(&mut self.buf, buf)
+            self.buf.write(buf)
+        }
+    }
+
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        let total_len = bufs.iter().map(|b| b.as_slice().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.inner.as_mut().unwrap().write_vectored(bufs);
+            self.panicked = false;
+            r
+        } else {
+            self.buf.write_vectored(bufs)
         }
     }
+
     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 b205f788838..ef636cc6f8c 100644
--- a/src/libstd/io/cursor.rs
+++ b/src/libstd/io/cursor.rs
@@ -2,7 +2,7 @@ use io::prelude::*;
 
 use core::convert::TryInto;
 use cmp;
-use io::{self, Initializer, SeekFrom, Error, ErrorKind};
+use io::{self, Initializer, SeekFrom, Error, ErrorKind, IoVec, IoVecMut};
 
 /// A `Cursor` wraps an in-memory buffer and provides it with a
 /// [`Seek`] implementation.
@@ -221,6 +221,19 @@ impl<T> Read for Cursor<T> where T: AsRef<[u8]> {
         Ok(n)
     }
 
+    fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        let mut nread = 0;
+        for buf in bufs {
+            let buf = buf.as_mut_slice();
+            let n = self.read(buf)?;
+            nread += n;
+            if n < buf.len() {
+                break;
+            }
+        }
+        Ok(nread)
+    }
+
     fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
         let n = buf.len();
         Read::read_exact(&mut self.fill_buf()?, buf)?;
@@ -251,6 +264,24 @@ fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<us
     Ok(amt)
 }
 
+fn slice_write_vectored(
+    pos_mut: &mut u64,
+    slice: &mut [u8],
+    bufs: &[IoVec<'_>],
+) -> io::Result<usize>
+{
+    let mut nwritten = 0;
+    for buf in bufs {
+        let buf = buf.as_slice();
+        let n = slice_write(pos_mut, slice, buf)?;
+        nwritten += n;
+        if n < buf.len() {
+            break;
+        }
+    }
+    Ok(nwritten)
+}
+
 // Resizing write implementation
 fn vec_write(pos_mut: &mut u64, vec: &mut Vec<u8>, buf: &[u8]) -> io::Result<usize> {
     let pos: usize = (*pos_mut).try_into().map_err(|_| {
@@ -278,12 +309,31 @@ fn vec_write(pos_mut: &mut u64, vec: &mut Vec<u8>, buf: &[u8]) -> io::Result<usi
     Ok(buf.len())
 }
 
+fn vec_write_vectored(
+    pos_mut: &mut u64,
+    vec: &mut Vec<u8>,
+    bufs: &[IoVec<'_>],
+) -> io::Result<usize>
+{
+    let mut nwritten = 0;
+    for buf in bufs {
+        nwritten += vec_write(pos_mut, vec, buf.as_slice())?;
+    }
+    Ok(nwritten)
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a> Write for Cursor<&'a mut [u8]> {
     #[inline]
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         slice_write(&mut self.pos, self.inner, buf)
     }
+
+    #[inline]
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        slice_write_vectored(&mut self.pos, self.inner, bufs)
+    }
+
     fn flush(&mut self) -> io::Result<()> { Ok(()) }
 }
 
@@ -292,6 +342,11 @@ impl<'a> Write for Cursor<&'a mut Vec<u8>> {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         vec_write(&mut self.pos, self.inner, buf)
     }
+
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        vec_write_vectored(&mut self.pos, self.inner, bufs)
+    }
+
     fn flush(&mut self) -> io::Result<()> { Ok(()) }
 }
 
@@ -300,6 +355,11 @@ impl Write for Cursor<Vec<u8>> {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         vec_write(&mut self.pos, &mut self.inner, buf)
     }
+
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        vec_write_vectored(&mut self.pos, &mut self.inner, bufs)
+    }
+
     fn flush(&mut self) -> io::Result<()> { Ok(()) }
 }
 
@@ -309,13 +369,19 @@ impl Write for Cursor<Box<[u8]>> {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         slice_write(&mut self.pos, &mut self.inner, buf)
     }
+
+    #[inline]
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        slice_write_vectored(&mut self.pos, &mut self.inner, bufs)
+    }
+
     fn flush(&mut self) -> io::Result<()> { Ok(()) }
 }
 
 #[cfg(test)]
 mod tests {
     use io::prelude::*;
-    use io::{Cursor, SeekFrom};
+    use io::{Cursor, SeekFrom, IoVec, IoVecMut};
 
     #[test]
     fn test_vec_writer() {
@@ -323,7 +389,10 @@ mod tests {
         assert_eq!(writer.write(&[0]).unwrap(), 1);
         assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
         assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
-        let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
+        assert_eq!(writer.write_vectored(
+            &[IoVec::new(&[]), IoVec::new(&[8, 9]), IoVec::new(&[10])],
+        ).unwrap(), 3);
+        let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
         assert_eq!(writer, b);
     }
 
@@ -333,7 +402,10 @@ mod tests {
         assert_eq!(writer.write(&[0]).unwrap(), 1);
         assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
         assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
-        let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
+        assert_eq!(writer.write_vectored(
+            &[IoVec::new(&[]), IoVec::new(&[8, 9]), IoVec::new(&[10])],
+        ).unwrap(), 3);
+        let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
         assert_eq!(&writer.get_ref()[..], b);
     }
 
@@ -344,7 +416,10 @@ mod tests {
         assert_eq!(writer.write(&[0]).unwrap(), 1);
         assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
         assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
-        let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
+        assert_eq!(writer.write_vectored(
+            &[IoVec::new(&[]), IoVec::new(&[8, 9]), IoVec::new(&[10])],
+        ).unwrap(), 3);
+        let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
         assert_eq!(&writer.get_ref()[..], b);
     }
 
@@ -367,6 +442,26 @@ mod tests {
     }
 
     #[test]
+    fn test_box_slice_writer_vectored() {
+        let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
+        assert_eq!(writer.position(), 0);
+        assert_eq!(writer.write_vectored(&[IoVec::new(&[0])]).unwrap(), 1);
+        assert_eq!(writer.position(), 1);
+        assert_eq!(
+            writer.write_vectored(&[IoVec::new(&[1, 2, 3]), IoVec::new(&[4, 5, 6, 7])]).unwrap(),
+            7,
+        );
+        assert_eq!(writer.position(), 8);
+        assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
+        assert_eq!(writer.position(), 8);
+
+        assert_eq!(writer.write_vectored(&[IoVec::new(&[8, 9])]).unwrap(), 1);
+        assert_eq!(writer.write_vectored(&[IoVec::new(&[10])]).unwrap(), 0);
+        let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
+        assert_eq!(&**writer.get_ref(), b);
+    }
+
+    #[test]
     fn test_buf_writer() {
         let mut buf = [0 as u8; 9];
         {
@@ -388,6 +483,31 @@ mod tests {
     }
 
     #[test]
+    fn test_buf_writer_vectored() {
+        let mut buf = [0 as u8; 9];
+        {
+            let mut writer = Cursor::new(&mut buf[..]);
+            assert_eq!(writer.position(), 0);
+            assert_eq!(writer.write_vectored(&[IoVec::new(&[0])]).unwrap(), 1);
+            assert_eq!(writer.position(), 1);
+            assert_eq!(
+                writer.write_vectored(
+                    &[IoVec::new(&[1, 2, 3]), IoVec::new(&[4, 5, 6, 7])],
+                ).unwrap(),
+                7,
+            );
+            assert_eq!(writer.position(), 8);
+            assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
+            assert_eq!(writer.position(), 8);
+
+            assert_eq!(writer.write_vectored(&[IoVec::new(&[8, 9])]).unwrap(), 1);
+            assert_eq!(writer.write_vectored(&[IoVec::new(&[10])]).unwrap(), 0);
+        }
+        let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
+        assert_eq!(buf, b);
+    }
+
+    #[test]
     fn test_buf_writer_seek() {
         let mut buf = [0 as u8; 8];
         {
@@ -448,6 +568,35 @@ mod tests {
     }
 
     #[test]
+    fn test_mem_reader_vectored() {
+        let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
+        let mut buf = [];
+        assert_eq!(reader.read_vectored(&mut [IoVecMut::new(&mut buf)]).unwrap(), 0);
+        assert_eq!(reader.position(), 0);
+        let mut buf = [0];
+        assert_eq!(
+            reader.read_vectored(&mut [IoVecMut::new(&mut []), IoVecMut::new(&mut buf)]).unwrap(),
+            1,
+        );
+        assert_eq!(reader.position(), 1);
+        let b: &[_] = &[0];
+        assert_eq!(buf, b);
+        let mut buf1 = [0; 4];
+        let mut buf2 = [0; 4];
+        assert_eq!(
+            reader.read_vectored(
+                &mut [IoVecMut::new(&mut buf1), IoVecMut::new(&mut buf2)],
+            ).unwrap(),
+            7,
+        );
+        let b1: &[_] = &[1, 2, 3, 4];
+        let b2: &[_] = &[5, 6, 7];
+        assert_eq!(buf1, b1);
+        assert_eq!(&buf2[..3], b2);
+        assert_eq!(reader.read(&mut buf).unwrap(), 0);
+    }
+
+    #[test]
     fn test_boxed_slice_reader() {
         let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice());
         let mut buf = [];
@@ -470,6 +619,35 @@ mod tests {
     }
 
     #[test]
+    fn test_boxed_slice_reader_vectored() {
+        let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice());
+        let mut buf = [];
+        assert_eq!(reader.read_vectored(&mut [IoVecMut::new(&mut buf)]).unwrap(), 0);
+        assert_eq!(reader.position(), 0);
+        let mut buf = [0];
+        assert_eq!(
+            reader.read_vectored(&mut [IoVecMut::new(&mut []), IoVecMut::new(&mut buf)]).unwrap(),
+            1,
+        );
+        assert_eq!(reader.position(), 1);
+        let b: &[_] = &[0];
+        assert_eq!(buf, b);
+        let mut buf1 = [0; 4];
+        let mut buf2 = [0; 4];
+        assert_eq!(
+            reader.read_vectored(
+                &mut [IoVecMut::new(&mut buf1), IoVecMut::new(&mut buf2)],
+            ).unwrap(),
+            7,
+        );
+        let b1: &[_] = &[1, 2, 3, 4];
+        let b2: &[_] = &[5, 6, 7];
+        assert_eq!(buf1, b1);
+        assert_eq!(&buf2[..3], b2);
+        assert_eq!(reader.read(&mut buf).unwrap(), 0);
+    }
+
+    #[test]
     fn read_to_end() {
         let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
         let mut v = Vec::new();
@@ -500,6 +678,35 @@ mod tests {
     }
 
     #[test]
+    fn test_slice_reader_vectored() {
+        let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
+        let reader = &mut &in_buf[..];
+        let mut buf = [];
+        assert_eq!(reader.read_vectored(&mut [IoVecMut::new(&mut buf)]).unwrap(), 0);
+        let mut buf = [0];
+        assert_eq!(
+            reader.read_vectored(&mut [IoVecMut::new(&mut []), IoVecMut::new(&mut buf)]).unwrap(),
+            1,
+        );
+        assert_eq!(reader.len(), 7);
+        let b: &[_] = &[0];
+        assert_eq!(buf, b);
+        let mut buf1 = [0; 4];
+        let mut buf2 = [0; 4];
+        assert_eq!(
+            reader.read_vectored(
+                &mut [IoVecMut::new(&mut buf1), IoVecMut::new(&mut buf2)],
+            ).unwrap(),
+            7,
+        );
+        let b1: &[_] = &[1, 2, 3, 4];
+        let b2: &[_] = &[5, 6, 7];
+        assert_eq!(buf1, b1);
+        assert_eq!(&buf2[..3], b2);
+        assert_eq!(reader.read(&mut buf).unwrap(), 0);
+    }
+
+    #[test]
     fn test_read_exact() {
         let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
         let reader = &mut &in_buf[..];
diff --git a/src/libstd/io/impls.rs b/src/libstd/io/impls.rs
index ec75a87aec3..4bba4af77c4 100644
--- a/src/libstd/io/impls.rs
+++ b/src/libstd/io/impls.rs
@@ -1,5 +1,6 @@
 use cmp;
-use io::{self, SeekFrom, Read, Initializer, Write, Seek, BufRead, Error, ErrorKind};
+use io::{self, SeekFrom, Read, Initializer, Write, Seek, BufRead, Error, ErrorKind, IoVecMut,
+         IoVec};
 use fmt;
 use mem;
 
@@ -14,6 +15,11 @@ impl<'a, R: Read + ?Sized> Read for &'a mut R {
     }
 
     #[inline]
+    fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        (**self).read_vectored(bufs)
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         (**self).initializer()
     }
@@ -39,6 +45,11 @@ impl<'a, W: Write + ?Sized> Write for &'a mut W {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> { (**self).write(buf) }
 
     #[inline]
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        (**self).write_vectored(bufs)
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> { (**self).flush() }
 
     #[inline]
@@ -83,6 +94,11 @@ impl<R: Read + ?Sized> Read for Box<R> {
     }
 
     #[inline]
+    fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        (**self).read_vectored(bufs)
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         (**self).initializer()
     }
@@ -108,6 +124,11 @@ impl<W: Write + ?Sized> Write for Box<W> {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> { (**self).write(buf) }
 
     #[inline]
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        (**self).write_vectored(bufs)
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> { (**self).flush() }
 
     #[inline]
@@ -172,6 +193,19 @@ impl<'a> Read for &'a [u8] {
     }
 
     #[inline]
+    fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        let mut nread = 0;
+        for buf in bufs {
+            nread += self.read(buf.as_mut_slice())?;
+            if self.is_empty() {
+                break;
+            }
+        }
+
+        Ok(nread)
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -232,6 +266,19 @@ impl<'a> Write for &'a mut [u8] {
     }
 
     #[inline]
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        let mut nwritten = 0;
+        for buf in bufs {
+            nwritten += self.write(buf.as_slice())?;
+            if self.is_empty() {
+                break;
+            }
+        }
+
+        Ok(nwritten)
+    }
+
+    #[inline]
     fn write_all(&mut self, data: &[u8]) -> io::Result<()> {
         if self.write(data)? == data.len() {
             Ok(())
@@ -255,6 +302,16 @@ impl Write for Vec<u8> {
     }
 
     #[inline]
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        let len = bufs.iter().map(|b| b.as_slice().len()).sum();
+        self.reserve(len);
+        for buf in bufs {
+            self.extend_from_slice(buf.as_slice());
+        }
+        Ok(len)
+    }
+
+    #[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 c0570ae60a1..b9765605f8e 100644
--- a/src/libstd/io/mod.rs
+++ b/src/libstd/io/mod.rs
@@ -265,6 +265,7 @@ use slice;
 use str;
 use memchr;
 use ptr;
+use sys;
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::buffered::{BufReader, BufWriter, LineWriter};
@@ -520,6 +521,22 @@ pub trait Read {
     #[stable(feature = "rust1", since = "1.0.0")]
     fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
 
+    /// Like `read`, except that it reads into a slice of buffers.
+    ///
+    /// Data is copied to fill each buffer in order, with the final buffer
+    /// written to possibly being only partially filled. This method must behave
+    /// as a single call to `read` with the buffers concatenated would.
+    ///
+    /// The default implementation simply passes the first nonempty buffer to
+    /// `read`.
+    #[unstable(feature = "iovec", issue = "0")]
+    fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> Result<usize> {
+        match bufs.iter_mut().map(|b| b.as_mut_slice()).find(|b| !b.is_empty()) {
+            Some(buf) => self.read(buf),
+            None => Ok(0),
+        }
+    }
+
     /// Determines if this `Read`er can work with buffers of uninitialized
     /// memory.
     ///
@@ -867,6 +884,85 @@ pub trait Read {
     }
 }
 
+/// A buffer type used with `Read::read_vectored`.
+///
+/// It is semantically a wrapper around an `&mut [u8]`, but is guaranteed to be
+/// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on
+/// Windows.
+#[unstable(feature = "iovec", issue = "0")]
+#[repr(transparent)]
+pub struct IoVecMut<'a>(sys::io::IoVecMut<'a>);
+
+#[unstable(feature = "iovec", issue = "0")]
+impl<'a> fmt::Debug for IoVecMut<'a> {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Debug::fmt(self.as_slice(), fmt)
+    }
+}
+
+impl<'a> IoVecMut<'a> {
+    /// Creates a new `IoVecMut` wrapping a byte slice.
+    ///
+    /// # Panics
+    ///
+    /// Panics on Windows if the slice is larger than 4GB.
+    #[unstable(feature = "iovec", issue = "0")]
+    #[inline]
+    pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> {
+        IoVecMut(sys::io::IoVecMut::new(buf))
+    }
+
+    /// Returns a shared reference to the inner slice.
+    #[unstable(feature = "iovec", issue = "0")]
+    #[inline]
+    pub fn as_slice(&self) -> &'a [u8] {
+        self.0.as_slice()
+    }
+
+    /// Returns a mutable reference to the inner slice.
+    #[unstable(feature = "iovec", issue = "0")]
+    #[inline]
+    pub fn as_mut_slice(&mut self) -> &'a mut [u8] {
+        self.0.as_mut_slice()
+    }
+}
+
+/// A buffer type used with `Write::write_vectored`.
+///
+/// It is semantically a wrapper around an `&[u8]`, but is guaranteed to be
+/// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on
+/// Windows.
+#[unstable(feature = "iovec", issue = "0")]
+#[repr(transparent)]
+pub struct IoVec<'a>(sys::io::IoVec<'a>);
+
+#[unstable(feature = "iovec", issue = "0")]
+impl<'a> fmt::Debug for IoVec<'a> {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Debug::fmt(self.as_slice(), fmt)
+    }
+}
+
+impl<'a> IoVec<'a> {
+    /// Creates a new `IoVec` wrapping a byte slice.
+    ///
+    /// # Panics
+    ///
+    /// Panics on Windows if the slice is larger than 4GB.
+    #[unstable(feature = "iovec", issue = "0")]
+    #[inline]
+    pub fn new(buf: &'a [u8]) -> IoVec<'a> {
+        IoVec(sys::io::IoVec::new(buf))
+    }
+
+    /// Returns a shared reference to the inner slice.
+    #[unstable(feature = "iovec", issue = "0")]
+    #[inline]
+    pub fn as_slice(&self) -> &'a [u8] {
+        self.0.as_slice()
+    }
+}
+
 /// A type used to conditionally initialize buffers passed to `Read` methods.
 #[unstable(feature = "read_initializer", issue = "42788")]
 #[derive(Debug)]
@@ -997,6 +1093,22 @@ pub trait Write {
     #[stable(feature = "rust1", since = "1.0.0")]
     fn write(&mut self, buf: &[u8]) -> Result<usize>;
 
+    /// Like `write`, except that it writes from a slice of buffers.
+    ///
+    /// Data is copied to from each buffer in order, with the final buffer
+    /// read from possibly being only partially consumed. This method must
+    /// behave as a call to `write` with the buffers concatenated would.
+    ///
+    /// The default implementation simply passes the first nonempty buffer to
+    /// `write`.
+    #[unstable(feature = "iovec", issue = "0")]
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> Result<usize> {
+        match bufs.iter().map(|b| b.as_slice()).find(|b| !b.is_empty()) {
+            Some(buf) => self.write(buf),
+            None => Ok(0),
+        }
+    }
+
     /// Flush this output stream, ensuring that all intermediately buffered
     /// contents reach their destination.
     ///
@@ -1691,13 +1803,23 @@ impl<T: Read, U: Read> Read for Chain<T, U> {
     fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
         if !self.done_first {
             match self.first.read(buf)? {
-                0 if buf.len() != 0 => { self.done_first = true; }
+                0 if buf.len() != 0 => self.done_first = true,
                 n => return Ok(n),
             }
         }
         self.second.read(buf)
     }
 
+    fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> Result<usize> {
+        if !self.done_first {
+            match self.first.read_vectored(bufs)? {
+                0 if bufs.iter().any(|b| !b.as_slice().is_empty()) => self.done_first = true,
+                n => return Ok(n),
+            }
+        }
+        self.second.read_vectored(bufs)
+    }
+
     unsafe fn initializer(&self) -> Initializer {
         let initializer = self.first.initializer();
         if initializer.should_initialize() {
diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs
index 8df961a9add..6743018793f 100644
--- a/src/libstd/io/util.rs
+++ b/src/libstd/io/util.rs
@@ -1,7 +1,7 @@
 #![allow(missing_copy_implementations)]
 
 use fmt;
-use io::{self, Read, Initializer, Write, ErrorKind, BufRead};
+use io::{self, Read, Initializer, Write, ErrorKind, BufRead, IoVec, IoVecMut};
 use mem;
 
 /// Copies the entire contents of a reader into a writer.
@@ -153,6 +153,15 @@ impl Read for Repeat {
     }
 
     #[inline]
+    fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        let mut nwritten = 0;
+        for buf in bufs {
+            nwritten += self.read(buf.as_mut_slice())?;
+        }
+        Ok(nwritten)
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -195,6 +204,13 @@ pub fn sink() -> Sink { Sink { _priv: () } }
 impl Write for Sink {
     #[inline]
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> { Ok(buf.len()) }
+
+    #[inline]
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        let total_len = bufs.iter().map(|b| b.as_slice().len()).sum();
+        Ok(total_len)
+    }
+
     #[inline]
     fn flush(&mut self) -> io::Result<()> { Ok(()) }
 }