diff options
Diffstat (limited to 'vendor/gix-features/src/zlib/stream/inflate.rs')
-rw-r--r-- | vendor/gix-features/src/zlib/stream/inflate.rs | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/vendor/gix-features/src/zlib/stream/inflate.rs b/vendor/gix-features/src/zlib/stream/inflate.rs new file mode 100644 index 000000000..007ecedc6 --- /dev/null +++ b/vendor/gix-features/src/zlib/stream/inflate.rs @@ -0,0 +1,57 @@ +use std::{io, io::BufRead}; + +use flate2::{Decompress, FlushDecompress, Status}; + +/// The boxed variant is faster for what we do (moving the decompressor in and out a lot) +pub struct ReadBoxed<R> { + /// The reader from which bytes should be decompressed. + pub inner: R, + /// The decompressor doing all the work. + pub decompressor: Box<Decompress>, +} + +impl<R> io::Read for ReadBoxed<R> +where + R: BufRead, +{ + fn read(&mut self, into: &mut [u8]) -> io::Result<usize> { + read(&mut self.inner, &mut self.decompressor, into) + } +} + +/// Read bytes from `rd` and decompress them using `state` into a pre-allocated fitting buffer `dst`, returning the amount of bytes written. +pub fn read(rd: &mut impl BufRead, state: &mut Decompress, mut dst: &mut [u8]) -> io::Result<usize> { + let mut total_written = 0; + loop { + let (written, consumed, ret, eof); + { + let input = rd.fill_buf()?; + eof = input.is_empty(); + let before_out = state.total_out(); + let before_in = state.total_in(); + let flush = if eof { + FlushDecompress::Finish + } else { + FlushDecompress::None + }; + ret = state.decompress(input, dst, flush); + written = (state.total_out() - before_out) as usize; + total_written += written; + dst = &mut dst[written..]; + consumed = (state.total_in() - before_in) as usize; + } + rd.consume(consumed); + + match ret { + // The stream has officially ended, nothing more to do here. + Ok(Status::StreamEnd) => return Ok(total_written), + // Either input our output are depleted even though the stream is not depleted yet. + Ok(Status::Ok) | Ok(Status::BufError) if eof || dst.is_empty() => return Ok(total_written), + // Some progress was made in both the input and the output, it must continue to reach the end. + Ok(Status::Ok) | Ok(Status::BufError) if consumed != 0 || written != 0 => continue, + // A strange state, where zlib makes no progress but isn't done either. Call it out. + Ok(Status::Ok) | Ok(Status::BufError) => unreachable!("Definitely a bug somewhere"), + Err(..) => return Err(io::Error::new(io::ErrorKind::InvalidInput, "corrupt deflate stream")), + } + } +} |