From 18f76fce2141a2db396bd914719918ffe92ea290 Mon Sep 17 00:00:00 2001 From: "Neal H. Walfield" Date: Mon, 1 Jan 2018 22:31:27 +0100 Subject: buffered_reader: Allow the user to store data in a BufferedReader. - We want to associate some data with a BufferedReader instance. Because a BufferedReader points to another BufferedReader, we can't use a wrapper object. This change provides a mechanism to store any required data inside the actual `BufferedReader`. Note: this is a zero-cost abstraction. If no data needs to be stored, then there is no cost. --- buffered-reader/src/decompress.rs | 132 ++++++++++++++++++++++++++++++-------- buffered-reader/src/generic.rs | 46 ++++++++++--- buffered-reader/src/lib.rs | 40 +++++++++--- buffered-reader/src/limitor.rs | 60 +++++++++++++---- buffered-reader/src/memory.rs | 43 +++++++++++-- 5 files changed, 258 insertions(+), 63 deletions(-) (limited to 'buffered-reader') diff --git a/buffered-reader/src/decompress.rs b/buffered-reader/src/decompress.rs index 648037fc..0bad2e48 100644 --- a/buffered-reader/src/decompress.rs +++ b/buffered-reader/src/decompress.rs @@ -7,25 +7,37 @@ use bzip2::read::BzDecoder; use super::*; -pub struct BufferedReaderDeflate { - reader: BufferedReaderGeneric>, +pub struct BufferedReaderDeflate, C> { + reader: BufferedReaderGeneric, C>, } -impl BufferedReaderDeflate { - pub fn new(reader: R) -> BufferedReaderDeflate { +impl > BufferedReaderDeflate { + /// Instantiate a new deflate decompression reader. `reader` is + /// the source to wrap. + pub fn new(reader: R) -> Self { + Self::with_cookie(reader, ()) + } +} + +impl , C> BufferedReaderDeflate { + /// 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 { BufferedReaderDeflate { - reader: BufferedReaderGeneric::new(DeflateDecoder::new(reader), None) + reader: BufferedReaderGeneric::with_cookie( + DeflateDecoder::new(reader), None, cookie), } } } -impl io::Read for BufferedReaderDeflate { +impl, C> io::Read for BufferedReaderDeflate { fn read(&mut self, buf: &mut [u8]) -> Result { self.reader.read(buf) } } -impl fmt::Debug for BufferedReaderDeflate { +impl , C> fmt::Debug for BufferedReaderDeflate { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("BufferedReaderDeflate") .field("reader", self.reader.reader.get_ref()) @@ -33,7 +45,8 @@ impl fmt::Debug for BufferedReaderDeflate { } } -impl BufferedReader for BufferedReaderDeflate { +impl, C> BufferedReader + for BufferedReaderDeflate { fn data(&mut self, amount: usize) -> Result<&[u8], io::Error> { return self.reader.data(amount); } @@ -75,31 +88,56 @@ impl BufferedReader for BufferedReaderDeflate { return self.reader.steal_eof(); } - fn into_inner<'b>(self: Box) -> Option> where Self: 'b { + fn into_inner<'b>(self: Box) + -> Option + 'b>> where Self: 'b { // Strip the outer box. Some(Box::new((*self).reader.reader.into_inner())) } + + fn cookie_set(&mut self, cookie: C) -> C { + self.reader.cookie_set(cookie) + } + + fn cookie_ref(&self) -> &C { + self.reader.cookie_ref() + } + + fn cookie_mut(&mut self) -> &mut C { + self.reader.cookie_mut() + } } -pub struct BufferedReaderZlib { - reader: BufferedReaderGeneric>, +pub struct BufferedReaderZlib, C> { + reader: BufferedReaderGeneric, C>, } -impl BufferedReaderZlib { - pub fn new(reader: R) -> BufferedReaderZlib { +impl > BufferedReaderZlib { + /// Instantiate a new zlib decompression reader. `reader` is + /// the source to wrap. + pub fn new(reader: R) -> Self { + Self::with_cookie(reader, ()) + } +} + +impl , C> BufferedReaderZlib { + /// 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 { BufferedReaderZlib { - reader: BufferedReaderGeneric::new(ZlibDecoder::new(reader), None) + reader: BufferedReaderGeneric::with_cookie( + ZlibDecoder::new(reader), None, cookie), } } } -impl io::Read for BufferedReaderZlib { +impl, C> io::Read for BufferedReaderZlib { fn read(&mut self, buf: &mut [u8]) -> Result { self.reader.read(buf) } } -impl fmt::Debug for BufferedReaderZlib { +impl , C> fmt::Debug for BufferedReaderZlib { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("BufferedReaderZlib") .field("reader", self.reader.reader.get_ref()) @@ -107,7 +145,8 @@ impl fmt::Debug for BufferedReaderZlib { } } -impl BufferedReader for BufferedReaderZlib { +impl, C> BufferedReader + for BufferedReaderZlib { fn data(&mut self, amount: usize) -> Result<&[u8], io::Error> { return self.reader.data(amount); } @@ -149,31 +188,56 @@ impl BufferedReader for BufferedReaderZlib { return self.reader.steal_eof(); } - fn into_inner<'b>(self: Box) -> Option> where Self: 'b { + fn into_inner<'b>(self: Box) + -> Option + 'b>> where Self: 'b { // Strip the outer box. Some(Box::new((*self).reader.reader.into_inner())) } + + fn cookie_set(&mut self, cookie: C) -> C { + self.reader.cookie_set(cookie) + } + + fn cookie_ref(&self) -> &C { + self.reader.cookie_ref() + } + + fn cookie_mut(&mut self) -> &mut C { + self.reader.cookie_mut() + } } -pub struct BufferedReaderBzip { - reader: BufferedReaderGeneric>, +pub struct BufferedReaderBzip, C> { + reader: BufferedReaderGeneric, C>, +} + +impl > BufferedReaderBzip { + /// Instantiate a new bzip decompression reader. `reader` is + /// the source to wrap. + pub fn new(reader: R) -> Self { + Self::with_cookie(reader, ()) + } } -impl BufferedReaderBzip { - pub fn new(reader: R) -> BufferedReaderBzip { +impl , C> BufferedReaderBzip { + /// 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 { BufferedReaderBzip { - reader: BufferedReaderGeneric::new(BzDecoder::new(reader), None) + reader: BufferedReaderGeneric::with_cookie( + BzDecoder::new(reader), None, cookie), } } } -impl io::Read for BufferedReaderBzip { +impl, C> io::Read for BufferedReaderBzip { fn read(&mut self, buf: &mut [u8]) -> Result { self.reader.read(buf) } } -impl fmt::Debug for BufferedReaderBzip { +impl , C> fmt::Debug for BufferedReaderBzip { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("BufferedReaderBzip") .field("reader", self.reader.reader.get_ref()) @@ -181,7 +245,7 @@ impl fmt::Debug for BufferedReaderBzip { } } -impl BufferedReader for BufferedReaderBzip { +impl, C> BufferedReader for BufferedReaderBzip { fn data(&mut self, amount: usize) -> Result<&[u8], io::Error> { return self.reader.data(amount); } @@ -223,8 +287,22 @@ impl BufferedReader for BufferedReaderBzip { return self.reader.steal_eof(); } - fn into_inner<'b>(self: Box) -> Option> where Self: 'b { + fn into_inner<'b>(self: Box) + -> Option + 'b>> where Self: 'b { // Strip the outer box. Some(Box::new((*self).reader.reader.into_inner())) } + + fn cookie_set(&mut self, cookie: C) -> C { + self.reader.cookie_set(cookie) + } + + fn cookie_ref(&self) -> &C { + self.reader.cookie_ref() + } + + fn cookie_mut(&mut self) -> &mut C { + self.reader.cookie_mut() + } } + diff --git a/buffered-reader/src/generic.rs b/buffered-reader/src/generic.rs index 92ed6151..b8a0ad25 100644 --- a/buffered-reader/src/generic.rs +++ b/buffered-reader/src/generic.rs @@ -9,7 +9,7 @@ use super::*; /// source that implements the `Read` trait. This is sufficient when /// reading from a file, and it even works with a `&[u8]` (but /// `BufferedReaderMemory` is more efficient). -pub struct BufferedReaderGeneric { +pub struct BufferedReaderGeneric { buffer: Option>, // The next byte to read in the buffer. cursor: usize, @@ -22,9 +22,12 @@ pub struct BufferedReaderGeneric { saw_eof: bool, // The last error that we encountered, but have not yet returned. error: Option, + + // The user settable cookie. + cookie: C, } -impl fmt::Debug for BufferedReaderGeneric { +impl fmt::Debug for BufferedReaderGeneric { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let buffered_data = if let Some(ref buffer) = self.buffer { buffer.len() - self.cursor @@ -41,13 +44,23 @@ impl fmt::Debug for BufferedReaderGeneric { } } -impl BufferedReaderGeneric { +impl BufferedReaderGeneric { /// Instantiate a new generic reader. `reader` is the source to /// wrap. `preferred_chuck_size` is the preferred chuck size. If /// None, then the default will be used, which is usually what you /// want. - pub fn new(reader: T, preferred_chunk_size: Option) - -> BufferedReaderGeneric { + pub fn new(reader: T, preferred_chunk_size: Option) -> Self { + Self::with_cookie(reader, preferred_chunk_size, ()) + } +} + +impl BufferedReaderGeneric { + /// 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: T, preferred_chunk_size: Option, cookie: C) + -> Self { BufferedReaderGeneric { buffer: None, cursor: 0, @@ -57,6 +70,7 @@ impl BufferedReaderGeneric { reader: Box::new(reader), saw_eof: false, error: None, + cookie: cookie, } } @@ -172,13 +186,13 @@ impl BufferedReaderGeneric { } } -impl io::Read for BufferedReaderGeneric { +impl io::Read for BufferedReaderGeneric { fn read(&mut self, buf: &mut [u8]) -> Result { return buffered_reader_generic_read_impl(self, buf); } } -impl BufferedReader for BufferedReaderGeneric { +impl BufferedReader for BufferedReaderGeneric { fn data(&mut self, amount: usize) -> Result<&[u8], io::Error> { return self.data_helper(amount, false, false); } @@ -198,7 +212,7 @@ impl BufferedReader for BufferedReaderGeneric { if let Some(ref buffer) = self.buffer { assert!(self.cursor <= buffer.len()); assert!(amount <= buffer.len() - self.cursor, - "buffer contains just {} bytes, but you are trying to + "buffer contains just {} bytes, but you are trying to \ consume {} bytes. Did you forget to call data()?", buffer.len() - self.cursor, amount); @@ -218,10 +232,24 @@ impl BufferedReader for BufferedReaderGeneric { return self.data_helper(amount, true, true); } - fn into_inner<'b>(self: Box) -> Option> + fn into_inner<'b>(self: Box) -> Option + 'b>> where Self: 'b { None } + + 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 + } } #[test] diff --git a/buffered-reader/src/lib.rs b/buffered-reader/src/lib.rs index a8b4ee3c..4738ed2c 100644 --- a/buffered-reader/src/lib.rs +++ b/buffered-reader/src/lib.rs @@ -31,7 +31,7 @@ const DEFAULT_BUF_SIZE: usize = 8 * 1024; /// to first copy it to a local buffer. However, unlike `BufRead`, /// `BufferedReader` allows the caller to ensure that the internal /// buffer has a certain amount of data. -pub trait BufferedReader : io::Read + fmt::Debug { +pub trait BufferedReader : io::Read + fmt::Debug { /// Return the data in the internal buffer. Normally, the /// returned buffer will contain *at least* `amount` bytes worth /// of data. Less data may be returned if (and only if) the end @@ -169,8 +169,17 @@ pub trait BufferedReader : io::Read + fmt::Debug { Ok(()) } - fn into_inner<'a>(self: Box) -> Option> + fn into_inner<'a>(self: Box) -> Option + 'a>> where Self: 'a; + + /// Sets the `BufferedReader`'s cookie and returns the old value. + fn cookie_set(&mut self, cookie: C) -> C; + + /// Returns a reference to the `BufferedReader`'s cookie. + fn cookie_ref(&self) -> &C; + + /// Returns a mutable reference to the `BufferedReader`'s cookie. + fn cookie_mut(&mut self) -> &mut C; } /// This function implements the `std::io::Read::read` method in terms @@ -196,8 +205,8 @@ pub trait BufferedReader : io::Read + fmt::Debug { /// /// but, alas, Rust doesn't like that ("error[E0119]: conflicting /// implementations of trait `std::io::Read` for type `&mut _`"). -pub fn buffered_reader_generic_read_impl - (bio: &mut T, buf: &mut [u8]) -> Result { +pub fn buffered_reader_generic_read_impl, C> + (bio: &mut T, buf: &mut [u8]) -> Result { match bio.data_consume(buf.len()) { Ok(inner) => { let amount = cmp::min(buf.len(), inner.len()); @@ -209,7 +218,7 @@ pub fn buffered_reader_generic_read_impl } /// Make a `Box` look like a BufferedReader. -impl <'a> BufferedReader for Box { +impl <'a, C> BufferedReader for Box + 'a> { fn data(&mut self, amount: usize) -> Result<&[u8], io::Error> { return self.as_mut().data(amount); } @@ -255,18 +264,30 @@ impl <'a> BufferedReader for Box { return self.as_mut().drop_eof(); } - fn into_inner<'b>(self: Box) -> Option> + fn into_inner<'b>(self: Box) -> Option + 'b>> where Self: 'b { // Strip the outer box. (*self).into_inner() } + + fn cookie_set(&mut self, cookie: C) -> C { + self.as_mut().cookie_set(cookie) + } + + fn cookie_ref(&self) -> &C { + self.as_ref().cookie_ref() + } + + fn cookie_mut(&mut self) -> &mut C { + self.as_mut().cookie_mut() + } } // The file was created as follows: // // for i in $(seq 0 9999); do printf "%04d\n" $i; done > buffered-reader-test.txt #[cfg(test)] -fn buffered_reader_test_data_check<'a, T: BufferedReader + 'a>(bio: &mut T) { +fn buffered_reader_test_data_check<'a, T: BufferedReader + 'a, C>(bio: &mut T) { use std::str; for i in 0 .. 10000 { @@ -308,7 +329,8 @@ mod test { // Try it again with a limitor. { let bio = BufferedReaderMemory::new(data); - let mut bio2 = BufferedReaderLimitor::new(bio, (data.len() / 2) as u64); + let mut bio2 = BufferedReaderLimitor::new( + bio, (data.len() / 2) as u64); let amount = { bio2.data_eof().unwrap().len() }; @@ -319,7 +341,7 @@ mod test { } #[cfg(test)] - fn buffered_reader_read_test_aux<'a, T: BufferedReader + 'a> + fn buffered_reader_read_test_aux<'a, T: BufferedReader + 'a, C> (mut bio: T, data: &[u8]) { let mut buffer = [0; 99]; diff --git a/buffered-reader/src/limitor.rs b/buffered-reader/src/limitor.rs index 052ceb0d..04916ef6 100644 --- a/buffered-reader/src/limitor.rs +++ b/buffered-reader/src/limitor.rs @@ -5,29 +5,53 @@ use super::*; /// A `BufferedReaderLimitor` limits the amount of data that can be /// read from a `BufferedReader`. -#[derive(Debug)] -pub struct BufferedReaderLimitor { +pub struct BufferedReaderLimitor, C> { reader: T, limit: u64, + + cookie: C, +} + +impl, C> fmt::Debug for BufferedReaderLimitor { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("BufferedReaderLimitor") + .field("limit", &self.limit) + .field("reader", &self.reader) + .finish() + } +} + +impl> BufferedReaderLimitor { + /// Instantiate a new limitor. `reader` is the source to wrap. + /// `limit` is the maximum number of bytes that will be returned + /// from the source. + pub fn new(reader: T, limit: u64) -> Self { + Self::with_cookie(reader, limit, ()) + } } -impl BufferedReaderLimitor { - pub fn new(reader: T, limit: u64) -> BufferedReaderLimitor { +impl, C> BufferedReaderLimitor { + /// 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: T, limit: u64, cookie: C) + -> BufferedReaderLimitor { BufferedReaderLimitor { reader: reader, limit: limit, + cookie: cookie, } } } -impl io::Read for BufferedReaderLimitor { +impl, C> io::Read for BufferedReaderLimitor { fn read(&mut self, buf: &mut [u8]) -> Result { let len = cmp::min(self.limit, buf.len() as u64) as usize; return self.reader.read(&mut buf[0..len]); } } -impl BufferedReader for BufferedReaderLimitor { +impl, C> BufferedReader for BufferedReaderLimitor { /// Return the buffer. Ensure that it contains at least `amount` /// bytes. fn data(&mut self, amount: usize) -> Result<&[u8], io::Error> { @@ -75,10 +99,24 @@ impl BufferedReader for BufferedReaderLimitor { return result; } - fn into_inner<'b>(self: Box) -> Option> + fn into_inner<'b>(self: Box) -> Option + '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 + } } #[test] @@ -87,7 +125,7 @@ fn buffered_reader_limitor_test() { /* Add a single limitor. */ { - let mut bio : Box + let mut bio : Box> = Box::new(BufferedReaderMemory::new(data)); bio = { @@ -125,15 +163,15 @@ fn buffered_reader_limitor_test() { /* Try with two limitors where the first one imposes the real * limit. */ { - let mut bio : Box + let mut bio : Box> = Box::new(BufferedReaderMemory::new(data)); bio = { - let bio2 : Box + let bio2 : Box> = Box::new(BufferedReaderLimitor::new(bio, 5)); // We limit to 15 bytes, but bio2 will still limit us to 5 // bytes. - let mut bio3 : Box + let mut bio3 : Box> = Box::new(BufferedReaderLimitor::new(bio2, 15)); { let result = bio3.data(100).unwrap(); diff --git a/buffered-reader/src/memory.rs b/buffered-reader/src/memory.rs index 8d387c49..2bdc0eb2 100644 --- a/buffered-reader/src/memory.rs +++ b/buffered-reader/src/memory.rs @@ -7,13 +7,16 @@ use std::io::{Error,ErrorKind}; use super::*; /// A `BufferedReader` specialized for reading from memory buffers. -pub struct BufferedReaderMemory<'a> { +pub struct BufferedReaderMemory<'a, C> { buffer: &'a [u8], // The next byte to read in the buffer. cursor: usize, + + // The user settable cookie. + cookie: C, } -impl <'a> fmt::Debug for BufferedReaderMemory<'a> { +impl<'a, C> fmt::Debug for BufferedReaderMemory<'a, C> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("BufferedReaderMemory") .field("buffer (bytes)", &&self.buffer.len()) @@ -22,11 +25,23 @@ impl <'a> fmt::Debug for BufferedReaderMemory<'a> { } } -impl<'a> BufferedReaderMemory<'a> { - pub fn new(buffer: &'a [u8]) -> BufferedReaderMemory<'a> { +impl<'a> BufferedReaderMemory<'a, ()> { + /// Instantiate a new memory-based reader. `buffer` contains the + /// reader's contents. + pub fn new(buffer: &'a [u8]) -> Self { + Self::with_cookie(buffer, ()) + } +} + +impl<'a, C> BufferedReaderMemory<'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(buffer: &'a [u8], cookie: C) -> Self { BufferedReaderMemory { buffer: buffer, cursor: 0, + cookie: cookie, } } @@ -37,7 +52,7 @@ impl<'a> BufferedReaderMemory<'a> { } } -impl<'a> io::Read for BufferedReaderMemory<'a> { +impl<'a, C> io::Read for BufferedReaderMemory<'a, C> { fn read(&mut self, buf: &mut [u8]) -> Result { let amount = cmp::min(buf.len(), self.buffer.len() - self.cursor); buf[0..amount].copy_from_slice( @@ -47,7 +62,7 @@ impl<'a> io::Read for BufferedReaderMemory<'a> { } } -impl<'a> BufferedReader for BufferedReaderMemory<'a> { +impl<'a, C> BufferedReader for BufferedReaderMemory<'a, C> { /// Return the buffer. Ensure that it contains at least `amount` /// bytes. fn data(&mut self, _amount: usize) -> Result<&[u8], io::Error> { @@ -77,10 +92,24 @@ impl<'a> BufferedReader for BufferedReaderMemory<'a> { return Ok(self.consume(amount)); } - fn into_inner<'b>(self: Box) -> Option> + fn into_inner<'b>(self: Box) -> Option + 'b>> where Self: 'b { None } + + 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 + } } #[test] -- cgit v1.2.3