diff --git a/src/write.rs b/src/write.rs index 24d5e247..168d6fc0 100644 --- a/src/write.rs +++ b/src/write.rs @@ -204,7 +204,13 @@ impl BzDecoder { /// [`write`]: Self::write pub fn try_finish(&mut self) -> io::Result<()> { while !self.done { - let _ = self.write(&[])?; + // The write is effectively a `self.flush()`, but we want to know how many + // bytes were written. exit if no input was read and no output was written + if self.write(&[])? == 0 { + // finishing the output stream is effectively EOF of the input + let msg = "Input EOF reached before logical stream end"; + return Err(io::Error::new(io::ErrorKind::UnexpectedEof, msg)); + } } self.dump() } @@ -303,7 +309,8 @@ mod tests { } #[test] - fn write_empty() { + fn roundtrip_empty() { + // this encodes and then decodes an empty input file let d = BzDecoder::new(Vec::new()); let mut c = BzEncoder::new(d, Compression::default()); let _ = c.write(b"").unwrap(); @@ -311,6 +318,30 @@ mod tests { assert_eq!(&data[..], b""); } + #[test] + fn finish_empty_explicit() { + // The empty sequence is not a valid .bzip2 file! + // A valid file at least includes the magic bytes, the checksum, etc. + // + // This used to loop infinitely, see + // + // - https://github.com/trifectatechfoundation/bzip2-rs/issues/96 + // - https://github.com/trifectatechfoundation/bzip2-rs/pull/97 + let mut d = BzDecoder::new(Vec::new()); + d.write(b"").unwrap(); + let e = d.finish().unwrap_err(); + assert_eq!(e.kind(), std::io::ErrorKind::UnexpectedEof); + } + + #[test] + fn finish_empty_drop() { + // the drop implementation used to loop infinitely for empty input + // + // see https://github.com/trifectatechfoundation/bzip2-rs/pull/118 + let d = BzDecoder::new(Vec::new()); + drop(d); + } + #[test] fn write_invalid() { // see https://github.com/trifectatechfoundation/bzip2-rs/issues/98