Skip to content

Commit 0a33eff

Browse files
authored
Derive Clone for InflateState to allow random-access reads (#157)
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.
1 parent 868c27a commit 0a33eff

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

miniz_oxide/src/inflate/core.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use self::output_buffer::OutputBuffer;
1212
pub const TINFL_LZ_DICT_SIZE: usize = 32_768;
1313

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

167168
/// Main decompression struct.
168169
///
170+
#[derive(Clone)]
169171
pub struct DecompressorOxide {
170172
/// Current state of the decompressor.
171173
state: core::State,

miniz_oxide/src/inflate/stream.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ impl ResetPolicy for FullReset {
5757

5858
/// A struct that compbines a decompressor with extra data for streaming decompression.
5959
///
60+
#[derive(Clone)]
6061
pub struct InflateState {
6162
/// Inner decompressor struct
6263
decomp: DecompressorOxide,
@@ -420,4 +421,67 @@ mod test {
420421
// Should still have the checksum read from the header file.
421422
assert_eq!(state.decompressor().adler32_header(), Some(459605011))
422423
}
424+
425+
#[test]
426+
fn test_partial_continue() {
427+
let encoded = [
428+
120u8, 156, 243, 72, 205, 201, 201, 215, 81, 168, 202, 201, 76, 82, 4, 0, 27, 101, 4,
429+
19,
430+
];
431+
432+
// Feed input bytes one at a time to the decompressor
433+
let mut out = vec![0; 50];
434+
let mut state = InflateState::new_boxed(DataFormat::Zlib);
435+
let mut part_in = 0;
436+
let mut part_out = 0;
437+
for i in 1..=encoded.len() {
438+
let res = inflate(&mut state, &encoded[part_in..i], &mut out[part_out..], MZFlush::None);
439+
let status = res.status.expect("Failed to decompress!");
440+
if i == encoded.len() {
441+
assert_eq!(status, MZStatus::StreamEnd);
442+
} else {
443+
assert_eq!(status, MZStatus::Ok);
444+
}
445+
part_out += res.bytes_written as usize;
446+
part_in += res.bytes_consumed;
447+
}
448+
449+
assert_eq!(out[..part_out as usize], b"Hello, zlib!"[..]);
450+
assert_eq!(part_in, encoded.len());
451+
assert_eq!(state.decompressor().adler32(), Some(459605011));
452+
}
453+
454+
455+
// Inflate part of a stream and clone the inflate state.
456+
// Discard the original state and resume the stream from the clone.
457+
#[test]
458+
fn test_rewind_and_resume() {
459+
let encoded = [
460+
120u8, 156, 243, 72, 205, 201, 201, 215, 81, 168, 202, 201, 76, 82, 4, 0, 27, 101, 4,
461+
19,
462+
];
463+
let decoded = b"Hello, zlib!";
464+
465+
// Feed partial input bytes to the decompressor
466+
let mut out = vec![0; 50];
467+
let mut state = InflateState::new_boxed(DataFormat::Zlib);
468+
let res1 = inflate(&mut state, &encoded[..10], &mut out, MZFlush::None);
469+
let status = res1.status.expect("Failed to decompress!");
470+
assert_eq!(status, MZStatus::Ok);
471+
472+
// Clone the state and discard the original
473+
let mut resume = state.clone();
474+
drop(state);
475+
476+
// Resume the stream using the cloned state
477+
let res2 = inflate(&mut resume, &encoded[res1.bytes_consumed..], &mut out[res1.bytes_written..], MZFlush::Finish);
478+
let status = res2.status.expect("Failed to decompress!");
479+
assert_eq!(status, MZStatus::StreamEnd);
480+
481+
assert_eq!(res1.bytes_consumed + res2.bytes_consumed, encoded.len());
482+
assert_eq!(res1.bytes_written + res2.bytes_written, decoded.len());
483+
assert_eq!(&out[..res1.bytes_written + res2.bytes_written as usize], decoded);
484+
assert_eq!(resume.decompressor().adler32(), Some(459605011));
485+
}
486+
423487
}

0 commit comments

Comments
 (0)