Skip to content

Commit

Permalink
Derive Clone for InflateState to allow random-access reads (#157)
Browse files Browse the repository at this point in the history
I am implementing Seek for a wrapper that reads deflated files.  To do this, I will
inflate the file once and save the dictionary and inflate state at several points in
the file.  Later, when I want to seek to some random place in the file, I will use the
cloned state+dict to position to a known file position and then inflate until I reach
the data I need.

To support this, I need to be able to Clone the InflateState and its parts (the current
dictionary).  Derive Clone for InflateState, HuffmanTable and DecompressorOxide.
  • Loading branch information
phord authored Oct 4, 2024
1 parent 868c27a commit 0a33eff
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 0 deletions.
2 changes: 2 additions & 0 deletions miniz_oxide/src/inflate/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use self::output_buffer::OutputBuffer;
pub const TINFL_LZ_DICT_SIZE: usize = 32_768;

/// A struct containing huffman code lengths and the huffman code tree used by the decompressor.
#[derive(Clone)]
struct HuffmanTable {
/// Length of the code at each index.
pub code_size: [u8; MAX_HUFF_SYMBOLS_0],
Expand Down Expand Up @@ -166,6 +167,7 @@ type BitBuffer = u32;

/// Main decompression struct.
///
#[derive(Clone)]
pub struct DecompressorOxide {
/// Current state of the decompressor.
state: core::State,
Expand Down
64 changes: 64 additions & 0 deletions miniz_oxide/src/inflate/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ impl ResetPolicy for FullReset {

/// A struct that compbines a decompressor with extra data for streaming decompression.
///
#[derive(Clone)]
pub struct InflateState {
/// Inner decompressor struct
decomp: DecompressorOxide,
Expand Down Expand Up @@ -420,4 +421,67 @@ mod test {
// Should still have the checksum read from the header file.
assert_eq!(state.decompressor().adler32_header(), Some(459605011))
}

#[test]
fn test_partial_continue() {
let encoded = [
120u8, 156, 243, 72, 205, 201, 201, 215, 81, 168, 202, 201, 76, 82, 4, 0, 27, 101, 4,
19,
];

// Feed input bytes one at a time to the decompressor
let mut out = vec![0; 50];
let mut state = InflateState::new_boxed(DataFormat::Zlib);
let mut part_in = 0;
let mut part_out = 0;
for i in 1..=encoded.len() {
let res = inflate(&mut state, &encoded[part_in..i], &mut out[part_out..], MZFlush::None);
let status = res.status.expect("Failed to decompress!");
if i == encoded.len() {
assert_eq!(status, MZStatus::StreamEnd);
} else {
assert_eq!(status, MZStatus::Ok);
}
part_out += res.bytes_written as usize;
part_in += res.bytes_consumed;
}

assert_eq!(out[..part_out as usize], b"Hello, zlib!"[..]);
assert_eq!(part_in, encoded.len());
assert_eq!(state.decompressor().adler32(), Some(459605011));
}


// Inflate part of a stream and clone the inflate state.
// Discard the original state and resume the stream from the clone.
#[test]
fn test_rewind_and_resume() {
let encoded = [
120u8, 156, 243, 72, 205, 201, 201, 215, 81, 168, 202, 201, 76, 82, 4, 0, 27, 101, 4,
19,
];
let decoded = b"Hello, zlib!";

// Feed partial input bytes to the decompressor
let mut out = vec![0; 50];
let mut state = InflateState::new_boxed(DataFormat::Zlib);
let res1 = inflate(&mut state, &encoded[..10], &mut out, MZFlush::None);
let status = res1.status.expect("Failed to decompress!");
assert_eq!(status, MZStatus::Ok);

// Clone the state and discard the original
let mut resume = state.clone();
drop(state);

// Resume the stream using the cloned state
let res2 = inflate(&mut resume, &encoded[res1.bytes_consumed..], &mut out[res1.bytes_written..], MZFlush::Finish);
let status = res2.status.expect("Failed to decompress!");
assert_eq!(status, MZStatus::StreamEnd);

assert_eq!(res1.bytes_consumed + res2.bytes_consumed, encoded.len());
assert_eq!(res1.bytes_written + res2.bytes_written, decoded.len());
assert_eq!(&out[..res1.bytes_written + res2.bytes_written as usize], decoded);
assert_eq!(resume.decompressor().adler32(), Some(459605011));
}

}

0 comments on commit 0a33eff

Please sign in to comment.