Skip to content

Commit

Permalink
feat(inflate): add option to ignore and not compute zlib checksum whe…
Browse files Browse the repository at this point in the history
…n decompressing

Do this in a mostly backwards-compatible way, so it may be a bit clunky.

Closes #102
  • Loading branch information
oyvindln committed Nov 1, 2021
1 parent 19782aa commit 2e9408a
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 5 deletions.
32 changes: 30 additions & 2 deletions miniz_oxide/src/inflate/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,20 @@ pub mod inflate_flags {
pub const TINFL_FLAG_HAS_MORE_INPUT: u32 = 2;
/// The output buffer should not wrap around.
pub const TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: u32 = 4;
/// Should we calculate the adler32 checksum of the output data?
/// Should we calculate the adler32 checksum of the output data even if we're not inflating a
/// zlib stream?
///
/// NOTE: Enable/disabling this between calls to decompress will result in an incorect checksum.
pub const TINFL_FLAG_COMPUTE_ADLER32: u32 = 8;
/// Should we ignore adler32 checksum even if we are inflating a zlib stream.
/// Overrides TINFL_FLAG_COMPUTE_ADLER32 if both are enabled.
///
/// NOTE: This flag does not exist in miniz as it does not support this and is a
/// custom addition for miniz_oxide.
/// NOTE: Should not be changed from enabled to disabled after decompression has started,
/// this will result in checksum failure (outside the unlikely event where the checksum happens
/// to match anyway).
pub const TINFL_FLAG_IGNORE_ADLER32: u32 = 64;
}

use self::inflate_flags::*;
Expand Down Expand Up @@ -174,6 +186,7 @@ impl DecompressorOxide {
}

/// Returns the adler32 checksum of the currently decompressed data.
/// Note: Will return Some(1) if decompressing zlib but ignoring adler32.
#[inline]
pub fn adler32(&self) -> Option<u32> {
if self.state != State::Start && !self.state.is_failure() && self.z_header0 != 0 {
Expand All @@ -182,6 +195,16 @@ impl DecompressorOxide {
None
}
}

/// Returns the adler32 that was read from the zlib header if it exists.
#[inline]
pub fn adler32_header(&self) -> Option<u32> {
if self.state != State::Start && self.state != State::BadZlibHeader && self.z_header0 != 0 {
Some(self.z_adler32)
} else {
None
}
}
}

impl Default for DecompressorOxide {
Expand Down Expand Up @@ -1616,7 +1639,12 @@ pub fn decompress(

// If this is a zlib stream, and update the adler32 checksum with the decompressed bytes if
// requested.
let need_adler = flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32) != 0;
let need_adler = if (flags & TINFL_FLAG_IGNORE_ADLER32) == 0 {
flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32) != 0
} else {
// If TINFL_FLAG_IGNORE_ADLER32 is enabled, ignore the checksum.
false
};
if need_adler && status as i32 >= 0 {
let out_buf_pos = out_buf.position();
r.check_adler32 = update_adler32(r.check_adler32, &out_buf.get_ref()[out_pos..out_buf_pos]);
Expand Down
25 changes: 23 additions & 2 deletions miniz_oxide/src/inflate/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,15 @@ pub fn inflate(
return StreamResult::error(MZError::Stream);
}

let mut decomp_flags = inflate_flags::TINFL_FLAG_COMPUTE_ADLER32;
if state.data_format == DataFormat::Zlib {
let mut decomp_flags = if state.data_format == DataFormat::Zlib {
inflate_flags::TINFL_FLAG_COMPUTE_ADLER32
} else {
inflate_flags::TINFL_FLAG_IGNORE_ADLER32
};

if (state.data_format == DataFormat::Zlib)
| (state.data_format == DataFormat::ZLibIgnoreChecksum)
{
decomp_flags |= inflate_flags::TINFL_FLAG_PARSE_ZLIB_HEADER;
}

Expand Down Expand Up @@ -375,5 +382,19 @@ mod test {
assert_eq!(status, MZStatus::StreamEnd);
assert_eq!(out[..res.bytes_written as usize], b"Hello, zlib!"[..]);
assert_eq!(res.bytes_consumed, encoded.len());
assert_eq!(state.decompressor().adler32(), Some(459605011));

// Test state when not computing adler.
state = InflateState::new_boxed(DataFormat::ZLibIgnoreChecksum);
out.iter_mut().map(|x| *x = 0).count();
let res = inflate(&mut state, &encoded, &mut out, MZFlush::Finish);
let status = res.status.expect("Failed to decompress!");
assert_eq!(status, MZStatus::StreamEnd);
assert_eq!(out[..res.bytes_written as usize], b"Hello, zlib!"[..]);
assert_eq!(res.bytes_consumed, encoded.len());
// Not computed, so should be Some(1)
assert_eq!(state.decompressor().adler32(), Some(1));
// Should still have the checksum read from the header file.
assert_eq!(state.decompressor().adler32_header(), Some(459605011))
}
}
6 changes: 5 additions & 1 deletion miniz_oxide/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,13 @@ pub enum MZError {

/// How compressed data is wrapped.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum DataFormat {
/// Wrapped using the [zlib](http://www.zlib.org/rfc-zlib.html) format.
Zlib,
/// Zlib wrapped but ignore and don't compute the adler32 checksum.
/// Currently only used for inflate, behaves the same as Zlib for compression.
ZLibIgnoreChecksum,
/// Raw DEFLATE.
Raw,
}
Expand All @@ -123,7 +127,7 @@ impl DataFormat {

pub(crate) fn to_window_bits(self) -> i32 {
match self {
DataFormat::Zlib => shared::MZ_DEFAULT_WINDOW_BITS,
DataFormat::Zlib | DataFormat::ZLibIgnoreChecksum => shared::MZ_DEFAULT_WINDOW_BITS,
DataFormat::Raw => -shared::MZ_DEFAULT_WINDOW_BITS,
}
}
Expand Down

0 comments on commit 2e9408a

Please sign in to comment.