summaryrefslogtreecommitdiffstats
path: root/buffered-reader
diff options
context:
space:
mode:
authorNeal H. Walfield <neal@pep.foundation>2018-04-11 14:41:11 +0200
committerNeal H. Walfield <neal@pep.foundation>2018-04-11 14:43:52 +0200
commit26b7598e27c7730ad0b88b1d7c8370b2a600b68a (patch)
treeddbc8fa0e9475293c30ec050ffd6152d4384ed0b /buffered-reader
parent947e04f310775e0acc4c9214a5b81921c5593097 (diff)
buffered-reader: Add BufferedReaderDup.
- Add a new BufferedReader implementation that allows using a BufferedReader as usual, but without actually consuming the data.
Diffstat (limited to 'buffered-reader')
-rw-r--r--buffered-reader/src/dup.rs197
-rw-r--r--buffered-reader/src/lib.rs5
2 files changed, 202 insertions, 0 deletions
diff --git a/buffered-reader/src/dup.rs b/buffered-reader/src/dup.rs
new file mode 100644
index 00000000..79f8c6da
--- /dev/null
+++ b/buffered-reader/src/dup.rs
@@ -0,0 +1,197 @@
+use std::io;
+use std::fmt;
+use std::cmp;
+
+use super::*;
+
+/// A `BufferedReader` that duplicates the underlying `BufferedReader`
+/// without consuming any of the data.
+///
+/// Note: this will likely cause the underlying stream to buffer as
+/// much data as you read. Thus, it should only be used for peeking
+/// at the underlying `BufferedReader`.
+pub struct BufferedReaderDup<R: BufferedReader<C>, C> {
+ reader: R,
+
+ // The number of bytes that have been consumed.
+ cursor: usize,
+
+ // The user settable cookie.
+ cookie: C,
+}
+
+impl<R: BufferedReader<C>, C> fmt::Debug for BufferedReaderDup<R, C> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("BufferedReaderDup")
+ .field("reader", &self.reader)
+ .field("cursor", &self.cursor)
+ .finish()
+ }
+}
+
+impl<R: BufferedReader<()>> BufferedReaderDup<R, ()> {
+ /// Instantiate a new memory-based reader. `buffer` contains the
+ /// reader's contents.
+ pub fn new(reader: R) -> Self {
+ Self::with_cookie(reader, ())
+ }
+}
+
+impl<R: BufferedReader<C>, C> BufferedReaderDup<R, 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: R, cookie: C) -> Self {
+ BufferedReaderDup {
+ reader: reader,
+ cursor: 0,
+ cookie: cookie,
+ }
+ }
+
+ /// Returns the number of bytes that have been consumed by this
+ /// reader.
+ pub fn total_out(&self) -> usize {
+ return self.cursor;
+ }
+}
+
+impl<R: BufferedReader<C>, C> io::Read for BufferedReaderDup<R, C> {
+ fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
+ let data = self.reader.data(self.cursor + buf.len())?;
+ assert!(data.len() >= self.cursor);
+ let data = &data[self.cursor..];
+
+ let amount = cmp::min(buf.len(), data.len());
+ buf.copy_from_slice(&data[..amount]);
+
+ self.cursor += amount;
+
+ Ok(amount)
+ }
+}
+
+impl<R: BufferedReader<C>, C> BufferedReader<C> for BufferedReaderDup<R, C> {
+ fn buffer(&self) -> &[u8] {
+ let data = self.reader.buffer();
+ assert!(data.len() >= self.cursor);
+ &data[self.cursor..]
+ }
+
+ fn data(&mut self, amount: usize) -> Result<&[u8], io::Error> {
+ let data = self.reader.data(self.cursor + amount)?;
+ assert!(data.len() >= self.cursor);
+ Ok(&data[self.cursor..])
+ }
+
+ fn consume(&mut self, amount: usize) -> &[u8] {
+ let data = self.reader.buffer();
+ assert!(data.len() >= self.cursor + amount);
+ let data = &data[self.cursor..];
+ self.cursor += amount;
+ data
+ }
+
+ fn data_consume(&mut self, amount: usize) -> Result<&[u8], io::Error> {
+ let data = self.reader.data(self.cursor + amount)?;
+ assert!(data.len() >= self.cursor);
+ let data = &data[self.cursor..];
+ self.cursor += cmp::min(data.len(), amount);
+ Ok(data)
+ }
+
+ fn data_consume_hard(&mut self, amount: usize) -> Result<&[u8], io::Error> {
+ let data = self.reader.data_hard(self.cursor + amount)?;
+ assert!(data.len() >= self.cursor + amount);
+ let data = &data[self.cursor..];
+ self.cursor += amount;
+ Ok(data)
+ }
+
+ 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(Box::new(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 buffered_reader_memory_test () {
+ let data : &[u8] = include_bytes!("buffered-reader-test.txt");
+ let reader = BufferedReaderMemory::new(data);
+ let mut reader = BufferedReaderDup::new(reader);
+
+ buffered_reader_test_data_check(&mut reader);
+
+ let consumed = reader.total_out();
+ assert_eq!(consumed, data.len());
+
+ // Since we haven't consumed the inner buffer, this should
+ // still work.
+ let mut reader = Box::new(reader).into_inner().unwrap();
+
+ // Try to read consumed + 1 bytes (which shouldn't be
+ // possible).
+ assert_eq!(consumed, reader.data(consumed + 1).unwrap().len());
+
+ buffered_reader_test_data_check(&mut reader);
+ }
+
+ // Test that buffer() returns the same data as data().
+ #[test]
+ fn buffer_test() {
+ // Test vector. A BufferedReaderDup returns all unconsumed
+ // data. So, use a relatively small buffer size.
+ let size = DEFAULT_BUF_SIZE;
+ let mut input = Vec::with_capacity(size);
+ let mut v = 0u8;
+ for _ in 0..size {
+ input.push(v);
+ if v == std::u8::MAX {
+ v = 0;
+ } else {
+ v += 1;
+ }
+ }
+
+ let reader = BufferedReaderMemory::new(&input[..]);
+ let mut reader = BufferedReaderDup::new(reader);
+
+ for i in 0..input.len() {
+ let data = reader.data(DEFAULT_BUF_SIZE + 1).unwrap().to_vec();
+ assert!(data.len() > 0);
+ assert_eq!(data, reader.buffer());
+ // And, we may as well check to make sure we read the
+ // right data.
+ assert_eq!(data, &input[i..i+data.len()]);
+
+ // Consume one byte and see what happens.
+ reader.consume(1);
+ }
+ }
+}
diff --git a/buffered-reader/src/lib.rs b/buffered-reader/src/lib.rs
index ae2d44ed..ab19fa37 100644
--- a/buffered-reader/src/lib.rs
+++ b/buffered-reader/src/lib.rs
@@ -11,12 +11,14 @@ use std::fmt;
mod generic;
mod memory;
mod limitor;
+mod dup;
mod eof;
mod decompress;
pub use self::generic::BufferedReaderGeneric;
pub use self::memory::BufferedReaderMemory;
pub use self::limitor::BufferedReaderLimitor;
+pub use self::dup::BufferedReaderDup;
pub use self::eof::BufferedReaderEOF;
pub use self::decompress::BufferedReaderDeflate;
pub use self::decompress::BufferedReaderZlib;
@@ -116,6 +118,9 @@ pub trait BufferedReader<C> : io::Read + fmt::Debug {
/// This is a convenient function that effectively combines data()
/// and consume().
+ ///
+ /// If less than `amount` bytes are available, this consumes only
+ /// what is available.
fn data_consume(&mut self, amount: usize)
-> Result<&[u8], std::io::Error>;