summaryrefslogtreecommitdiffstats
path: root/vendor/gix-features/src/zlib/stream/inflate.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-features/src/zlib/stream/inflate.rs')
-rw-r--r--vendor/gix-features/src/zlib/stream/inflate.rs57
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")),
+ }
+ }
+}