From 07db74b009bb44190ae6c0bb001075a6d9261649 Mon Sep 17 00:00:00 2001
From: Stepan Koltsov <stepan.koltsov@gmail.com>
Date: Mon, 15 May 2017 21:27:45 +0300
Subject: [PATCH] Bytes::extend_from_slice (#120)

`extend_with_slice` is super-convenient operation on `Bytes`.

While `put_u8` would be expensive on `Bytes`, `extend_from_slice`
is OK, because it is batch, and it checks for kind only once.

Patch also adds `impl Extend for Bytes`.

cc #116
---
 src/bytes.rs        | 74 +++++++++++++++++++++++++++++++++++++++++++++
 tests/test_bytes.rs | 29 +++++++++++++++++-
 2 files changed, 102 insertions(+), 1 deletion(-)

diff --git a/src/bytes.rs b/src/bytes.rs
index e71d24b..297659d 100644
--- a/src/bytes.rs
+++ b/src/bytes.rs
@@ -648,6 +648,48 @@ impl Bytes {
             Err(self)
         }
     }
+
+    /// Append given bytes to this object.
+    ///
+    /// If this `Bytes` object has not enough capacity, it is resized first.
+    /// It `Bytes` is shared (`refcount > 1`), it is copied first.
+    ///
+    /// This operation can be less effective than similar operation on `BytesMut`,
+    /// especially on small additions.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use bytes::Bytes;
+    ///
+    /// let mut buf = Bytes::from("aabb");
+    /// buf.extend_from_slice(b"ccdd");
+    /// buf.extend_from_slice(b"eeff");
+    ///
+    /// assert_eq!(b"aabbccddeeff", &buf[..]);
+    /// ```
+    pub fn extend_from_slice(&mut self, extend: &[u8]) {
+        if extend.is_empty() {
+            return;
+        }
+
+        let new_cap = self.len().checked_add(extend.len()).expect("capacity overflow");
+
+        let result = match mem::replace(self, Bytes::new()).try_mut() {
+            Ok(mut bytes_mut) => {
+                bytes_mut.extend_from_slice(extend);
+                bytes_mut
+            },
+            Err(bytes) => {
+                let mut bytes_mut = BytesMut::with_capacity(new_cap);
+                bytes_mut.put_slice(&bytes);
+                bytes_mut.put_slice(extend);
+                bytes_mut
+            }
+        };
+
+        mem::replace(self, result.freeze());
+    }
 }
 
 impl IntoBuf for Bytes {
@@ -786,6 +828,38 @@ impl<'a> IntoIterator for &'a Bytes {
     }
 }
 
+impl Extend<u8> for Bytes {
+    fn extend<T>(&mut self, iter: T) where T: IntoIterator<Item = u8> {
+        let iter = iter.into_iter();
+
+        let (lower, upper) = iter.size_hint();
+
+        // Avoid possible conversion into mut if there's nothing to add
+        if let Some(0) = upper {
+            return;
+        }
+
+        let mut bytes_mut = match mem::replace(self, Bytes::new()).try_mut() {
+            Ok(bytes_mut) => bytes_mut,
+            Err(bytes) => {
+                let mut bytes_mut = BytesMut::with_capacity(bytes.len() + lower);
+                bytes_mut.put_slice(&bytes);
+                bytes_mut
+            }
+        };
+
+        bytes_mut.extend(iter);
+
+        mem::replace(self, bytes_mut.freeze());
+    }
+}
+
+impl<'a> Extend<&'a u8> for Bytes {
+    fn extend<T>(&mut self, iter: T) where T: IntoIterator<Item = &'a u8> {
+        self.extend(iter.into_iter().map(|b| *b))
+    }
+}
+
 /*
  *
  * ===== BytesMut =====
diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs
index 1a3435f..5c30475 100644
--- a/tests/test_bytes.rs
+++ b/tests/test_bytes.rs
@@ -357,12 +357,39 @@ fn inline_storage() {
 }
 
 #[test]
-fn extend() {
+fn extend_mut() {
     let mut bytes = BytesMut::with_capacity(0);
     bytes.extend(LONG);
     assert_eq!(*bytes, LONG[..]);
 }
 
+#[test]
+fn extend_shr() {
+    let mut bytes = Bytes::new();
+    bytes.extend(LONG);
+    assert_eq!(*bytes, LONG[..]);
+}
+
+#[test]
+fn extend_from_slice_mut() {
+    for &i in &[3, 34] {
+        let mut bytes = BytesMut::new();
+        bytes.extend_from_slice(&LONG[..i]);
+        bytes.extend_from_slice(&LONG[i..]);
+        assert_eq!(LONG[..], *bytes);
+    }
+}
+
+#[test]
+fn extend_from_slice_shr() {
+    for &i in &[3, 34] {
+        let mut bytes = Bytes::new();
+        bytes.extend_from_slice(&LONG[..i]);
+        bytes.extend_from_slice(&LONG[i..]);
+        assert_eq!(LONG[..], *bytes);
+    }
+}
+
 #[test]
 fn from_static() {
     let mut a = Bytes::from_static(b"ab");
-- 
GitLab