about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-02-26 02:48:13 +0000
committerbors <bors@rust-lang.org>2019-02-26 02:48:13 +0000
commitfb162e69449b423c5aed0d9c39f6c046fa300c30 (patch)
tree5d4292df6a7db1b62137c4472f1e65317d3919e1
parent55c173c8ae8bda689fd609f391ee5e2e5b1b6d44 (diff)
parent4785c748f2190440fb3f90b5319f121f2d31e0e4 (diff)
downloadrust-fb162e69449b423c5aed0d9c39f6c046fa300c30.tar.gz
rust-fb162e69449b423c5aed0d9c39f6c046fa300c30.zip
Auto merge of #58357 - sfackler:vectored-io, r=alexcrichton
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.

r? @alexcrichton
-rw-r--r--src/libstd/io/buffered.rs33
-rw-r--r--src/libstd/io/cursor.rs215
-rw-r--r--src/libstd/io/impls.rs59
-rw-r--r--src/libstd/io/mod.rs132
-rw-r--r--src/libstd/io/util.rs18
-rw-r--r--src/libstd/lib.rs1
-rw-r--r--src/libstd/net/tcp.rs69
-rw-r--r--src/libstd/sys/cloudabi/io.rs32
-rw-r--r--src/libstd/sys/cloudabi/mod.rs36
-rw-r--r--src/libstd/sys/cloudabi/shims/net.rs10
-rw-r--r--src/libstd/sys/redox/io.rs32
-rw-r--r--src/libstd/sys/redox/mod.rs11
-rw-r--r--src/libstd/sys/redox/net/tcp.rs16
-rw-r--r--src/libstd/sys/sgx/io.rs32
-rw-r--r--src/libstd/sys/sgx/mod.rs52
-rw-r--r--src/libstd/sys/sgx/net.rs18
-rw-r--r--src/libstd/sys/unix/ext/net.rs37
-rw-r--r--src/libstd/sys/unix/fd.rs20
-rw-r--r--src/libstd/sys/unix/io.rs61
-rw-r--r--src/libstd/sys/unix/l4re.rs18
-rw-r--r--src/libstd/sys/unix/mod.rs9
-rw-r--r--src/libstd/sys/unix/net.rs10
-rw-r--r--src/libstd/sys/wasm/io.rs32
-rw-r--r--src/libstd/sys/wasm/mod.rs12
-rw-r--r--src/libstd/sys/wasm/net.rs10
-rw-r--r--src/libstd/sys/windows/c.rs25
-rw-r--r--src/libstd/sys/windows/io.rs63
-rw-r--r--src/libstd/sys/windows/mod.rs27
-rw-r--r--src/libstd/sys/windows/net.rs43
-rw-r--r--src/libstd/sys_common/net.rs10
-rw-r--r--src/tools/linkchecker/main.rs4
31 files changed, 1052 insertions, 95 deletions
diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs
index 0c1d155a916..6383a14cf18 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.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.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 758d8568672..577a115025b 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,18 @@ 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 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 +263,23 @@ 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 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 +307,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)?;
+    }
+    Ok(nwritten)
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Write for Cursor<&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 +340,11 @@ impl Write for Cursor<&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 +353,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 +367,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 +387,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 +400,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 +414,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 +440,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 +481,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 +566,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 +617,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 +676,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 2577b284714..aa8db177ffc 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<R: Read + ?Sized> Read for &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<W: Write + ?Sized> Write for &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 Read for &[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)?;
+            if self.is_empty() {
+                break;
+            }
+        }
+
+        Ok(nread)
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -232,6 +266,19 @@ impl Write for &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)?;
+            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.len()).sum();
+        self.reserve(len);
+        for buf in bufs {
+            self.extend_from_slice(buf);
+        }
+        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 b634ea43e34..12b158e4197 100644
--- a/src/libstd/io/mod.rs
+++ b/src/libstd/io/mod.rs
@@ -264,7 +264,9 @@ use fmt;
 use slice;
 use str;
 use memchr;
+use ops::{Deref, DerefMut};
 use ptr;
+use sys;
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::buffered::{BufReader, BufWriter, LineWriter};
@@ -520,6 +522,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 = "58452")]
+    fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> Result<usize> {
+        match bufs.iter_mut().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 +885,92 @@ 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 = "58452")]
+#[repr(transparent)]
+pub struct IoVecMut<'a>(sys::io::IoVecMut<'a>);
+
+#[unstable(feature = "iovec", issue = "58452")]
+impl<'a> fmt::Debug for IoVecMut<'a> {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Debug::fmt(self.0.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 = "58452")]
+    #[inline]
+    pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> {
+        IoVecMut(sys::io::IoVecMut::new(buf))
+    }
+}
+
+#[unstable(feature = "iovec", issue = "58452")]
+impl<'a> Deref for IoVecMut<'a> {
+    type Target = [u8];
+
+    #[inline]
+    fn deref(&self) -> &[u8] {
+        self.0.as_slice()
+    }
+}
+
+#[unstable(feature = "iovec", issue = "58452")]
+impl<'a> DerefMut for IoVecMut<'a> {
+    #[inline]
+    fn deref_mut(&mut self) -> &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 = "58452")]
+#[repr(transparent)]
+pub struct IoVec<'a>(sys::io::IoVec<'a>);
+
+#[unstable(feature = "iovec", issue = "58452")]
+impl<'a> fmt::Debug for IoVec<'a> {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Debug::fmt(self.0.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 = "58452")]
+    #[inline]
+    pub fn new(buf: &'a [u8]) -> IoVec<'a> {
+        IoVec(sys::io::IoVec::new(buf))
+    }
+}
+
+#[unstable(feature = "iovec", issue = "58452")]
+impl<'a> Deref for IoVec<'a> {
+    type Target = [u8];
+
+    #[inline]
+    fn deref(&self) -> &[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 +1101,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 = "58452")]
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> Result<usize> {
+        match bufs.iter().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 +1811,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.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..5ce955eb1e4 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)?;
+        }
+        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.len()).sum();
+        Ok(total_len)
+    }
+
     #[inline]
     fn flush(&mut self) -> io::Result<()> { Ok(()) }
 }
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index 7af569ab350..877a5a96c10 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -234,6 +234,7 @@
 #![feature(arbitrary_self_types)]
 #![feature(array_error_internals)]
 #![feature(asm)]
+#![feature(bind_by_move_pattern_guards)]
 #![feature(box_syntax)]
 #![feature(c_variadic)]
 #![feature(cfg_target_has_atomic)]
diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs
index 51bd76ae436..3aa29f83b29 100644
--- a/src/libstd/net/tcp.rs
+++ b/src/libstd/net/tcp.rs
@@ -1,7 +1,7 @@
 use io::prelude::*;
 
 use fmt;
-use io::{self, Initializer};
+use io::{self, Initializer, IoVec, IoVecMut};
 use net::{ToSocketAddrs, SocketAddr, Shutdown};
 use sys_common::net as net_imp;
 use sys_common::{AsInner, FromInner, IntoInner};
@@ -569,6 +569,10 @@ impl TcpStream {
 impl Read for TcpStream {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) }
 
+    fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        self.0.read_vectored(bufs)
+    }
+
     #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
@@ -577,12 +581,21 @@ impl Read for TcpStream {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Write for TcpStream {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) }
+
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        self.0.write_vectored(bufs)
+    }
+
     fn flush(&mut self) -> io::Result<()> { Ok(()) }
 }
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Read for &TcpStream {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) }
 
+    fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        self.0.read_vectored(bufs)
+    }
+
     #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
@@ -591,6 +604,11 @@ impl Read for &TcpStream {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Write for &TcpStream {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) }
+
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        self.0.write_vectored(bufs)
+    }
+
     fn flush(&mut self) -> io::Result<()> { Ok(()) }
 }
 
@@ -911,7 +929,7 @@ impl fmt::Debug for TcpListener {
 
 #[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten"))))]
 mod tests {
-    use io::ErrorKind;
+    use io::{ErrorKind, IoVec, IoVecMut};
     use io::prelude::*;
     use net::*;
     use net::test::{next_test_ip4, next_test_ip6};
@@ -1185,6 +1203,53 @@ mod tests {
     }
 
     #[test]
+    fn read_vectored() {
+        each_ip(&mut |addr| {
+            let srv = t!(TcpListener::bind(&addr));
+            let mut s1 = t!(TcpStream::connect(&addr));
+            let mut s2 = t!(srv.accept()).0;
+
+            let len = s1.write(&[10, 11, 12]).unwrap();
+            assert_eq!(len, 3);
+
+            let mut a = [];
+            let mut b = [0];
+            let mut c = [0; 3];
+            let len = t!(s2.read_vectored(
+                &mut [IoVecMut::new(&mut a), IoVecMut::new(&mut b), IoVecMut::new(&mut c)],
+            ));
+            assert!(len > 0);
+            assert_eq!(b, [10]);
+            // some implementations don't support readv, so we may only fill the first buffer
+            assert!(len == 1 || c == [11, 12, 0]);
+        })
+    }
+
+    #[test]
+    fn write_vectored() {
+        each_ip(&mut |addr| {
+            let srv = t!(TcpListener::bind(&addr));
+            let mut s1 = t!(TcpStream::connect(&addr));
+            let mut s2 = t!(srv.accept()).0;
+
+            let a = [];
+            let b = [10];
+            let c = [11, 12];
+            t!(s1.write_vectored(&[IoVec::new(&a), IoVec::new(&b), IoVec::new(&c)]));
+
+            let mut buf = [0; 4];
+            let len = t!(s2.read(&mut buf));
+            // some implementations don't support writev, so we may only write the first buffer
+            if len == 1 {
+                assert_eq!(buf, [10, 0, 0, 0]);
+            } else {
+                assert_eq!(len, 3);
+                assert_eq!(buf, [10, 11, 12, 0]);
+            }
+        })
+    }
+
+    #[test]
     fn double_bind() {
         each_ip(&mut |addr| {
             let listener1 = t!(TcpListener::bind(&addr));
diff --git a/src/libstd/sys/cloudabi/io.rs b/src/libstd/sys/cloudabi/io.rs
new file mode 100644
index 00000000000..8b02d3fd19d
--- /dev/null
+++ b/src/libstd/sys/cloudabi/io.rs
@@ -0,0 +1,32 @@
+pub struct IoVec<'a>(&'a [u8]);
+
+impl<'a> IoVec<'a> {
+    #[inline]
+    pub fn new(buf: &'a [u8]) -> IoVec<'a> {
+        IoVec(buf)
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        self.0
+    }
+}
+
+pub struct IoVecMut<'a>(&'a mut [u8]);
+
+impl<'a> IoVecMut<'a> {
+    #[inline]
+    pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> {
+        IoVecMut(buf)
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        self.0
+    }
+
+    #[inline]
+    pub fn as_mut_slice(&mut self) -> &mut [u8] {
+        self.0
+    }
+}
diff --git a/src/libstd/sys/cloudabi/mod.rs b/src/libstd/sys/cloudabi/mod.rs
index cd621b76945..d9bc21861c9 100644
--- a/src/libstd/sys/cloudabi/mod.rs
+++ b/src/libstd/sys/cloudabi/mod.rs
@@ -1,4 +1,3 @@
-use io;
 use libc;
 use mem;
 
@@ -10,6 +9,7 @@ pub mod backtrace;
 #[path = "../unix/cmath.rs"]
 pub mod cmath;
 pub mod condvar;
+pub mod io;
 #[path = "../unix/memchr.rs"]
 pub mod memchr;
 pub mod mutex;
@@ -32,24 +32,24 @@ pub use self::shims::*;
 #[allow(dead_code)]
 pub fn init() {}
 
-pub fn decode_error_kind(errno: i32) -> io::ErrorKind {
+pub fn decode_error_kind(errno: i32) -> ::io::ErrorKind {
     match errno {
-        x if x == abi::errno::ACCES as i32 => io::ErrorKind::PermissionDenied,
-        x if x == abi::errno::ADDRINUSE as i32 => io::ErrorKind::AddrInUse,
-        x if x == abi::errno::ADDRNOTAVAIL as i32 => io::ErrorKind::AddrNotAvailable,
-        x if x == abi::errno::AGAIN as i32 => io::ErrorKind::WouldBlock,
-        x if x == abi::errno::CONNABORTED as i32 => io::ErrorKind::ConnectionAborted,
-        x if x == abi::errno::CONNREFUSED as i32 => io::ErrorKind::ConnectionRefused,
-        x if x == abi::errno::CONNRESET as i32 => io::ErrorKind::ConnectionReset,
-        x if x == abi::errno::EXIST as i32 => io::ErrorKind::AlreadyExists,
-        x if x == abi::errno::INTR as i32 => io::ErrorKind::Interrupted,
-        x if x == abi::errno::INVAL as i32 => io::ErrorKind::InvalidInput,
-        x if x == abi::errno::NOENT as i32 => io::ErrorKind::NotFound,
-        x if x == abi::errno::NOTCONN as i32 => io::ErrorKind::NotConnected,
-        x if x == abi::errno::PERM as i32 => io::ErrorKind::PermissionDenied,
-        x if x == abi::errno::PIPE as i32 => io::ErrorKind::BrokenPipe,
-        x if x == abi::errno::TIMEDOUT as i32 => io::ErrorKind::TimedOut,
-        _ => io::ErrorKind::Other,
+        x if x == abi::errno::ACCES as i32 => ::io::ErrorKind::PermissionDenied,
+        x if x == abi::errno::ADDRINUSE as i32 => ::io::ErrorKind::AddrInUse,
+        x if x == abi::errno::ADDRNOTAVAIL as i32 => ::io::ErrorKind::AddrNotAvailable,
+        x if x == abi::errno::AGAIN as i32 => ::io::ErrorKind::WouldBlock,
+        x if x == abi::errno::CONNABORTED as i32 => ::io::ErrorKind::ConnectionAborted,
+        x if x == abi::errno::CONNREFUSED as i32 => ::io::ErrorKind::ConnectionRefused,
+        x if x == abi::errno::CONNRESET as i32 => ::io::ErrorKind::ConnectionReset,
+        x if x == abi::errno::EXIST as i32 => ::io::ErrorKind::AlreadyExists,
+        x if x == abi::errno::INTR as i32 => ::io::ErrorKind::Interrupted,
+        x if x == abi::errno::INVAL as i32 => ::io::ErrorKind::InvalidInput,
+        x if x == abi::errno::NOENT as i32 => ::io::ErrorKind::NotFound,
+        x if x == abi::errno::NOTCONN as i32 => ::io::ErrorKind::NotConnected,
+        x if x == abi::errno::PERM as i32 => ::io::ErrorKind::PermissionDenied,
+        x if x == abi::errno::PIPE as i32 => ::io::ErrorKind::BrokenPipe,
+        x if x == abi::errno::TIMEDOUT as i32 => ::io::ErrorKind::TimedOut,
+        _ => ::io::ErrorKind::Other,
     }
 }
 
diff --git a/src/libstd/sys/cloudabi/shims/net.rs b/src/libstd/sys/cloudabi/shims/net.rs
index b4caa899a75..869a0ef87a7 100644
--- a/src/libstd/sys/cloudabi/shims/net.rs
+++ b/src/libstd/sys/cloudabi/shims/net.rs
@@ -1,5 +1,5 @@
 use fmt;
-use io;
+use io::{self, IoVec, IoVecMut};
 use net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
 use time::Duration;
 use sys::{unsupported, Void};
@@ -42,10 +42,18 @@ impl TcpStream {
         match self.0 {}
     }
 
+    pub fn read_vectored(&self, _: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
     pub fn write(&self, _: &[u8]) -> io::Result<usize> {
         match self.0 {}
     }
 
+    pub fn write_vectored(&self, _: &[IoVec<'_>]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
         match self.0 {}
     }
diff --git a/src/libstd/sys/redox/io.rs b/src/libstd/sys/redox/io.rs
new file mode 100644
index 00000000000..8b02d3fd19d
--- /dev/null
+++ b/src/libstd/sys/redox/io.rs
@@ -0,0 +1,32 @@
+pub struct IoVec<'a>(&'a [u8]);
+
+impl<'a> IoVec<'a> {
+    #[inline]
+    pub fn new(buf: &'a [u8]) -> IoVec<'a> {
+        IoVec(buf)
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        self.0
+    }
+}
+
+pub struct IoVecMut<'a>(&'a mut [u8]);
+
+impl<'a> IoVecMut<'a> {
+    #[inline]
+    pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> {
+        IoVecMut(buf)
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        self.0
+    }
+
+    #[inline]
+    pub fn as_mut_slice(&mut self) -> &mut [u8] {
+        self.0
+    }
+}
diff --git a/src/libstd/sys/redox/mod.rs b/src/libstd/sys/redox/mod.rs
index c106db8ddfa..c3878349bb3 100644
--- a/src/libstd/sys/redox/mod.rs
+++ b/src/libstd/sys/redox/mod.rs
@@ -1,6 +1,6 @@
 #![allow(dead_code, missing_docs, nonstandard_style)]
 
-use io::{self, ErrorKind};
+use ::io::{ErrorKind};
 
 pub use libc::strlen;
 pub use self::rand::hashmap_random_keys;
@@ -17,6 +17,7 @@ pub mod ext;
 pub mod fast_thread_local;
 pub mod fd;
 pub mod fs;
+pub mod io;
 pub mod memchr;
 pub mod mutex;
 pub mod net;
@@ -63,8 +64,8 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
     }
 }
 
-pub fn cvt(result: Result<usize, syscall::Error>) -> io::Result<usize> {
-    result.map_err(|err| io::Error::from_raw_os_error(err.errno))
+pub fn cvt(result: Result<usize, syscall::Error>) -> ::io::Result<usize> {
+    result.map_err(|err| ::io::Error::from_raw_os_error(err.errno))
 }
 
 #[doc(hidden)]
@@ -82,9 +83,9 @@ macro_rules! impl_is_minus_one {
 
 impl_is_minus_one! { i8 i16 i32 i64 isize }
 
-pub fn cvt_libc<T: IsMinusOne>(t: T) -> io::Result<T> {
+pub fn cvt_libc<T: IsMinusOne>(t: T) -> ::io::Result<T> {
     if t.is_minus_one() {
-        Err(io::Error::last_os_error())
+        Err(::io::Error::last_os_error())
     } else {
         Ok(t)
     }
diff --git a/src/libstd/sys/redox/net/tcp.rs b/src/libstd/sys/redox/net/tcp.rs
index e0353b130bb..abb9f72c324 100644
--- a/src/libstd/sys/redox/net/tcp.rs
+++ b/src/libstd/sys/redox/net/tcp.rs
@@ -1,5 +1,5 @@
 use cmp;
-use io::{self, Error, ErrorKind, Result};
+use io::{self, Error, ErrorKind, Result, IoVec, IoVecMut};
 use mem;
 use net::{SocketAddr, Shutdown};
 use path::Path;
@@ -34,10 +34,24 @@ impl TcpStream {
         self.0.read(buf)
     }
 
+    pub fn read_vectored(&self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        match bufs.iter_mut().find(|b| !b.is_empty()) {
+            Some(buf) => self.read(buf),
+            None => Ok(0),
+        }
+    }
+
     pub fn write(&self, buf: &[u8]) -> Result<usize> {
         self.0.write(buf)
     }
 
+    pub fn write_vectored(&self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        match bufs.iter().find(|b| !b.is_empty()) {
+            Some(buf) => self.write(buf),
+            None => Ok(0),
+        }
+    }
+
     pub fn take_error(&self) -> Result<Option<Error>> {
         Ok(None)
     }
diff --git a/src/libstd/sys/sgx/io.rs b/src/libstd/sys/sgx/io.rs
new file mode 100644
index 00000000000..8b02d3fd19d
--- /dev/null
+++ b/src/libstd/sys/sgx/io.rs
@@ -0,0 +1,32 @@
+pub struct IoVec<'a>(&'a [u8]);
+
+impl<'a> IoVec<'a> {
+    #[inline]
+    pub fn new(buf: &'a [u8]) -> IoVec<'a> {
+        IoVec(buf)
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        self.0
+    }
+}
+
+pub struct IoVecMut<'a>(&'a mut [u8]);
+
+impl<'a> IoVecMut<'a> {
+    #[inline]
+    pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> {
+        IoVecMut(buf)
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        self.0
+    }
+
+    #[inline]
+    pub fn as_mut_slice(&mut self) -> &mut [u8] {
+        self.0
+    }
+}
diff --git a/src/libstd/sys/sgx/mod.rs b/src/libstd/sys/sgx/mod.rs
index 4225ecbb206..403dd61187f 100644
--- a/src/libstd/sys/sgx/mod.rs
+++ b/src/libstd/sys/sgx/mod.rs
@@ -3,7 +3,6 @@
 //! This module contains the facade (aka platform-specific) implementations of
 //! OS level functionality for Fortanix SGX.
 
-use io;
 use os::raw::c_char;
 use sync::atomic::{AtomicBool, Ordering};
 
@@ -20,6 +19,7 @@ pub mod env;
 pub mod ext;
 pub mod fd;
 pub mod fs;
+pub mod io;
 pub mod memchr;
 pub mod mutex;
 pub mod net;
@@ -41,12 +41,12 @@ pub fn init() {
 
 /// This function is used to implement functionality that simply doesn't exist.
 /// Programs relying on this functionality will need to deal with the error.
-pub fn unsupported<T>() -> io::Result<T> {
+pub fn unsupported<T>() -> ::io::Result<T> {
     Err(unsupported_err())
 }
 
-pub fn unsupported_err() -> io::Error {
-    io::Error::new(io::ErrorKind::Other,
+pub fn unsupported_err() -> ::io::Error {
+    ::io::Error::new(::io::ErrorKind::Other,
                    "operation not supported on SGX yet")
 }
 
@@ -55,58 +55,58 @@ pub fn unsupported_err() -> io::Error {
 /// returned, the program might very well be able to function normally. This is
 /// what happens when `SGX_INEFFECTIVE_ERROR` is set to `true`. If it is
 /// `false`, the behavior is the same as `unsupported`.
-pub fn sgx_ineffective<T>(v: T) -> io::Result<T> {
+pub fn sgx_ineffective<T>(v: T) -> ::io::Result<T> {
     static SGX_INEFFECTIVE_ERROR: AtomicBool = AtomicBool::new(false);
     if SGX_INEFFECTIVE_ERROR.load(Ordering::Relaxed) {
-        Err(io::Error::new(io::ErrorKind::Other,
+        Err(::io::Error::new(::io::ErrorKind::Other,
                        "operation can't be trusted to have any effect on SGX"))
     } else {
         Ok(v)
     }
 }
 
-pub fn decode_error_kind(code: i32) -> io::ErrorKind {
+pub fn decode_error_kind(code: i32) -> ::io::ErrorKind {
     use fortanix_sgx_abi::Error;
 
     // FIXME: not sure how to make sure all variants of Error are covered
     if code == Error::NotFound as _ {
-        io::ErrorKind::NotFound
+        ::io::ErrorKind::NotFound
     } else if code == Error::PermissionDenied as _ {
-        io::ErrorKind::PermissionDenied
+        ::io::ErrorKind::PermissionDenied
     } else if code == Error::ConnectionRefused as _ {
-        io::ErrorKind::ConnectionRefused
+        ::io::ErrorKind::ConnectionRefused
     } else if code == Error::ConnectionReset as _ {
-        io::ErrorKind::ConnectionReset
+        ::io::ErrorKind::ConnectionReset
     } else if code == Error::ConnectionAborted as _ {
-        io::ErrorKind::ConnectionAborted
+        ::io::ErrorKind::ConnectionAborted
     } else if code == Error::NotConnected as _ {
-        io::ErrorKind::NotConnected
+        ::io::ErrorKind::NotConnected
     } else if code == Error::AddrInUse as _ {
-        io::ErrorKind::AddrInUse
+        ::io::ErrorKind::AddrInUse
     } else if code == Error::AddrNotAvailable as _ {
-        io::ErrorKind::AddrNotAvailable
+        ::io::ErrorKind::AddrNotAvailable
     } else if code == Error::BrokenPipe as _ {
-        io::ErrorKind::BrokenPipe
+        ::io::ErrorKind::BrokenPipe
     } else if code == Error::AlreadyExists as _ {
-        io::ErrorKind::AlreadyExists
+        ::io::ErrorKind::AlreadyExists
     } else if code == Error::WouldBlock as _ {
-        io::ErrorKind::WouldBlock
+        ::io::ErrorKind::WouldBlock
     } else if code == Error::InvalidInput as _ {
-        io::ErrorKind::InvalidInput
+        ::io::ErrorKind::InvalidInput
     } else if code == Error::InvalidData as _ {
-        io::ErrorKind::InvalidData
+        ::io::ErrorKind::InvalidData
     } else if code == Error::TimedOut as _ {
-        io::ErrorKind::TimedOut
+        ::io::ErrorKind::TimedOut
     } else if code == Error::WriteZero as _ {
-        io::ErrorKind::WriteZero
+        ::io::ErrorKind::WriteZero
     } else if code == Error::Interrupted as _ {
-        io::ErrorKind::Interrupted
+        ::io::ErrorKind::Interrupted
     } else if code == Error::Other as _ {
-        io::ErrorKind::Other
+        ::io::ErrorKind::Other
     } else if code == Error::UnexpectedEof as _ {
-        io::ErrorKind::UnexpectedEof
+        ::io::ErrorKind::UnexpectedEof
     } else {
-        io::ErrorKind::Other
+        ::io::ErrorKind::Other
     }
 }
 
diff --git a/src/libstd/sys/sgx/net.rs b/src/libstd/sys/sgx/net.rs
index 6e86b06b286..c4c2de43ff7 100644
--- a/src/libstd/sys/sgx/net.rs
+++ b/src/libstd/sys/sgx/net.rs
@@ -1,5 +1,5 @@
 use fmt;
-use io;
+use io::{self, IoVec, IoVecMut};
 use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr, ToSocketAddrs};
 use time::Duration;
 use sys::{unsupported, Void, sgx_ineffective, AsInner, FromInner, IntoInner, TryIntoInner};
@@ -103,10 +103,26 @@ impl TcpStream {
         self.inner.inner.read(buf)
     }
 
+    pub fn read_vectored(&self, buf: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        let buf = match buf.get_mut(0) {
+            Some(buf) => buf,
+            None => return Ok(0),
+        };
+        self.read(buf)
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         self.inner.inner.write(buf)
     }
 
+    pub fn write_vectored(&self, buf: &[IoVec<'_>]) -> io::Result<usize> {
+        let buf = match buf.get(0) {
+            Some(buf) => buf,
+            None => return Ok(0),
+        };
+        self.write(buf)
+    }
+
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
         addr_to_sockaddr(&self.peer_addr)
     }
diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs
index acc064acfcd..4b60ea654c1 100644
--- a/src/libstd/sys/unix/ext/net.rs
+++ b/src/libstd/sys/unix/ext/net.rs
@@ -18,7 +18,7 @@ mod libc {
 use ascii;
 use ffi::OsStr;
 use fmt;
-use io::{self, Initializer};
+use io::{self, Initializer, IoVec, IoVecMut};
 use mem;
 use net::{self, Shutdown};
 use os::unix::ffi::OsStrExt;
@@ -551,6 +551,10 @@ impl io::Read for UnixStream {
         io::Read::read(&mut &*self, buf)
     }
 
+    fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        io::Read::read_vectored(&mut &*self, bufs)
+    }
+
     #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
@@ -563,6 +567,10 @@ impl<'a> io::Read for &'a UnixStream {
         self.0.read(buf)
     }
 
+    fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        self.0.read_vectored(bufs)
+    }
+
     #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
@@ -575,6 +583,10 @@ impl io::Write for UnixStream {
         io::Write::write(&mut &*self, buf)
     }
 
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        io::Write::write_vectored(&mut &*self, bufs)
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         io::Write::flush(&mut &*self)
     }
@@ -586,6 +598,10 @@ impl<'a> io::Write for &'a UnixStream {
         self.0.write(buf)
     }
 
+    fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        self.0.write_vectored(bufs)
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
@@ -1511,6 +1527,25 @@ mod test {
     }
 
     #[test]
+    fn vectored() {
+        let (mut s1, mut s2) = or_panic!(UnixStream::pair());
+
+        let len = or_panic!(s1.write_vectored(
+            &[IoVec::new(b"hello"), IoVec::new(b" "), IoVec::new(b"world!")],
+        ));
+        assert_eq!(len, 12);
+
+        let mut buf1 = [0; 6];
+        let mut buf2 = [0; 7];
+        let len = or_panic!(s2.read_vectored(
+            &mut [IoVecMut::new(&mut buf1), IoVecMut::new(&mut buf2)],
+        ));
+        assert_eq!(len, 12);
+        assert_eq!(&buf1, b"hello ");
+        assert_eq!(&buf2, b"world!\0");
+    }
+
+    #[test]
     fn pair() {
         let msg1 = b"hello";
         let msg2 = b"world!";
diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs
index 2cbd9536f4d..6946b7b5dfa 100644
--- a/src/libstd/sys/unix/fd.rs
+++ b/src/libstd/sys/unix/fd.rs
@@ -1,7 +1,7 @@
 #![unstable(reason = "not public", issue = "0", feature = "fd")]
 
 use cmp;
-use io::{self, Read, Initializer};
+use io::{self, Read, Initializer, IoVec, IoVecMut};
 use libc::{self, c_int, c_void, ssize_t};
 use mem;
 use sync::atomic::{AtomicBool, Ordering};
@@ -52,6 +52,15 @@ impl FileDesc {
         Ok(ret as usize)
     }
 
+    pub fn read_vectored(&self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        let ret = cvt(unsafe {
+            libc::readv(self.fd,
+                        bufs.as_ptr() as *const libc::iovec,
+                        cmp::min(bufs.len(), c_int::max_value() as usize) as c_int)
+        })?;
+        Ok(ret as usize)
+    }
+
     pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
         let mut me = self;
         (&mut me).read_to_end(buf)
@@ -105,6 +114,15 @@ impl FileDesc {
         Ok(ret as usize)
     }
 
+    pub fn write_vectored(&self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        let ret = cvt(unsafe {
+            libc::writev(self.fd,
+                         bufs.as_ptr() as *const libc::iovec,
+                         cmp::min(bufs.len(), c_int::max_value() as usize) as c_int)
+        })?;
+        Ok(ret as usize)
+    }
+
     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
         #[cfg(target_os = "android")]
         use super::android::cvt_pwrite64;
diff --git a/src/libstd/sys/unix/io.rs b/src/libstd/sys/unix/io.rs
new file mode 100644
index 00000000000..65e4c6e0577
--- /dev/null
+++ b/src/libstd/sys/unix/io.rs
@@ -0,0 +1,61 @@
+use marker::PhantomData;
+use libc::{iovec, c_void};
+use slice;
+
+#[repr(transparent)]
+pub struct IoVec<'a> {
+    vec: iovec,
+    _p: PhantomData<&'a [u8]>,
+}
+
+impl<'a> IoVec<'a> {
+    #[inline]
+    pub fn new(buf: &'a [u8]) -> IoVec<'a> {
+        IoVec {
+            vec: iovec {
+                iov_base: buf.as_ptr() as *mut u8 as *mut c_void,
+                iov_len: buf.len()
+            },
+            _p: PhantomData,
+        }
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        unsafe {
+            slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len)
+        }
+    }
+}
+
+pub struct IoVecMut<'a> {
+    vec: iovec,
+    _p: PhantomData<&'a mut [u8]>,
+}
+
+impl<'a> IoVecMut<'a> {
+    #[inline]
+    pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> {
+        IoVecMut {
+            vec: iovec {
+                iov_base: buf.as_mut_ptr() as *mut c_void,
+                iov_len: buf.len()
+            },
+            _p: PhantomData,
+        }
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        unsafe {
+            slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len)
+        }
+    }
+
+    #[inline]
+    pub fn as_mut_slice(&mut self) -> &mut [u8] {
+        unsafe {
+            slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len)
+        }
+    }
+}
diff --git a/src/libstd/sys/unix/l4re.rs b/src/libstd/sys/unix/l4re.rs
index 48037310c8d..4775e29fb57 100644
--- a/src/libstd/sys/unix/l4re.rs
+++ b/src/libstd/sys/unix/l4re.rs
@@ -5,7 +5,7 @@ macro_rules! unimpl {
 pub mod net {
     #![allow(warnings)]
     use fmt;
-    use io;
+    use io::{self, IoVec, IoVecMut};
     use libc;
     use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr};
     use sys_common::{AsInner, FromInner, IntoInner};
@@ -46,6 +46,10 @@ pub mod net {
             unimpl!();
         }
 
+        pub fn read_vectored(&self, _: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+            unimpl!();
+        }
+
         pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
             unimpl!();
         }
@@ -62,6 +66,10 @@ pub mod net {
             unimpl!();
         }
 
+        pub fn write_vectored(&self, _: &[IoVec<'_>]) -> io::Result<usize> {
+            unimpl!();
+        }
+
         pub fn set_timeout(&self, _: Option<Duration>, _: libc::c_int) -> io::Result<()> {
             unimpl!();
         }
@@ -144,10 +152,18 @@ pub mod net {
             unimpl!();
         }
 
+        pub fn read_vectored(&self, _: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+            unimpl!();
+        }
+
         pub fn write(&self, _: &[u8]) -> io::Result<usize> {
             unimpl!();
         }
 
+        pub fn write_vectored(&self, _: &[IoVec<'_>]) -> io::Result<usize> {
+            unimpl!();
+        }
+
         pub fn peer_addr(&self) -> io::Result<SocketAddr> {
             unimpl!();
         }
diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs
index b36c117fd09..0de1a223fbd 100644
--- a/src/libstd/sys/unix/mod.rs
+++ b/src/libstd/sys/unix/mod.rs
@@ -1,6 +1,6 @@
 #![allow(missing_docs, nonstandard_style)]
 
-use io::{self, ErrorKind};
+use io::ErrorKind;
 use libc;
 
 #[cfg(any(rustdoc, target_os = "linux"))] pub use os::linux as platform;
@@ -39,6 +39,7 @@ pub mod fast_thread_local;
 pub mod fd;
 pub mod fs;
 pub mod memchr;
+pub mod io;
 pub mod mutex;
 #[cfg(not(target_os = "l4re"))]
 pub mod net;
@@ -126,15 +127,15 @@ macro_rules! impl_is_minus_one {
 
 impl_is_minus_one! { i8 i16 i32 i64 isize }
 
-pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
+pub fn cvt<T: IsMinusOne>(t: T) -> ::io::Result<T> {
     if t.is_minus_one() {
-        Err(io::Error::last_os_error())
+        Err(::io::Error::last_os_error())
     } else {
         Ok(t)
     }
 }
 
-pub fn cvt_r<T, F>(mut f: F) -> io::Result<T>
+pub fn cvt_r<T, F>(mut f: F) -> ::io::Result<T>
     where T: IsMinusOne,
           F: FnMut() -> T
 {
diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs
index d780d71c376..521d9b42517 100644
--- a/src/libstd/sys/unix/net.rs
+++ b/src/libstd/sys/unix/net.rs
@@ -1,5 +1,5 @@
 use ffi::CStr;
-use io;
+use io::{self, IoVec, IoVecMut};
 use libc::{self, c_int, c_void, size_t, sockaddr, socklen_t, EAI_SYSTEM, MSG_PEEK};
 use mem;
 use net::{SocketAddr, Shutdown};
@@ -241,6 +241,10 @@ impl Socket {
         self.recv_with_flags(buf, MSG_PEEK)
     }
 
+    pub fn read_vectored(&self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        self.0.read_vectored(bufs)
+    }
+
     fn recv_from_with_flags(&self, buf: &mut [u8], flags: c_int)
                             -> io::Result<(usize, SocketAddr)> {
         let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
@@ -269,6 +273,10 @@ impl Socket {
         self.0.write(buf)
     }
 
+    pub fn write_vectored(&self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        self.0.write_vectored(bufs)
+    }
+
     pub fn set_timeout(&self, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> {
         let timeout = match dur {
             Some(dur) => {
diff --git a/src/libstd/sys/wasm/io.rs b/src/libstd/sys/wasm/io.rs
new file mode 100644
index 00000000000..8b02d3fd19d
--- /dev/null
+++ b/src/libstd/sys/wasm/io.rs
@@ -0,0 +1,32 @@
+pub struct IoVec<'a>(&'a [u8]);
+
+impl<'a> IoVec<'a> {
+    #[inline]
+    pub fn new(buf: &'a [u8]) -> IoVec<'a> {
+        IoVec(buf)
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        self.0
+    }
+}
+
+pub struct IoVecMut<'a>(&'a mut [u8]);
+
+impl<'a> IoVecMut<'a> {
+    #[inline]
+    pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> {
+        IoVecMut(buf)
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        self.0
+    }
+
+    #[inline]
+    pub fn as_mut_slice(&mut self) -> &mut [u8] {
+        self.0
+    }
+}
diff --git a/src/libstd/sys/wasm/mod.rs b/src/libstd/sys/wasm/mod.rs
index e21455ec6da..e71c6bcd7fe 100644
--- a/src/libstd/sys/wasm/mod.rs
+++ b/src/libstd/sys/wasm/mod.rs
@@ -14,7 +14,6 @@
 //! compiling for wasm. That way it's a compile time error for something that's
 //! guaranteed to be a runtime error!
 
-use io;
 use os::raw::c_char;
 use ptr;
 use sys::os_str::Buf;
@@ -29,6 +28,7 @@ pub mod backtrace;
 pub mod cmath;
 pub mod env;
 pub mod fs;
+pub mod io;
 pub mod memchr;
 pub mod net;
 pub mod os;
@@ -63,17 +63,17 @@ cfg_if! {
 pub fn init() {
 }
 
-pub fn unsupported<T>() -> io::Result<T> {
+pub fn unsupported<T>() -> ::io::Result<T> {
     Err(unsupported_err())
 }
 
-pub fn unsupported_err() -> io::Error {
-    io::Error::new(io::ErrorKind::Other,
+pub fn unsupported_err() -> ::io::Error {
+    ::io::Error::new(::io::ErrorKind::Other,
                    "operation not supported on wasm yet")
 }
 
-pub fn decode_error_kind(_code: i32) -> io::ErrorKind {
-    io::ErrorKind::Other
+pub fn decode_error_kind(_code: i32) -> ::io::ErrorKind {
+    ::io::ErrorKind::Other
 }
 
 // This enum is used as the storage for a bunch of types which can't actually
diff --git a/src/libstd/sys/wasm/net.rs b/src/libstd/sys/wasm/net.rs
index 81e4e8255bf..d9f5d538432 100644
--- a/src/libstd/sys/wasm/net.rs
+++ b/src/libstd/sys/wasm/net.rs
@@ -1,5 +1,5 @@
 use fmt;
-use io;
+use io::{self, IoVec, IoVecMut};
 use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr};
 use time::Duration;
 use sys::{unsupported, Void};
@@ -40,10 +40,18 @@ impl TcpStream {
         match self.0 {}
     }
 
+    pub fn read_vectored(&self, _: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
     pub fn write(&self, _: &[u8]) -> io::Result<usize> {
         match self.0 {}
     }
 
+    pub fn write_vectored(&self, _: &[IoVec<'_>]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
         match self.0 {}
     }
diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs
index 28fd4df386e..a78b599204b 100644
--- a/src/libstd/sys/windows/c.rs
+++ b/src/libstd/sys/windows/c.rs
@@ -57,6 +57,9 @@ pub type LPWSAPROTOCOL_INFO = *mut WSAPROTOCOL_INFO;
 pub type LPSTR = *mut CHAR;
 pub type LPWSTR = *mut WCHAR;
 pub type LPFILETIME = *mut FILETIME;
+pub type LPWSABUF = *mut WSABUF;
+pub type LPWSAOVERLAPPED = *mut c_void;
+pub type LPWSAOVERLAPPED_COMPLETION_ROUTINE = *mut c_void;
 
 pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE;
 pub type PLARGE_INTEGER = *mut c_longlong;
@@ -325,6 +328,12 @@ pub struct WSADATA {
 }
 
 #[repr(C)]
+pub struct WSABUF {
+    pub len: ULONG,
+    pub buf: *mut CHAR,
+}
+
+#[repr(C)]
 pub struct WSAPROTOCOL_INFO {
     pub dwServiceFlags1: DWORD,
     pub dwServiceFlags2: DWORD,
@@ -988,6 +997,22 @@ extern "system" {
                                dwProcessId: DWORD,
                                lpProtocolInfo: LPWSAPROTOCOL_INFO)
                                -> c_int;
+    pub fn WSASend(s: SOCKET,
+                   lpBuffers: LPWSABUF,
+                   dwBufferCount: DWORD,
+                   lpNumberOfBytesSent: LPDWORD,
+                   dwFlags: DWORD,
+                   lpOverlapped: LPWSAOVERLAPPED,
+                   lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE)
+                   -> c_int;
+    pub fn WSARecv(s: SOCKET,
+                   lpBuffers: LPWSABUF,
+                   dwBufferCount: DWORD,
+                   lpNumberOfBytesRecvd: LPDWORD,
+                   lpFlags: LPDWORD,
+                   lpOverlapped: LPWSAOVERLAPPED,
+                   lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE)
+                   -> c_int;
     pub fn GetCurrentProcessId() -> DWORD;
     pub fn WSASocketW(af: c_int,
                       kind: c_int,
diff --git a/src/libstd/sys/windows/io.rs b/src/libstd/sys/windows/io.rs
new file mode 100644
index 00000000000..662e3047923
--- /dev/null
+++ b/src/libstd/sys/windows/io.rs
@@ -0,0 +1,63 @@
+use marker::PhantomData;
+use slice;
+use sys::c;
+
+#[repr(transparent)]
+pub struct IoVec<'a> {
+    vec: c::WSABUF,
+    _p: PhantomData<&'a [u8]>,
+}
+
+impl<'a> IoVec<'a> {
+    #[inline]
+    pub fn new(buf: &'a [u8]) -> IoVec<'a> {
+        assert!(buf.len() <= c::ULONG::max_value() as usize);
+        IoVec {
+            vec: c::WSABUF {
+                len: buf.len() as c::ULONG,
+                buf: buf.as_ptr() as *mut u8 as *mut c::CHAR,
+            },
+            _p: PhantomData,
+        }
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        unsafe {
+            slice::from_raw_parts(self.vec.buf as *mut u8, self.vec.len as usize)
+        }
+    }
+}
+
+pub struct IoVecMut<'a> {
+    vec: c::WSABUF,
+    _p: PhantomData<&'a mut [u8]>,
+}
+
+impl<'a> IoVecMut<'a> {
+    #[inline]
+    pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> {
+        assert!(buf.len() <= c::ULONG::max_value() as usize);
+        IoVecMut {
+            vec: c::WSABUF {
+                len: buf.len() as c::ULONG,
+                buf: buf.as_mut_ptr() as *mut c::CHAR,
+            },
+            _p: PhantomData,
+        }
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        unsafe {
+            slice::from_raw_parts(self.vec.buf as *mut u8, self.vec.len as usize)
+        }
+    }
+
+    #[inline]
+    pub fn as_mut_slice(&mut self) -> &mut [u8] {
+        unsafe {
+            slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.len as usize)
+        }
+    }
+}
diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs
index e97e436efbf..56c76a169fe 100644
--- a/src/libstd/sys/windows/mod.rs
+++ b/src/libstd/sys/windows/mod.rs
@@ -2,7 +2,7 @@
 
 use ptr;
 use ffi::{OsStr, OsString};
-use io::{self, ErrorKind};
+use io::ErrorKind;
 use os::windows::ffi::{OsStrExt, OsStringExt};
 use path::PathBuf;
 use time::Duration;
@@ -26,6 +26,7 @@ pub mod ext;
 pub mod fast_thread_local;
 pub mod fs;
 pub mod handle;
+pub mod io;
 pub mod memchr;
 pub mod mutex;
 pub mod net;
@@ -75,12 +76,12 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
     }
 }
 
-pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> io::Result<Vec<u16>> {
-    fn inner(s: &OsStr) -> io::Result<Vec<u16>> {
+pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> ::io::Result<Vec<u16>> {
+    fn inner(s: &OsStr) -> ::io::Result<Vec<u16>> {
         let mut maybe_result: Vec<u16> = s.encode_wide().collect();
         if maybe_result.iter().any(|&u| u == 0) {
-            return Err(io::Error::new(io::ErrorKind::InvalidInput,
-                                      "strings passed to WinAPI cannot contain NULs"));
+            return Err(::io::Error::new(::io::ErrorKind::InvalidInput,
+                                        "strings passed to WinAPI cannot contain NULs"));
         }
         maybe_result.push(0);
         Ok(maybe_result)
@@ -102,7 +103,7 @@ pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> io::Result<Vec<u16>> {
 // Once the syscall has completed (errors bail out early) the second closure is
 // yielded the data which has been read from the syscall. The return value
 // from this closure is then the return value of the function.
-fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> io::Result<T>
+fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> ::io::Result<T>
     where F1: FnMut(*mut u16, c::DWORD) -> c::DWORD,
           F2: FnOnce(&[u16]) -> T
 {
@@ -134,7 +135,7 @@ fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> io::Result<T>
             c::SetLastError(0);
             let k = match f1(buf.as_mut_ptr(), n as c::DWORD) {
                 0 if c::GetLastError() == 0 => 0,
-                0 => return Err(io::Error::last_os_error()),
+                0 => return Err(::io::Error::last_os_error()),
                 n => n,
             } as usize;
             if k == n && c::GetLastError() == c::ERROR_INSUFFICIENT_BUFFER {
@@ -157,7 +158,7 @@ fn wide_char_to_multi_byte(code_page: u32,
                            flags: u32,
                            s: &[u16],
                            no_default_char: bool)
-                           -> io::Result<Vec<i8>> {
+                           -> ::io::Result<Vec<i8>> {
     unsafe {
         let mut size = c::WideCharToMultiByte(code_page,
                                               flags,
@@ -168,7 +169,7 @@ fn wide_char_to_multi_byte(code_page: u32,
                                               ptr::null(),
                                               ptr::null_mut());
         if size == 0 {
-            return Err(io::Error::last_os_error());
+            return Err(::io::Error::last_os_error());
         }
 
         let mut buf = Vec::with_capacity(size as usize);
@@ -185,10 +186,10 @@ fn wide_char_to_multi_byte(code_page: u32,
                                       if no_default_char { &mut used_default_char }
                                       else { ptr::null_mut() });
         if size == 0 {
-            return Err(io::Error::last_os_error());
+            return Err(::io::Error::last_os_error());
         }
         if no_default_char && used_default_char == c::TRUE {
-            return Err(io::Error::new(io::ErrorKind::InvalidData,
+            return Err(::io::Error::new(::io::ErrorKind::InvalidData,
                                       "string cannot be converted to requested code page"));
         }
 
@@ -220,9 +221,9 @@ macro_rules! impl_is_zero {
 
 impl_is_zero! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize }
 
-pub fn cvt<I: IsZero>(i: I) -> io::Result<I> {
+pub fn cvt<I: IsZero>(i: I) -> ::io::Result<I> {
     if i.is_zero() {
-        Err(io::Error::last_os_error())
+        Err(::io::Error::last_os_error())
     } else {
         Ok(i)
     }
diff --git a/src/libstd/sys/windows/net.rs b/src/libstd/sys/windows/net.rs
index acda81dcde5..76be26a9d1a 100644
--- a/src/libstd/sys/windows/net.rs
+++ b/src/libstd/sys/windows/net.rs
@@ -1,7 +1,7 @@
 #![unstable(issue = "0", feature = "windows_net")]
 
 use cmp;
-use io::{self, Read};
+use io::{self, Read, IoVec, IoVecMut};
 use libc::{c_int, c_void, c_ulong, c_long};
 use mem;
 use net::{SocketAddr, Shutdown};
@@ -207,6 +207,30 @@ impl Socket {
         self.recv_with_flags(buf, 0)
     }
 
+    pub fn read_vectored(&self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        // On unix when a socket is shut down all further reads return 0, so we
+        // do the same on windows to map a shut down socket to returning EOF.
+        let len = cmp::min(bufs.len(), c::DWORD::max_value() as usize) as c::DWORD;
+        let mut nread = 0;
+        let mut flags = 0;
+        unsafe {
+            let ret = c::WSARecv(
+                self.0,
+                bufs.as_mut_ptr() as *mut c::WSABUF,
+                len,
+                &mut nread,
+                &mut flags,
+                ptr::null_mut(),
+                ptr::null_mut(),
+            );
+            match ret {
+                0 => Ok(nread as usize),
+                _ if c::WSAGetLastError() == c::WSAESHUTDOWN => Ok(0),
+                _ => Err(last_error()),
+            }
+        }
+    }
+
     pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
         self.recv_with_flags(buf, c::MSG_PEEK)
     }
@@ -243,6 +267,23 @@ impl Socket {
         self.recv_from_with_flags(buf, c::MSG_PEEK)
     }
 
+    pub fn write_vectored(&self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        let len = cmp::min(bufs.len(), c::DWORD::max_value() as usize) as c::DWORD;
+        let mut nwritten = 0;
+        unsafe {
+            cvt(c::WSASend(
+                self.0,
+                bufs.as_ptr() as *const c::WSABUF as *mut c::WSABUF,
+                len,
+                &mut nwritten,
+                0,
+                ptr::null_mut(),
+                ptr::null_mut(),
+            ))?;
+        }
+        Ok(nwritten as usize)
+    }
+
     pub fn set_timeout(&self, dur: Option<Duration>,
                        kind: c_int) -> io::Result<()> {
         let timeout = match dur {
diff --git a/src/libstd/sys_common/net.rs b/src/libstd/sys_common/net.rs
index f75df3ea695..0d60593ce1f 100644
--- a/src/libstd/sys_common/net.rs
+++ b/src/libstd/sys_common/net.rs
@@ -1,7 +1,7 @@
 use cmp;
 use ffi::CString;
 use fmt;
-use io::{self, Error, ErrorKind};
+use io::{self, Error, ErrorKind, IoVec, IoVecMut};
 use libc::{c_int, c_void};
 use mem;
 use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr};
@@ -255,6 +255,10 @@ impl TcpStream {
         self.inner.read(buf)
     }
 
+    pub fn read_vectored(&self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        self.inner.read_vectored(bufs)
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         let len = cmp::min(buf.len(), <wrlen_t>::max_value() as usize) as wrlen_t;
         let ret = cvt(unsafe {
@@ -266,6 +270,10 @@ impl TcpStream {
         Ok(ret as usize)
     }
 
+    pub fn write_vectored(&self, bufs: &[IoVec<'_>]) -> io::Result<usize> {
+        self.inner.write_vectored(bufs)
+    }
+
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
         sockname(|buf, len| unsafe {
             c::getpeername(*self.inner.as_inner(), buf, len)
diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs
index af704ce260d..9a6c97dbca0 100644
--- a/src/tools/linkchecker/main.rs
+++ b/src/tools/linkchecker/main.rs
@@ -134,7 +134,9 @@ fn check(cache: &mut Cache,
        file.ends_with("log/index.html") ||
        file.ends_with("ty/struct.Slice.html") ||
        file.ends_with("ty/enum.Attributes.html") ||
-       file.ends_with("ty/struct.SymbolName.html") {
+       file.ends_with("ty/struct.SymbolName.html") ||
+       file.ends_with("io/struct.IoVec.html") ||
+       file.ends_with("io/struct.IoVecMut.html") {
         return None;
     }
     // FIXME(#32553)