From d650404bb89da3c12529dc3ffbb7d4ffb7147506 Mon Sep 17 00:00:00 2001
From: Carl Lerche <me@carllerche.com>
Date: Sat, 23 Jul 2016 09:49:59 -0700
Subject: [PATCH] Add an AppendBuf

---
 src/alloc/mod.rs    |  13 ++----
 src/buf/append.rs   | 111 ++++++++++++++++++++++++++++++++++++++++++++
 src/buf/byte.rs     |  14 ++++--
 src/buf/mod.rs      |   2 +
 src/buf/ring.rs     |   2 +-
 src/str/seq.rs      |   2 +-
 test/test.rs        |   1 +
 test/test_append.rs |  30 ++++++++++++
 8 files changed, 159 insertions(+), 16 deletions(-)
 create mode 100644 src/buf/append.rs
 create mode 100644 test/test_append.rs

diff --git a/src/alloc/mod.rs b/src/alloc/mod.rs
index 891412f..820ede7 100644
--- a/src/alloc/mod.rs
+++ b/src/alloc/mod.rs
@@ -49,20 +49,15 @@ impl MemRef {
     }
 
     #[inline]
-    pub fn bytes(&self) -> &[u8] {
+    pub unsafe fn bytes(&self) -> &[u8] {
         use std::slice;
-
-        unsafe {
-            slice::from_raw_parts(self.bytes_ptr(), self.len())
-        }
+        slice::from_raw_parts(self.bytes_ptr(), self.len())
     }
 
     #[inline]
-    pub fn bytes_mut(&mut self) -> &mut [u8] {
+    pub unsafe fn bytes_mut(&mut self) -> &mut [u8] {
         use std::slice;
-        unsafe {
-            slice::from_raw_parts_mut(self.bytes_ptr(), self.len())
-        }
+        slice::from_raw_parts_mut(self.bytes_ptr(), self.len())
     }
 
     #[inline]
diff --git a/src/buf/append.rs b/src/buf/append.rs
new file mode 100644
index 0000000..6528eda
--- /dev/null
+++ b/src/buf/append.rs
@@ -0,0 +1,111 @@
+use alloc;
+use buf::{MutBuf};
+use str::{ByteStr, Bytes, SeqByteStr, SmallByteStr};
+use std::cell::Cell;
+use std::cmp;
+
+/// A `Buf` backed by a contiguous region of memory.
+///
+/// This buffer can only be written to once. Byte strings (immutable views) can
+/// be created at any time, not just when the writing is complete.
+pub struct AppendBuf {
+    mem: alloc::MemRef,
+    rd: Cell<u32>, // Read cursor
+    wr: u32, // Write cursor
+    cap: u32,
+}
+
+impl AppendBuf {
+    pub fn with_capacity(mut capacity: u32) -> AppendBuf {
+        // Handle 0 capacity case
+        if capacity == 0 {
+            return AppendBuf::none();
+        }
+
+        // Round the capacity to the closest power of 2
+        capacity = capacity.next_power_of_two();
+
+        // Allocate the memory
+        let mem = alloc::heap(capacity as usize);
+
+        // If the allocation failed, return a blank buf
+        if mem.is_none() {
+            return AppendBuf::none();
+        }
+
+        AppendBuf {
+            mem: mem,
+            rd: Cell::new(0),
+            wr: 0,
+            cap: capacity,
+        }
+    }
+
+    /// Returns an AppendBuf with no capacity
+    pub fn none() -> AppendBuf {
+        AppendBuf {
+            mem: alloc::MemRef::none(),
+            rd: Cell::new(0),
+            wr: 0,
+            cap: 0,
+        }
+    }
+
+    pub unsafe fn from_mem_ref(mem: alloc::MemRef, cap: u32, pos: u32) -> AppendBuf {
+        AppendBuf {
+            mem: mem,
+            rd: Cell::new(pos),
+            wr: pos,
+            cap: cap,
+        }
+    }
+
+    pub fn bytes(&self) -> &[u8] {
+        let rd = self.rd.get() as usize;
+        let wr = self.wr as usize;
+        unsafe { &self.mem.bytes()[rd..wr] }
+    }
+
+    pub fn shift(&self, n: usize) -> Bytes {
+        let ret = self.slice(0, n);
+        self.rd.set(self.rd.get() + ret.len() as u32);
+        ret
+    }
+
+    pub fn slice(&self, begin: usize, end: usize) -> Bytes {
+        if end <= begin {
+            return Bytes::of(SmallByteStr::zero());
+        }
+
+        if let Some(bytes) = SmallByteStr::from_slice(&self.bytes()[begin..end]) {
+            return Bytes::of(bytes);
+        }
+
+        let begin = cmp::min(self.wr, begin as u32 + self.rd.get());
+        let end = cmp::min(self.wr, end as u32 + self.rd.get());
+
+        let bytes = unsafe { SeqByteStr::from_mem_ref(self.mem.clone(), begin, end - begin) };
+
+        Bytes::of(bytes)
+    }
+}
+
+impl MutBuf for AppendBuf {
+    fn remaining(&self) -> usize {
+        (self.cap - self.wr) as usize
+    }
+
+    unsafe fn advance(&mut self, cnt: usize) {
+        self.wr += cnt as u32;
+
+        if self.wr > self.cap {
+            self.wr = self.cap;
+        }
+    }
+
+    unsafe fn mut_bytes<'a>(&'a mut self) -> &'a mut [u8] {
+        let wr = self.wr as usize;
+        let cap = self.cap as usize;
+        &mut self.mem.bytes_mut()[wr..cap]
+    }
+}
diff --git a/src/buf/byte.rs b/src/buf/byte.rs
index 661abf9..0bfe6d6 100644
--- a/src/buf/byte.rs
+++ b/src/buf/byte.rs
@@ -103,7 +103,9 @@ impl ByteBuf {
         let cnt = len as u32;
         let pos = self.pos as usize;
 
-        dst[0..len].copy_from_slice(&self.mem.bytes()[pos..pos+len]);
+        unsafe {
+            dst[0..len].copy_from_slice(&self.mem.bytes()[pos..pos+len]);
+        }
 
         self.pos += cnt;
         len
@@ -168,7 +170,7 @@ impl Buf for ByteBuf {
 
     #[inline]
     fn bytes<'a>(&'a self) -> &'a [u8] {
-        &self.mem.bytes()[self.pos()..self.lim()]
+        unsafe { &self.mem.bytes()[self.pos()..self.lim()] }
     }
 
     #[inline]
@@ -294,8 +296,10 @@ impl MutByteBuf {
         let cnt = cmp::min(src.len(), self.buf.remaining());
         let pos = self.buf.pos as usize;
 
-        self.buf.mem.bytes_mut()[pos..pos+cnt]
-            .copy_from_slice(&src[0..cnt]);
+        unsafe {
+            self.buf.mem.bytes_mut()[pos..pos+cnt]
+                .copy_from_slice(&src[0..cnt]);
+        }
 
         self.buf.pos += cnt as u32;
 
@@ -303,7 +307,7 @@ impl MutByteBuf {
     }
 
     pub fn bytes<'a>(&'a self) -> &'a [u8] {
-        &self.buf.mem.bytes()[..self.buf.pos()]
+        unsafe { &self.buf.mem.bytes()[..self.buf.pos()] }
     }
 }
 
diff --git a/src/buf/mod.rs b/src/buf/mod.rs
index 1ce2c00..4b1e3eb 100644
--- a/src/buf/mod.rs
+++ b/src/buf/mod.rs
@@ -1,3 +1,4 @@
+mod append;
 mod byte;
 mod ring;
 mod sink;
@@ -5,6 +6,7 @@ mod slice;
 mod source;
 mod take;
 
+pub use self::append::AppendBuf;
 pub use self::byte::{ByteBuf, MutByteBuf, ROByteBuf};
 pub use self::ring::RingBuf;
 pub use self::slice::{SliceBuf, MutSliceBuf};
diff --git a/src/buf/ring.rs b/src/buf/ring.rs
index 891eb12..e799e2a 100644
--- a/src/buf/ring.rs
+++ b/src/buf/ring.rs
@@ -156,7 +156,7 @@ impl Buf for RingBuf {
             to = self.cap
         }
 
-        &self.ptr.bytes()[self.pos .. to]
+        unsafe { &self.ptr.bytes()[self.pos .. to] }
     }
 
     fn advance(&mut self, cnt: usize) {
diff --git a/src/str/seq.rs b/src/str/seq.rs
index 044f507..b36456a 100644
--- a/src/str/seq.rs
+++ b/src/str/seq.rs
@@ -81,7 +81,7 @@ impl ops::Index<usize> for SeqByteStr {
 
     fn index(&self, index: usize) -> &u8 {
         assert!(index < self.len());
-        self.mem.bytes().index(index + self.pos as usize)
+        unsafe { self.mem.bytes().index(index + self.pos as usize) }
     }
 }
 
diff --git a/test/test.rs b/test/test.rs
index 9fc3f42..d8172d6 100644
--- a/test/test.rs
+++ b/test/test.rs
@@ -4,6 +4,7 @@ extern crate bytes;
 extern crate rand;
 extern crate byteorder;
 
+mod test_append;
 mod test_buf;
 mod test_buf_fill;
 mod test_buf_take;
diff --git a/test/test_append.rs b/test/test_append.rs
new file mode 100644
index 0000000..227e9ae
--- /dev/null
+++ b/test/test_append.rs
@@ -0,0 +1,30 @@
+use bytes::{ByteStr, Buf, MutBuf};
+use bytes::buf::AppendBuf;
+
+#[test]
+pub fn test_initial_buf_empty() {
+    // Run in a loop a bunch in hope that if there is a memory issue, it will
+    // be exposed
+    for _ in 0..1000 {
+        let mut buf = AppendBuf::with_capacity(100);
+        let mut dst: Vec<u8> = vec![];
+
+        assert_eq!(buf.remaining(), 128);
+
+        buf.write_slice(b"hello world");
+        assert_eq!(buf.remaining(), 117);
+        assert_eq!(buf.bytes(), b"hello world");
+
+        let view1 = buf.slice(0, 11);
+        view1.buf().copy_to(&mut dst).unwrap();
+
+        assert_eq!(dst, b"hello world");
+        assert_eq!(view1, buf.slice(0, 11));
+
+        drop(buf);
+        let mut buf = AppendBuf::with_capacity(100);
+        buf.write_slice(b"zomg no no no no");
+
+        assert_eq!(dst, b"hello world");
+    }
+}
-- 
GitLab