summaryrefslogtreecommitdiffstats
path: root/buffered-reader
diff options
context:
space:
mode:
authorNeal H. Walfield <neal@pep.foundation>2018-09-26 13:36:29 +0200
committerNeal H. Walfield <neal@pep.foundation>2018-09-26 13:37:40 +0200
commitd86988b24efbd1e76962801b89bf65f26f4e554d (patch)
tree37fd6f1a15be3cf480257e9bf6bb6689b0022112 /buffered-reader
parent1c6b7bccdfdcbfcf1e0711a98d607cd6804717bc (diff)
buffered-reader: Add a new reader, BufferedReaderReserve
- This buffered reader prevents the reader from reading the last X bytes of the underlying buffered reader.
Diffstat (limited to 'buffered-reader')
-rw-r--r--buffered-reader/src/lib.rs4
-rw-r--r--buffered-reader/src/reserve.rs297
2 files changed, 300 insertions, 1 deletions
diff --git a/buffered-reader/src/lib.rs b/buffered-reader/src/lib.rs
index 4eb0f419..7d3e475e 100644
--- a/buffered-reader/src/lib.rs
+++ b/buffered-reader/src/lib.rs
@@ -14,6 +14,7 @@ use std::fmt;
mod generic;
mod memory;
mod limitor;
+mod reserve;
mod dup;
mod eof;
#[cfg(feature = "compression-deflate")]
@@ -24,6 +25,7 @@ mod decompress_bzip2;
pub use self::generic::BufferedReaderGeneric;
pub use self::memory::BufferedReaderMemory;
pub use self::limitor::BufferedReaderLimitor;
+pub use self::reserve::BufferedReaderReserve;
pub use self::dup::BufferedReaderDup;
pub use self::eof::BufferedReaderEOF;
#[cfg(feature = "compression-deflate")]
@@ -83,7 +85,7 @@ pub trait BufferedReader<C> : io::Read + fmt::Debug {
let result = self.data(amount);
if let Ok(buffer) = result {
if buffer.len() < amount {
- return Err(Error::new(ErrorKind::UnexpectedEof, "unepxected EOF"));
+ return Err(Error::new(ErrorKind::UnexpectedEof, "unexpected EOF"));
}
}
return result;
diff --git a/buffered-reader/src/reserve.rs b/buffered-reader/src/reserve.rs
new file mode 100644
index 00000000..c2787845
--- /dev/null
+++ b/buffered-reader/src/reserve.rs
@@ -0,0 +1,297 @@
+use std::io;
+
+use super::*;
+
+/// A `BufferedReaderReserve` allows a reader to read everything
+/// except for the last N bytes (the reserve) from the underlying
+/// `BufferedReader`.
+///
+/// Note: because the `BufferedReaderReserve` doesn't generally know
+/// how much data can be read from the underlying `BufferedReader`,
+/// it causes at least N bytes to by buffered.
+pub struct BufferedReaderReserve<'a, C> {
+ reader: Box<'a + BufferedReader<C>>,
+ reserve: usize,
+
+ cookie: C,
+}
+
+impl<'a, C> fmt::Debug for BufferedReaderReserve<'a, C> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("BufferedReaderReserve")
+ .field("reserve", &self.reserve)
+ .field("reader", &self.reader)
+ .finish()
+ }
+}
+
+impl<'a> BufferedReaderReserve<'a, ()> {
+ /// Instantiate a new `BufferedReaderReserve`. `reader` is the
+ /// source to wrap. `n` is the number of bytes that will not be
+ /// returned to the reader.
+ pub fn new(reader: Box<'a + BufferedReader<()>>, reserve: usize) -> Self {
+ Self::with_cookie(reader, reserve, ())
+ }
+}
+
+impl<'a, C> BufferedReaderReserve<'a, C> {
+ /// Like `new()`, but sets a cookie, which can be retrieved using
+ /// the `cookie_ref` and `cookie_mut` methods, and set using the
+ /// `cookie_set` method.
+ pub fn with_cookie(reader: Box<'a + BufferedReader<C>>,
+ reserve: usize, cookie: C)
+ -> BufferedReaderReserve<'a, C> {
+ BufferedReaderReserve {
+ reader: reader,
+ reserve: reserve,
+ cookie: cookie,
+ }
+ }
+}
+
+impl<'a, C> io::Read for BufferedReaderReserve<'a, C> {
+ fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
+ let to_read = {
+ let data = self.reader.data(buf.len() + self.reserve)?;
+ if data.len() > self.reserve {
+ data.len() - self.reserve
+ } else {
+ return Ok(0);
+ }
+ };
+
+ return self.reader.read(&mut buf[0..to_read]);
+ }
+}
+
+impl<'a, C> BufferedReader<C> for BufferedReaderReserve<'a, C> {
+ fn buffer(&self) -> &[u8] {
+ let buf = self.reader.buffer();
+ if buf.len() > self.reserve {
+ &buf[..buf.len() - self.reserve]
+ } else {
+ b""
+ }
+ }
+
+ /// Return the buffer. Ensure that it contains at least `amount`
+ /// bytes.
+ fn data(&mut self, amount: usize) -> Result<&[u8], io::Error> {
+ let data = self.reader.data(amount + self.reserve)?;
+ if data.len() <= self.reserve {
+ // EOF.
+ Ok(b"")
+ } else {
+ // More than enough.
+ Ok(&data[..data.len() - self.reserve])
+ }
+ }
+
+ fn consume(&mut self, amount: usize) -> &[u8] {
+ assert!(amount <= self.buffer().len());
+
+ // consume may return more than amount. If it does, make sure
+ // it doesn't return any of the reserve.
+ let data = self.reader.consume(amount);
+ assert!(data.len() >= amount);
+
+ if data.len() > amount {
+ // We got more than `amount`. We need to be careful to
+ // not return data from the reserve. But, we also know
+ // that `amount` does not include data from the reserve.
+ if data.len() > amount + self.reserve {
+ return &data[..data.len() - self.reserve];
+ }
+ }
+ &data[..amount]
+ }
+
+ fn data_consume(&mut self, amount: usize) -> Result<&[u8], io::Error> {
+ let amount = cmp::min(amount, self.data(amount)?.len());
+ Ok(self.consume(amount))
+ }
+
+ fn data_consume_hard(&mut self, amount: usize) -> Result<&[u8], io::Error> {
+ self.data_hard(amount)?;
+ Ok(self.consume(amount))
+ }
+
+ fn get_mut(&mut self) -> Option<&mut BufferedReader<C>> {
+ Some(&mut self.reader)
+ }
+
+ fn get_ref(&self) -> Option<&BufferedReader<C>> {
+ Some(&self.reader)
+ }
+
+ fn into_inner<'b>(self: Box<Self>) -> Option<Box<BufferedReader<C> + 'b>>
+ where Self: 'b {
+ Some(self.reader)
+ }
+
+ fn cookie_set(&mut self, cookie: C) -> C {
+ use std::mem;
+
+ mem::replace(&mut self.cookie, cookie)
+ }
+
+ fn cookie_ref(&self) -> &C {
+ &self.cookie
+ }
+
+ fn cookie_mut(&mut self) -> &mut C {
+ &mut self.cookie
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn data() {
+ use ::BufferedReaderMemory;
+
+ // orig is the original buffer
+ //
+ // cursor is the BufferedReaderReserve's position in orig.
+ //
+ // to_read is how much to read.
+ //
+ // total is the total to_read that be read from orig.
+ //
+ // cursor / reserve \
+ // orig: [ | to_read | | ]
+ // \ total /
+ //
+ fn read_chunk<'a, R: BufferedReader<C>, C>(
+ orig: &[u8], r: &mut R, to_read: usize, cursor: usize, total: usize,
+ mode: usize)
+ {
+ // Use data.
+ let data_len = {
+ let data = r.data(to_read).unwrap();
+ assert_eq!(data, &orig[cursor..cursor + data.len()]);
+ data.len()
+ };
+ assert!(data_len <= total - cursor);
+ assert_eq!(r.buffer().len(), data_len);
+
+ // Use data_hard.
+ let data_hard_len = {
+ let data_hard = r.data_hard(to_read).unwrap();
+ assert_eq!(data_hard, &orig[cursor..cursor + data_hard.len()]);
+ data_hard.len()
+ };
+ assert!(data_len <= data_hard_len);
+ assert!(data_hard_len <= total - cursor);
+ assert_eq!(r.buffer().len(), data_hard_len);
+
+
+
+ // Make sure data_hard fails when requesting too much
+ // data.
+ assert!(r.data_hard(total - cursor + 1).is_err());
+
+ // And that a failing data_hard does not move the cursor.
+ let data_len = {
+ let data = r.data(to_read).unwrap();
+ assert_eq!(data, &orig[cursor..cursor + data.len()]);
+ data.len()
+ };
+ assert!(data_len <= total - cursor);
+ assert_eq!(r.buffer().len(), data_len);
+
+
+ // Likewise for data_consume_hard.
+ assert!(r.data_consume_hard(total - cursor + 1).is_err());
+
+ // And that a failing data_hard does not move the cursor.
+ let data_len = {
+ let data = r.data(to_read).unwrap();
+ assert_eq!(data, &orig[cursor..cursor + data.len()]);
+ data.len()
+ };
+ assert!(data_len <= total - cursor);
+ assert_eq!(r.buffer().len(), data_len);
+
+
+
+ // Consume the chunk.
+ match mode {
+ 0 => {
+ // Use consume.
+ let l = r.consume(to_read).len();
+ assert!(to_read <= l);
+ assert!(l <= total - cursor);
+ }
+ 1 => {
+ // Use data_consume.
+ let data_len = {
+ let data = r.data_consume(to_read).unwrap();
+ assert_eq!(data, &orig[cursor..cursor + data.len()]);
+ data.len()
+ };
+ assert!(data_len <= total - cursor);
+ assert!(r.buffer().len() <= total - cursor - to_read);
+ }
+ 2 => {
+ // Use data_consume_hard.
+ let data_len = {
+ let data = r.data_consume_hard(to_read).unwrap();
+ assert_eq!(data, &orig[cursor..cursor + data.len()]);
+ data.len()
+ };
+ assert!(data_len <= total - cursor);
+ assert!(r.buffer().len() <= total - cursor - to_read);
+ }
+ _ => panic!("Invalid mode"),
+ }
+ }
+
+ fn test(orig: &[u8], mode: usize, reserve: usize,
+ mid1: usize, mid2: usize) {
+ let total = orig.len() - reserve;
+
+ let mut r = BufferedReaderReserve::new(
+ Box::new(BufferedReaderMemory::new(orig)), reserve);
+
+ // Read the first chunk.
+ read_chunk(orig, &mut r, mid1, 0, total, mode);
+
+ // Read the second chunk.
+ read_chunk(orig, &mut r, mid2 - mid1, mid1, total, mode);
+
+ // Read the remaining bit.
+ read_chunk(orig, &mut r, total - mid2, mid2, total, mode);
+
+ // And, we should be at EOF.
+ assert_eq!(r.data(100).unwrap().len(), 0);
+ assert_eq!(r.buffer().len(), 0);
+ assert!(r.data_hard(100).is_err());
+ assert_eq!(r.data_hard(0).unwrap().len(), 0);
+
+ let mut g = Box::new(r).into_inner().unwrap();
+ read_chunk(orig, &mut g,
+ orig.len() - total, total, orig.len(),
+ mode);
+ }
+
+ // 26 letters.
+ let orig : &[u8] = b"abcdefghijklmnopqrstuvwxyz";
+
+ // We break up the above into four pieces: three chunks, and
+ // the reserved area.
+ for mode in 0..3 {
+ for reserve in 0..orig.len() {
+ let total = orig.len() - reserve;
+
+ for mid1 in 0..total {
+ for mid2 in mid1..total {
+ test(orig, mode, reserve, mid1, mid2);
+ }
+ }
+ }
+ }
+ }
+}