Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 14 additions & 16 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,22 +143,20 @@ impl<R: Read + Seek> RevBufReader<R> {
/// }
/// ```
pub fn with_capacity(capacity: usize, mut inner: R) -> RevBufReader<R> {
unsafe {
let mut buffer = Vec::with_capacity(capacity);
buffer.set_len(capacity);

#[cfg(feature = "read_initializer")]
inner.initializer().initialize(&mut buffer);

inner
.seek(SeekFrom::End(0))
.expect("Cannot find the end of the stream.");
RevBufReader {
inner,
buf: buffer.into_boxed_slice(),
pos: 0,
cap: 0,
}
// The buffer is handed to `inner`'s `Read` impl in `fill_buf`, so it
// has to be initialised: `Read::read` only *recommends* against
// reading `buf`, and an implementation that does so would otherwise
// observe uninitialised memory.
let buffer = vec![0; capacity];

inner
.seek(SeekFrom::End(0))
.expect("Cannot find the end of the stream.");
RevBufReader {
inner,
buf: buffer.into_boxed_slice(),
pos: 0,
cap: 0,
}
}

Expand Down
60 changes: 60 additions & 0 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,3 +246,63 @@ fn test_lines() {
assert_eq!(it.next().unwrap().unwrap(), "a".to_string());
assert!(it.next().is_none());
}

/// The internal buffer is handed to the wrapped reader in `fill_buf`, so it
/// has to be initialised before it gets there. Under Miri a regression shows
/// up as "reading memory ... but memory is uninitialized"; natively the
/// assertion below catches it.
#[test]
fn test_buffer_handed_to_reader_is_initialized() {
struct PeekingReader {
data: Vec<u8>,
pos: usize,
first_read: bool,
}

impl Read for PeekingReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
// A `Read` impl is only *recommended* not to read `buf`, so
// doing so is within contract and must be well defined.
if self.first_read {
self.first_read = false;
let sum = buf.iter().fold(0u64, |a, b| a.wrapping_add(*b as u64));
assert_eq!(sum, 0, "buffer was not initialized before use");
}

let n = buf.len().min(self.data.len() - self.pos);
buf[..n].copy_from_slice(&self.data[self.pos..self.pos + n]);
self.pos += n;
Ok(n)
}
}

impl Seek for PeekingReader {
fn seek(&mut self, from: SeekFrom) -> io::Result<u64> {
let len = self.data.len() as i64;
let target = match from {
SeekFrom::Start(n) => n as i64,
SeekFrom::End(n) => len + n,
SeekFrom::Current(n) => self.pos as i64 + n,
};
if target < 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"negative seek",
));
}
self.pos = (target as usize).min(self.data.len());
Ok(self.pos as u64)
}
}

let inner = PeekingReader {
data: (0u8..64).collect(),
pos: 0,
first_read: true,
};
let mut reader = RevBufReader::with_capacity(128, inner);

let mut out = Vec::new();
reader.read_to_end(&mut out).unwrap();
assert_eq!(out.len(), 64);
}