From ed087be8532dba1e698e57cbb99a8e5428244985 Mon Sep 17 00:00:00 2001
From: Carl Lerche <me@carllerche.com>
Date: Tue, 28 Jul 2015 12:51:24 -0700
Subject: [PATCH] Provide `Take` decorator that limits `Buf` size

---
 src/buf/mod.rs        |  2 ++
 src/buf/take.rs       | 79 +++++++++++++++++++++++++++++++++++++++++++
 src/lib.rs            |  3 +-
 test/test.rs          |  1 +
 test/test_buf_take.rs | 12 +++++++
 5 files changed, 96 insertions(+), 1 deletion(-)
 create mode 100644 src/buf/take.rs
 create mode 100644 test/test_buf_take.rs

diff --git a/src/buf/mod.rs b/src/buf/mod.rs
index 07de5f8..2498b19 100644
--- a/src/buf/mod.rs
+++ b/src/buf/mod.rs
@@ -3,10 +3,12 @@ mod ring;
 mod sink;
 mod slice;
 mod source;
+mod take;
 
 pub use self::byte::{ByteBuf, MutByteBuf, ROByteBuf};
 pub use self::ring::RingBuf;
 pub use self::slice::{SliceBuf, MutSliceBuf};
+pub use self::take::Take;
 
 use {BufError, RopeBuf};
 use std::{cmp, fmt, io, ptr, usize};
diff --git a/src/buf/take.rs b/src/buf/take.rs
new file mode 100644
index 0000000..9995152
--- /dev/null
+++ b/src/buf/take.rs
@@ -0,0 +1,79 @@
+use buf::{Buf, MutBuf};
+use std::{cmp, io};
+
+#[derive(Debug)]
+pub struct Take<T> {
+    inner: T,
+    limit: usize,
+}
+
+impl<T> Take<T> {
+    pub fn new(inner: T, limit: usize) -> Take<T> {
+        Take {
+            inner: inner,
+            limit: limit,
+        }
+    }
+
+    pub fn into_inner(self) -> T {
+        self.inner
+    }
+
+    pub fn get_ref(&self) -> &T {
+        &self.inner
+    }
+
+    pub fn get_mut(&mut self) -> &mut T {
+        &mut self.inner
+    }
+
+    pub fn limit(&self) -> usize {
+        self.limit
+    }
+
+    pub fn set_limit(&mut self, lim: usize) {
+        self.limit = lim
+    }
+}
+
+impl<T: Buf> Buf for Take<T> {
+    fn remaining(&self) -> usize {
+        cmp::min(self.inner.remaining(), self.limit)
+    }
+
+    fn bytes<'a>(&'a self) -> &'a [u8] {
+        &self.inner.bytes()[..self.limit]
+    }
+
+    fn advance(&mut self, cnt: usize) {
+        let cnt = cmp::min(cnt, self.limit);
+        self.limit -= cnt;
+        self.inner.advance(cnt);
+    }
+}
+
+impl<T: Buf> io::Read for Take<T> {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        if !self.has_remaining() {
+            return Ok(0);
+        }
+
+        Ok(self.read_slice(buf))
+    }
+}
+
+impl<T: MutBuf> MutBuf for Take<T> {
+    fn remaining(&self) -> usize {
+        cmp::min(self.inner.remaining(), self.limit)
+    }
+
+    fn mut_bytes<'a>(&'a mut self) -> &'a mut [u8] {
+        &mut self.inner.mut_bytes()[..self.limit]
+    }
+
+    fn advance(&mut self, cnt: usize) {
+        let cnt = cmp::min(cnt, self.limit);
+        self.limit -= cnt;
+        self.inner.advance(cnt);
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 42a9eab..820f49b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -17,7 +17,8 @@ pub use buf::{
     SliceBuf,
     MutSliceBuf,
     Source,
-    Sink
+    Sink,
+    Take,
 };
 pub use str::{
     ByteStr,
diff --git a/test/test.rs b/test/test.rs
index 283685f..77fcd57 100644
--- a/test/test.rs
+++ b/test/test.rs
@@ -5,6 +5,7 @@ extern crate rand;
 
 mod test_buf;
 mod test_buf_fill;
+mod test_buf_take;
 mod test_byte_buf;
 mod test_bytes;
 mod test_ring;
diff --git a/test/test_buf_take.rs b/test/test_buf_take.rs
new file mode 100644
index 0000000..b2300c1
--- /dev/null
+++ b/test/test_buf_take.rs
@@ -0,0 +1,12 @@
+use bytes::*;
+use std::io::{Cursor, Read};
+
+#[test]
+pub fn test_take_from_buf() {
+    let mut buf = Take::new(Cursor::new(b"hello world".to_vec()), 5);
+    let mut res = vec![];
+
+    buf.read_to_end(&mut res);
+
+    assert_eq!(&res, b"hello");
+}
-- 
GitLab