From ce54720c8b93e032f3e77e8f05bf9f9870b49df0 Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Wed, 27 Nov 2024 01:20:17 -0800 Subject: [PATCH 1/3] Use use static block table between consecutive static blocks --- src/decompress.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/decompress.rs b/src/decompress.rs index 43f50f3..825c9b9 100644 --- a/src/decompress.rs +++ b/src/decompress.rs @@ -103,6 +103,7 @@ pub struct Decompressor { queued_rle: Option<(u8, usize)>, queued_backref: Option<(usize, usize)>, last_block: bool, + static_table: bool, state: State, checksum: Adler32, @@ -145,6 +146,7 @@ impl Decompressor { state: State::ZlibHeader, last_block: false, ignore_adler32: false, + static_table: false, } } @@ -209,8 +211,10 @@ impl Decompressor { } 0b01 => { self.consume_bits(3); - // TODO: Do this statically rather than every time. - Self::build_tables(288, &FIXED_CODE_LENGTHS, &mut self.compression)?; + if !self.static_table { + self.static_table = true; + Self::build_tables(288, &FIXED_CODE_LENGTHS, &mut self.compression)?; + } self.state = State::CompressedData; Ok(()) } @@ -231,6 +235,7 @@ impl Decompressor { self.consume_bits(17); self.state = State::CodeLengthCodes; + self.static_table = false; Ok(()) } 0b11 => Err(DecompressionError::InvalidBlockType), From 1b2d66dee63a44d9a093bd896c1fc476275f3ecd Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Thu, 28 Nov 2024 13:00:32 -0800 Subject: [PATCH 2/3] Detect empty blocks --- src/decompress.rs | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/decompress.rs b/src/decompress.rs index 825c9b9..b804132 100644 --- a/src/decompress.rs +++ b/src/decompress.rs @@ -103,7 +103,7 @@ pub struct Decompressor { queued_rle: Option<(u8, usize)>, queued_backref: Option<(usize, usize)>, last_block: bool, - static_table: bool, + fixed_table: bool, state: State, checksum: Adler32, @@ -146,7 +146,7 @@ impl Decompressor { state: State::ZlibHeader, last_block: false, ignore_adler32: false, - static_table: false, + fixed_table: false, } } @@ -184,7 +184,7 @@ impl Decompressor { fn read_block_header(&mut self, remaining_input: &mut &[u8]) -> Result<(), DecompressionError> { self.fill_buffer(remaining_input); - if self.nbits < 3 { + if self.nbits < 10 { return Ok(()); } @@ -211,10 +211,35 @@ impl Decompressor { } 0b01 => { self.consume_bits(3); - if !self.static_table { - self.static_table = true; + + // Check for an entirely empty blocks which can happen if there are "partial + // flushes" in the deflate stream). With fixed huffman codes, the EOF symbol is + // 7-bits of zeros so we peak ahead and see if the next 7-bits are all zero. + if self.peak_bits(7) == 0 { + self.consume_bits(7); + if self.last_block { + self.state = State::Checksum; + return Ok(()); + } + + // At this point we've consumed the entire block and need to read the next block + // header. If tail call optimization were guaranteed, we could just recurse + // here. But without it, a long sequence of empty fixed-blocks might cause a + // stack overflow. Instead, we consume all empty blocks in a loop and then + // recurse. This is the only recursive call this function, and thus is safe. + while self.nbits >= 10 && self.peak_bits(10) == 0b010 { + self.consume_bits(10); + self.fill_buffer(remaining_input); + } + return self.read_block_header(remaining_input); + } + + // Build decoding tables if the previous block wasn't also a fixed block. + if !self.fixed_table { + self.fixed_table = true; Self::build_tables(288, &FIXED_CODE_LENGTHS, &mut self.compression)?; } + self.state = State::CompressedData; Ok(()) } @@ -235,7 +260,7 @@ impl Decompressor { self.consume_bits(17); self.state = State::CodeLengthCodes; - self.static_table = false; + self.fixed_table = false; Ok(()) } 0b11 => Err(DecompressionError::InvalidBlockType), From 5c204e1e126ebd191eeebee878947a7dfc6eb4ee Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Thu, 28 Nov 2024 17:16:38 -0800 Subject: [PATCH 3/3] typo --- src/decompress.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/decompress.rs b/src/decompress.rs index b804132..f5ff29c 100644 --- a/src/decompress.rs +++ b/src/decompress.rs @@ -213,7 +213,7 @@ impl Decompressor { self.consume_bits(3); // Check for an entirely empty blocks which can happen if there are "partial - // flushes" in the deflate stream). With fixed huffman codes, the EOF symbol is + // flushes" in the deflate stream. With fixed huffman codes, the EOF symbol is // 7-bits of zeros so we peak ahead and see if the next 7-bits are all zero. if self.peak_bits(7) == 0 { self.consume_bits(7);