From bb9bf7ee3e89435a16f60afc715e52e6a1d24b01 Mon Sep 17 00:00:00 2001
From: Carl Lerche <me@carllerche.com>
Date: Tue, 28 Feb 2017 11:15:35 -0800
Subject: [PATCH] Add vectored support to Buf and BufMut

---
 Cargo.toml            |  1 +
 src/buf/buf.rs        | 37 +++++++++++++++++++++++++++++++++++++
 src/buf/buf_mut.rs    | 38 ++++++++++++++++++++++++++++++++++++++
 src/bytes.rs          |  3 ++-
 src/lib.rs            |  1 +
 tests/test_buf.rs     | 10 ++++++++++
 tests/test_buf_mut.rs | 14 ++++++++++++++
 7 files changed, 103 insertions(+), 1 deletion(-)

diff --git a/Cargo.toml b/Cargo.toml
index b873542..217319a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,6 +21,7 @@ categories = ["network-programming", "data-structures"]
 
 [dependencies]
 byteorder = "1.0.0"
+iovec = { git = "https://github.com/carllerche/iovec" }
 
 [dev-dependencies]
 tokio-core = "0.1.0"
diff --git a/src/buf/buf.rs b/src/buf/buf.rs
index 3f3dbb6..0732aff 100644
--- a/src/buf/buf.rs
+++ b/src/buf/buf.rs
@@ -1,5 +1,6 @@
 use super::{Take, Reader, Iter, FromBuf};
 use byteorder::ByteOrder;
+use iovec::IoVec;
 
 use std::{cmp, io, ptr};
 
@@ -72,6 +73,34 @@ pub trait Buf {
     /// ```
     fn bytes(&self) -> &[u8];
 
+    /// Fills `dst` with potentially multiple slices starting at `self`'s
+    /// current position.
+    ///
+    /// If the `Buf` is backed by disjoint slices of bytes, `bytes_vec` enables
+    /// fetching more than one slice at once. `dst` is a slice of `IoVec`
+    /// references, enabling the slice to be directly used with [`writev`]
+    /// without any further conversion. The sum of the lengths of all the
+    /// buffers in `dst` will be less than or equal to `Buf::remaining()`.
+    ///
+    /// The entries in `dst` will be overwritten, but the data **contained** by
+    /// the slices **will not** be modified. If `bytes_vec` does not fill every
+    /// entry in `dst`, then `dst` is guaranteed to contain all remaining slices
+    /// in `self.
+    ///
+    /// This is a lower level function. Most operations are done with other
+    /// functions.
+    ///
+    /// [`writev`]: http://man7.org/linux/man-pages/man2/readv.2.html
+    fn bytes_vec<'a>(&'a self, dst: &mut [&'a IoVec]) -> usize {
+        if dst.is_empty() {
+            return 0;
+        }
+
+        dst[0] = self.bytes().into();
+
+        1
+    }
+
     /// Advance the internal cursor of the Buf
     ///
     /// The next call to `bytes` will return a slice starting `cnt` bytes
@@ -576,6 +605,10 @@ impl<'a, T: Buf + ?Sized> Buf for &'a mut T {
         (**self).bytes()
     }
 
+    fn bytes_vec<'b>(&'b self, dst: &mut [&'b IoVec]) -> usize {
+        (**self).bytes_vec(dst)
+    }
+
     fn advance(&mut self, cnt: usize) {
         (**self).advance(cnt)
     }
@@ -590,6 +623,10 @@ impl<T: Buf + ?Sized> Buf for Box<T> {
         (**self).bytes()
     }
 
+    fn bytes_vec<'b>(&'b self, dst: &mut [&'b IoVec]) -> usize {
+        (**self).bytes_vec(dst)
+    }
+
     fn advance(&mut self, cnt: usize) {
         (**self).advance(cnt)
     }
diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs
index 2e3c141..7ecc8b2 100644
--- a/src/buf/buf_mut.rs
+++ b/src/buf/buf_mut.rs
@@ -1,5 +1,6 @@
 use super::{Source, Writer};
 use byteorder::ByteOrder;
+use iovec::IoVec;
 
 use std::{cmp, io, ptr, usize};
 
@@ -137,6 +138,35 @@ pub trait BufMut {
     /// ```
     unsafe fn bytes_mut(&mut self) -> &mut [u8];
 
+    /// Fills `dst` with potentially multiple mutable slices starting at `self`'s
+    /// current position.
+    ///
+    /// If the `BufMut` is backed by disjoint slices of bytes, `bytes_vec_mut`
+    /// enables fetching more than one slice at once. `dst` is a slice of
+    /// mutable `IoVec` references, enabling the slice to be directly used with
+    /// [`readv`] without any further conversion. The sum of the lengths of all
+    /// the buffers in `dst` will be less than or equal to
+    /// `Buf::remaining_mut()`.
+    ///
+    /// The entries in `dst` will be overwritten, but the data **contained** by
+    /// the slices **will not** be modified. If `bytes_vec_mut` does not fill every
+    /// entry in `dst`, then `dst` is guaranteed to contain all remaining slices
+    /// in `self.
+    ///
+    /// This is a lower level function. Most operations are done with other
+    /// functions.
+    ///
+    /// [`readv`]: http://man7.org/linux/man-pages/man2/readv.2.html
+    unsafe fn bytes_vec_mut<'a>(&'a mut self, dst: &mut [&'a mut IoVec]) -> usize {
+        if dst.is_empty() {
+            return 0;
+        }
+
+        dst[0] = self.bytes_mut().into();
+
+        1
+    }
+
     /// Transfer bytes into `self` from `src` and advance the cursor by the
     /// number of bytes written.
     ///
@@ -513,6 +543,10 @@ impl<'a, T: BufMut + ?Sized> BufMut for &'a mut T {
         (**self).bytes_mut()
     }
 
+    unsafe fn bytes_vec_mut<'b>(&'b mut self, dst: &mut [&'b mut IoVec]) -> usize {
+        (**self).bytes_vec_mut(dst)
+    }
+
     unsafe fn advance_mut(&mut self, cnt: usize) {
         (**self).advance_mut(cnt)
     }
@@ -527,6 +561,10 @@ impl<T: BufMut + ?Sized> BufMut for Box<T> {
         (**self).bytes_mut()
     }
 
+    unsafe fn bytes_vec_mut<'b>(&'b mut self, dst: &mut [&'b mut IoVec]) -> usize {
+        (**self).bytes_vec_mut(dst)
+    }
+
     unsafe fn advance_mut(&mut self, cnt: usize) {
         (**self).advance_mut(cnt)
     }
diff --git a/src/bytes.rs b/src/bytes.rs
index 1ebcff1..fb70101 100644
--- a/src/bytes.rs
+++ b/src/bytes.rs
@@ -1229,7 +1229,8 @@ impl<'a> From<&'a [u8]> for BytesMut {
             }
         } else {
             let mut buf = BytesMut::with_capacity(src.len());
-            buf.put(src.as_ref());
+            let src: &[u8] = src.as_ref();
+            buf.put(src);
             buf
         }
     }
diff --git a/src/lib.rs b/src/lib.rs
index 521f80c..d62db78 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -72,6 +72,7 @@
 #![doc(html_root_url = "https://docs.rs/bytes/0.4")]
 
 extern crate byteorder;
+extern crate iovec;
 
 pub mod buf;
 pub use buf::{
diff --git a/tests/test_buf.rs b/tests/test_buf.rs
index 27229e8..5c989c3 100644
--- a/tests/test_buf.rs
+++ b/tests/test_buf.rs
@@ -1,7 +1,9 @@
 extern crate bytes;
 extern crate byteorder;
+extern crate iovec;
 
 use bytes::Buf;
+use iovec::IoVec;
 use std::io::Cursor;
 
 #[test]
@@ -46,3 +48,11 @@ fn test_get_u16_buffer_underflow() {
     let mut buf = Cursor::new(b"\x21");
     buf.get_u16::<byteorder::BigEndian>();
 }
+
+#[test]
+fn test_bufs_vec() {
+    let buf = Cursor::new(b"hello world");
+    let mut dst: [&IoVec; 2] = Default::default();
+
+    assert_eq!(1, buf.bytes_vec(&mut dst[..]));
+}
diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs
index d15a920..fe2e9c8 100644
--- a/tests/test_buf_mut.rs
+++ b/tests/test_buf_mut.rs
@@ -1,7 +1,9 @@
 extern crate bytes;
 extern crate byteorder;
+extern crate iovec;
 
 use bytes::{BufMut, BytesMut};
+use iovec::IoVec;
 use std::usize;
 use std::fmt::Write;
 
@@ -56,3 +58,15 @@ fn test_clone() {
     buf.write_str(" of our emergecy broadcast system").unwrap();
     assert!(buf != buf2);
 }
+
+#[test]
+fn test_bufs_vec_mut() {
+    use std::mem;
+
+    let mut buf = BytesMut::from(&b"hello world"[..]);
+
+    unsafe {
+        let mut dst: [&mut IoVec; 2] = mem::zeroed();
+        assert_eq!(1, buf.bytes_vec_mut(&mut dst[..]));
+    }
+}
-- 
GitLab