diff --git a/fuzz/fuzz_targets/inflate.rs b/fuzz/fuzz_targets/inflate.rs index 1f968c1..b2a6ca4 100644 --- a/fuzz/fuzz_targets/inflate.rs +++ b/fuzz/fuzz_targets/inflate.rs @@ -15,7 +15,9 @@ fuzz_target!(|input: &[u8]| { let Ok(decompressed2) = miniz_oxide::inflate::decompress_to_vec_zlib(&input) else {return}; assert_eq!(decompressed, decompressed2); } + Err(fdeflate::DecompressionError::BadCodeLengthHuffmanTree) => {} Err(fdeflate::DecompressionError::BadLiteralLengthHuffmanTree) => {} + Err(fdeflate::DecompressionError::BadDistanceHuffmanTree) => {} Err(fdeflate::DecompressionError::InvalidDistanceCode) => {} Err(err) => match miniz_oxide::inflate::decompress_to_vec_zlib(&input) { Err(r) diff --git a/src/decompress.rs b/src/decompress.rs index f89747e..2aaab94 100644 --- a/src/decompress.rs +++ b/src/decompress.rs @@ -1,8 +1,12 @@ use simd_adler32::Adler32; -use crate::tables::{ - self, CLCL_ORDER, DIST_SYM_TO_DIST_BASE, DIST_SYM_TO_DIST_EXTRA, FDEFLATE_DIST_DECODE_TABLE, - FDEFLATE_LITLEN_DECODE_TABLE, FIXED_CODE_LENGTHS, LEN_SYM_TO_LEN_BASE, LEN_SYM_TO_LEN_EXTRA, +use crate::{ + huffman::{self, build_table}, + tables::{ + self, CLCL_ORDER, DIST_SYM_TO_DIST_BASE, DIST_SYM_TO_DIST_EXTRA, + FDEFLATE_DIST_DECODE_TABLE, FDEFLATE_LITLEN_DECODE_TABLE, FIXED_CODE_LENGTHS, + LEN_SYM_TO_LEN_BASE, LEN_SYM_TO_LEN_EXTRA, LITLEN_TABLE_ENTRIES, + }, }; /// An error encountered while decompressing a deflate stream. @@ -50,40 +54,24 @@ struct BlockHeader { num_lengths_read: usize, /// Low 3-bits are code length code length, high 5-bits are code length code. - table: [u8; 128], + table: [u32; 128], code_lengths: [u8; 320], } -const LITERAL_ENTRY: u32 = 0x8000; -const EXCEPTIONAL_ENTRY: u32 = 0x4000; -const SECONDARY_TABLE_ENTRY: u32 = 0x2000; +pub const LITERAL_ENTRY: u32 = 0x8000; +pub const EXCEPTIONAL_ENTRY: u32 = 0x4000; +pub const SECONDARY_TABLE_ENTRY: u32 = 0x2000; /// The Decompressor state for a compressed block. -/// -/// The main litlen_table uses a 12-bit input to lookup the meaning of the symbol. The table is -/// split into 4 sections: -/// -/// aaaaaaaa_bbbbbbbb_1000yyyy_0000xxxx x = input_advance_bits, y = output_advance_bytes (literal) -/// 0000000z_zzzzzzzz_00000yyy_0000xxxx x = input_advance_bits, y = extra_bits, z = distance_base (length) -/// 00000000_00000000_01000000_0000xxxx x = input_advance_bits (EOF) -/// 0000xxxx_xxxxxxxx_01100000_00000000 x = secondary_table_index -/// 00000000_00000000_01000000_00000000 invalid code -/// -/// The distance table is a 512-entry table that maps 9 bits of distance symbols to their meaning. -/// -/// 00000000_00000000_00000000_00000000 symbol is more than 9 bits -/// zzzzzzzz_zzzzzzzz_0000yyyy_0000xxxx x = input_advance_bits, y = extra_bits, z = distance_base #[repr(align(64))] #[derive(Eq, PartialEq, Debug)] struct CompressedBlock { litlen_table: [u32; 4096], - dist_table: [u32; 512], + secondary_table: Vec, - dist_symbol_lengths: [u8; 30], - dist_symbol_masks: [u16; 30], - dist_symbol_codes: [u16; 30], + dist_table: [u32; 512], + dist_secondary_table: Vec, - secondary_table: Vec, eof_code: u16, eof_mask: u16, eof_bits: u8, @@ -91,19 +79,9 @@ struct CompressedBlock { const FDEFLATE_COMPRESSED_BLOCK: CompressedBlock = CompressedBlock { litlen_table: FDEFLATE_LITLEN_DECODE_TABLE, - dist_table: FDEFLATE_DIST_DECODE_TABLE, - dist_symbol_lengths: [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ], - dist_symbol_masks: [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ], - dist_symbol_codes: [ - 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - ], secondary_table: Vec::new(), + dist_table: FDEFLATE_DIST_DECODE_TABLE, + dist_secondary_table: Vec::new(), eof_code: 0x8ff, eof_mask: 0xfff, eof_bits: 0xc, @@ -158,9 +136,7 @@ impl Decompressor { litlen_table: [0; 4096], dist_table: [0; 512], secondary_table: Vec::new(), - dist_symbol_lengths: [0; 30], - dist_symbol_masks: [0; 30], - dist_symbol_codes: [0xffff; 30], + dist_secondary_table: Vec::new(), eof_code: 0, eof_mask: 0, eof_bits: 0, @@ -245,7 +221,7 @@ 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, 6)?; + Self::build_tables(288, &FIXED_CODE_LENGTHS, &mut self.compression)?; self.state = State::CompressedData; Ok(()) } @@ -293,19 +269,18 @@ impl Decompressor { self.fill_buffer(remaining_input); } } - let code_length_codes: [u16; 19] = crate::compute_codes(&code_length_lengths) - .ok_or(DecompressionError::BadCodeLengthHuffmanTree)?; - - self.header.table = [255; 128]; - for i in 0..19 { - let length = code_length_lengths[i]; - if length > 0 { - let mut j = code_length_codes[i]; - while j < 128 { - self.header.table[j as usize] = ((i as u8) << 3) | length; - j += 1 << length; - } - } + + let mut codes = [0; 19]; + if !build_table( + &code_length_lengths, + &[], + &mut codes, + &mut self.header.table, + &mut Vec::new(), + false, + false, + ) { + return Err(DecompressionError::BadCodeLengthHuffmanTree); } self.state = State::CodeLengths; @@ -323,8 +298,8 @@ impl Decompressor { let code = self.peak_bits(7); let entry = self.header.table[code as usize]; - let length = entry & 0x7; - let symbol = entry >> 3; + let length = (entry & 0x7) as u8; + let symbol = (entry >> 16) as u8; debug_assert!(length != 0); match symbol { @@ -395,7 +370,6 @@ impl Decompressor { self.header.hlit, &self.header.code_lengths, &mut self.compression, - 6, )?; } self.state = State::CompressedData; @@ -406,7 +380,6 @@ impl Decompressor { hlit: usize, code_lengths: &[u8], compression: &mut CompressedBlock, - max_search_bits: u8, ) -> Result<(), DecompressionError> { // If there is no code assigned for the EOF symbol then the bitstream is invalid. if code_lengths[256] == 0 { @@ -414,161 +387,40 @@ impl Decompressor { return Err(DecompressionError::BadLiteralLengthHuffmanTree); } - // Build the literal/length code table. - let lengths = &code_lengths[..288]; - let codes: [u16; 288] = crate::compute_codes(&lengths.try_into().unwrap()) - .ok_or(DecompressionError::BadLiteralLengthHuffmanTree)?; - - let table_bits = lengths.iter().cloned().max().unwrap().min(12).max(6); - let table_size = 1 << table_bits; - - for i in 0..256 { - let code = codes[i]; - let length = lengths[i]; - let mut j = code; - - while j < table_size && length != 0 && length <= 12 { - compression.litlen_table[j as usize] = - ((i as u32) << 16) | LITERAL_ENTRY | (1 << 8) | length as u32; - j += 1 << length; - } - - if length > 0 && length <= max_search_bits { - for ii in 0..256 { - let code2 = codes[ii]; - let length2 = lengths[ii]; - if length2 != 0 && length + length2 <= table_bits { - let mut j = code | (code2 << length); - - while j < table_size { - compression.litlen_table[j as usize] = (ii as u32) << 24 - | (i as u32) << 16 - | LITERAL_ENTRY - | (2 << 8) - | ((length + length2) as u32); - j += 1 << (length + length2); - } - } - } - } - } - - if lengths[256] != 0 && lengths[256] <= 12 { - let mut j = codes[256]; - while j < table_size { - compression.litlen_table[j as usize] = EXCEPTIONAL_ENTRY | lengths[256] as u32; - j += 1 << lengths[256]; - } - } - - let table_size = table_size as usize; - for i in (table_size..4096).step_by(table_size) { - compression.litlen_table.copy_within(0..table_size, i); - } - - compression.eof_code = codes[256]; - compression.eof_mask = (1 << lengths[256]) - 1; - compression.eof_bits = lengths[256]; - - for i in 257..hlit { - let code = codes[i]; - let length = lengths[i]; - if length != 0 && length <= 12 { - let mut j = code; - while j < 4096 { - compression.litlen_table[j as usize] = if i < 286 { - (LEN_SYM_TO_LEN_BASE[i - 257] as u32) << 16 - | (LEN_SYM_TO_LEN_EXTRA[i - 257] as u32) << 8 - | length as u32 - } else { - EXCEPTIONAL_ENTRY - }; - j += 1 << length; - } - } - } - - for i in 0..hlit { - if lengths[i] > 12 { - compression.litlen_table[(codes[i] & 0xfff) as usize] = u32::MAX; - } + let mut codes = [0; 288]; + compression.secondary_table.clear(); + if !huffman::build_table( + &code_lengths[..hlit], + &LITLEN_TABLE_ENTRIES, + &mut codes[..hlit], + &mut compression.litlen_table, + &mut compression.secondary_table, + false, + true, + ) { + return Err(DecompressionError::BadCodeLengthHuffmanTree); } - let mut secondary_table_len = 0; - for i in 0..hlit { - if lengths[i] > 12 { - let j = (codes[i] & 0xfff) as usize; - if compression.litlen_table[j] == u32::MAX { - compression.litlen_table[j] = - (secondary_table_len << 16) | EXCEPTIONAL_ENTRY | SECONDARY_TABLE_ENTRY; - secondary_table_len += 8; - } - } - } - assert!(secondary_table_len <= 0x7ff); - compression.secondary_table = vec![0; secondary_table_len as usize]; - for i in 0..hlit { - let code = codes[i]; - let length = lengths[i]; - if length > 12 { - let j = (codes[i] & 0xfff) as usize; - let k = (compression.litlen_table[j] >> 16) as usize; - - let mut s = code >> 12; - while s < 8 { - debug_assert_eq!(compression.secondary_table[k + s as usize], 0); - compression.secondary_table[k + s as usize] = - ((i as u16) << 4) | (length as u16); - s += 1 << (length - 12); - } - } - } - debug_assert!(compression - .secondary_table - .iter() - .all(|&x| x != 0 && (x & 0xf) > 12)); + compression.eof_code = codes[256] as u16; + compression.eof_mask = (1 << code_lengths[256]) - 1; + compression.eof_bits = code_lengths[256]; // Build the distance code table. let lengths = &code_lengths[288..320]; if lengths == [0; 32] { - compression.dist_symbol_masks = [0; 30]; - compression.dist_symbol_codes = [0xffff; 30]; compression.dist_table.fill(0); } else { - let codes: [u16; 32] = match crate::compute_codes(&lengths.try_into().unwrap()) { - Some(codes) => codes, - None => { - if lengths.iter().filter(|&&l| l != 0).count() != 1 { - return Err(DecompressionError::BadDistanceHuffmanTree); - } - [0; 32] - } - }; - - compression.dist_symbol_codes.copy_from_slice(&codes[..30]); - compression - .dist_symbol_lengths - .copy_from_slice(&lengths[..30]); - compression.dist_table.fill(0); - for i in 0..30 { - let length = lengths[i]; - let code = codes[i]; - if length == 0 { - compression.dist_symbol_masks[i] = 0; - compression.dist_symbol_codes[i] = 0xffff; - } else { - compression.dist_symbol_masks[i] = (1 << lengths[i]) - 1; - if lengths[i] <= 9 { - let mut j = code; - while j < 512 { - compression.dist_table[j as usize] = (DIST_SYM_TO_DIST_BASE[i] as u32) - << 16 - | (DIST_SYM_TO_DIST_EXTRA[i] as u32) << 8 - | length as u32; - j += 1 << lengths[i]; - } - } - } + let mut dist_codes = [0; 32]; + if !huffman::build_table( + lengths, + &tables::DISTANCE_TABLE_ENTRIES, + &mut dist_codes, + &mut compression.dist_table, + &mut compression.dist_secondary_table, + true, + false, + ) { + return Err(DecompressionError::BadDistanceHuffmanTree); } } @@ -692,9 +544,10 @@ impl Decompressor { litlen_code_bits, ) } else if litlen_entry & SECONDARY_TABLE_ENTRY != 0 { - let secondary_index = litlen_entry >> 16; - let secondary_entry = self.compression.secondary_table - [secondary_index as usize + ((bits >> 12) & 0x7) as usize]; + let secondary_table_index = + (litlen_entry >> 16) + ((bits >> 12) as u32 & (litlen_entry & 0xff)); + let secondary_entry = + self.compression.secondary_table[secondary_table_index as usize]; let litlen_symbol = secondary_entry >> 4; let litlen_code_bits = (secondary_entry & 0xf) as u8; @@ -743,30 +596,31 @@ impl Decompressor { bits >>= length_extra_bits; let dist_entry = self.compression.dist_table[(bits & 0x1ff) as usize]; - let (dist_base, dist_extra_bits, dist_code_bits) = if dist_entry != 0 { + let (dist_base, dist_extra_bits, dist_code_bits) = if dist_entry & LITERAL_ENTRY != 0 { ( (dist_entry >> 16) as u16, - (dist_entry >> 8) as u8, + (dist_entry >> 8) as u8 & 0xf, dist_entry as u8, ) } else if self.nbits > litlen_code_bits + length_extra_bits + 9 { - let mut dist_extra_bits = 0; - let mut dist_base = 0; - let mut dist_advance_bits = 0; - for i in 0..self.compression.dist_symbol_lengths.len() { - if bits as u16 & self.compression.dist_symbol_masks[i] - == self.compression.dist_symbol_codes[i] - { - dist_extra_bits = DIST_SYM_TO_DIST_EXTRA[i]; - dist_base = DIST_SYM_TO_DIST_BASE[i]; - dist_advance_bits = self.compression.dist_symbol_lengths[i]; - break; - } + if dist_entry >> 8 == 0 { + return Err(DecompressionError::InvalidDistanceCode); } - if dist_advance_bits == 0 { + + let secondary_table_index = + (dist_entry >> 16) + ((bits >> 9) as u32 & (dist_entry & 0xff)); + let secondary_entry = + self.compression.dist_secondary_table[secondary_table_index as usize]; + let dist_symbol = (secondary_entry >> 4) as usize; + if dist_symbol >= 30 { return Err(DecompressionError::InvalidDistanceCode); } - (dist_base, dist_extra_bits, dist_advance_bits) + + ( + DIST_SYM_TO_DIST_BASE[dist_symbol], + DIST_SYM_TO_DIST_EXTRA[dist_symbol], + (secondary_entry & 0xf) as u8, + ) } else { break; }; @@ -1149,10 +1003,8 @@ mod tests { let mut compression = CompressedBlock { litlen_table: [0; 4096], dist_table: [0; 512], - dist_symbol_lengths: [0; 30], - dist_symbol_masks: [0; 30], - dist_symbol_codes: [0; 30], secondary_table: Vec::new(), + dist_secondary_table: Vec::new(), eof_code: 0, eof_mask: 0, eof_bits: 0, @@ -1161,7 +1013,7 @@ mod tests { lengths.resize(288, 0); lengths.push(1); lengths.resize(320, 0); - Decompressor::build_tables(286, &lengths, &mut compression, 11).unwrap(); + Decompressor::build_tables(286, &lengths, &mut compression).unwrap(); assert_eq!( compression, FDEFLATE_COMPRESSED_BLOCK, diff --git a/src/huffman.rs b/src/huffman.rs new file mode 100644 index 0000000..a635e93 --- /dev/null +++ b/src/huffman.rs @@ -0,0 +1,181 @@ +use crate::decompress::{EXCEPTIONAL_ENTRY, LITERAL_ENTRY, SECONDARY_TABLE_ENTRY}; + +/// Return the next code, or if the codeword is already all ones (which is the final code), return +/// the same code again. +fn next_codeword(mut codeword: u16, table_size: u16) -> u16 { + if codeword == table_size - 1 { + return codeword; + } + + let adv = (u16::BITS - 1) - (codeword ^ (table_size - 1)).leading_zeros(); + let bit = 1 << adv; + codeword &= bit - 1; + codeword |= bit; + codeword +} + +pub fn build_table( + lengths: &[u8], + entries: &[u32], + codes: &mut [u16], + primary_table: &mut [u32], + secondary_table: &mut Vec, + is_distance_table: bool, + double_literal: bool, +) -> bool { + // Count the number of symbols with each code length. + let mut histogram = [0; 16]; + for &length in lengths { + histogram[length as usize] += 1; + } + + // Determine the maximum code length. + let mut max_length = 15; + while max_length > 1 && histogram[max_length] == 0 { + max_length -= 1; + } + + // Handle zero and one symbol huffman codes (which are only allowed for distance codes). + if is_distance_table { + if max_length == 0 { + primary_table.fill(0); + secondary_table.clear(); + return true; + } else if max_length == 1 && histogram[1] == 1 { + let symbol = lengths.iter().position(|&l| l == 1).unwrap(); + codes[symbol] = 0; + let entry = entries + .get(symbol) + .cloned() + .unwrap_or((symbol as u32) << 16) + | 1; + for chunk in primary_table.chunks_mut(2) { + chunk[0] = entry; + chunk[1] = 0; + } + return true; + } + } + + // Sort symbols by code length. Given the histogram, we can determine the starting offset + // for each code length. + let mut offsets = [0; 16]; + let mut codespace_used = 0; + offsets[1] = histogram[0]; + for i in 1..max_length { + offsets[i + 1] = offsets[i] + histogram[i]; + codespace_used = (codespace_used << 1) + histogram[i]; + } + codespace_used = (codespace_used << 1) + histogram[max_length]; + + // Check that the provided lengths form a valid Huffman tree. + if codespace_used != (1 << max_length) { + return false; + } + + // Sort the symbols by code length. + let mut next_index = offsets; + let mut sorted_symbols = [0; 288]; + for symbol in 0..lengths.len() { + let length = lengths[symbol]; + sorted_symbols[next_index[length as usize]] = symbol; + next_index[length as usize] += 1; + } + + let mut codeword = 0u16; + let mut i = histogram[0]; + + // Populate the primary decoding table + let primary_table_bits = primary_table.len().ilog2() as usize; + let primary_table_mask = (1 << primary_table_bits) - 1; + for length in 1..=primary_table_bits { + let current_table_end = 1 << length; + + // Loop over all symbols with the current code length and set their table entries. + for _ in 0..histogram[length] { + let symbol = sorted_symbols[i]; + i += 1; + + primary_table[codeword as usize] = entries + .get(symbol) + .cloned() + .unwrap_or((symbol as u32) << 16) + | length as u32; + + codes[symbol] = codeword; + codeword = next_codeword(codeword, current_table_end as u16); + } + + if double_literal { + for len1 in 1..(length - 1) { + let len2 = length - len1; + for sym1_index in offsets[len1]..next_index[len1] { + for sym2_index in offsets[len2]..next_index[len2] { + let sym1 = sorted_symbols[sym1_index]; + let sym2 = sorted_symbols[sym2_index]; + if sym1 < 256 && sym2 < 256 { + let codeword1 = codes[sym1]; + let codeword2 = codes[sym2]; + let codeword = codeword1 | (codeword2 << len1); + let entry = (sym1 as u32) << 16 + | (sym2 as u32) << 24 + | LITERAL_ENTRY + | (2 << 8); + primary_table[codeword as usize] = entry | (length as u32); + } + } + } + } + } + + // If we aren't at the maximum table size, double the size of the table. + if length < primary_table_bits { + primary_table.copy_within(0..current_table_end, current_table_end); + } + } + + // Populate the secondary decoding table. + secondary_table.clear(); + if max_length > primary_table_bits { + let mut subtable_start = 0; + let mut subtable_prefix = !0; + for length in (primary_table_bits + 1)..=max_length { + let subtable_size = 1 << (length - primary_table_bits); + for _ in 0..histogram[length] { + // If the codeword's prefix doesn't match the current subtable, create a new + // subtable. + if codeword & primary_table_mask != subtable_prefix { + subtable_prefix = codeword & primary_table_mask; + subtable_start = secondary_table.len(); + primary_table[subtable_prefix as usize] = ((subtable_start as u32) << 16) + | EXCEPTIONAL_ENTRY + | SECONDARY_TABLE_ENTRY + | (subtable_size as u32 - 1); + secondary_table.resize(subtable_start + subtable_size, 0); + } + + // Lookup the symbol. + let symbol = sorted_symbols[i]; + i += 1; + + // Insert the symbol into the secondary table and advance to the next codeword. + codes[symbol] = codeword; + secondary_table[subtable_start + (codeword >> primary_table_bits) as usize] = + ((symbol as u16) << 4) | (length as u16); + codeword = next_codeword(codeword, 1 << length); + } + + // If there are more codes with the same subtable prefix, extend the subtable. + if length < max_length && codeword & primary_table_mask == subtable_prefix { + secondary_table.extend_from_within(subtable_start..); + let subtable_size = secondary_table.len() - subtable_start; + primary_table[subtable_prefix as usize] = ((subtable_start as u32) << 16) + | EXCEPTIONAL_ENTRY + | SECONDARY_TABLE_ENTRY + | (subtable_size as u32 - 1); + } + } + } + + true +} diff --git a/src/lib.rs b/src/lib.rs index 569c872..e273699 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,7 @@ mod compress; mod decompress; +mod huffman; mod tables; pub use compress::{compress_to_vec, Compressor, StoredOnlyCompressor}; diff --git a/src/tables.rs b/src/tables.rs index 8b233c8..bf038fd 100644 --- a/src/tables.rs +++ b/src/tables.rs @@ -1,3 +1,5 @@ +use crate::decompress::{EXCEPTIONAL_ENTRY, LITERAL_ENTRY}; + /// Hard-coded Huffman codes used regardless of the input. /// /// These values work well for PNGs with some form of filtering enabled, but will likely make most @@ -85,6 +87,47 @@ pub(crate) const DIST_SYM_TO_DIST_BASE: [u16; 30] = [ 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, ]; +/// The main litlen_table uses a 12-bit input to lookup the meaning of the symbol. The table is +/// split into 4 sections: +/// +/// aaaaaaaa_bbbbbbbb_1000yyyy_0000xxxx x = input_advance_bits, y = output_advance_bytes (literal) +/// 0000000z_zzzzzzzz_00000yyy_0000xxxx x = input_advance_bits, y = extra_bits, z = distance_base (length) +/// 00000000_00000000_01000000_0000xxxx x = input_advance_bits (EOF) +/// 0000xxxx_xxxxxxxx_01100000_00000000 x = secondary_table_index +/// 00000000_00000000_01000000_00000000 invalid code +pub(crate) const LITLEN_TABLE_ENTRIES: [u32; 288] = { + let mut entries = [EXCEPTIONAL_ENTRY; 288]; + let mut i = 0; + while i < 256 { + entries[i] = (i as u32) << 16 | LITERAL_ENTRY | (1 << 8); + i += 1; + } + + let mut i = 257; + while i < 286 { + entries[i] = (LEN_SYM_TO_LEN_BASE[i - 257] as u32) << 16 + | (LEN_SYM_TO_LEN_EXTRA[i - 257] as u32) << 8; + i += 1; + } + entries +}; + +/// The distance table is a 512-entry table that maps 9 bits of distance symbols to their meaning. +/// +/// 00000000_00000000_00000000_00000000 symbol is more than 9 bits +/// zzzzzzzz_zzzzzzzz_0000yyyy_0000xxxx x = input_advance_bits, y = extra_bits, z = distance_base +pub(crate) const DISTANCE_TABLE_ENTRIES: [u32; 32] = { + let mut entries = [0; 32]; + let mut i = 0; + while i < 30 { + entries[i] = (DIST_SYM_TO_DIST_BASE[i] as u32) << 16 + | (DIST_SYM_TO_DIST_EXTRA[i] as u32) << 8 + | LITERAL_ENTRY; + i += 1; + } + entries +}; + pub(crate) const FDEFLATE_LITLEN_DECODE_TABLE: [u32; 4096] = [ 0x8204, 0x28206, 0x18205, 0xfa8208, 0x2008206, 0x38207, 0xff8205, 0xf4820a, 0x1008205, 0xfe8206, 0x2018207, 0x98209, 0xfa008208, 0xfd8207, 0x2ff8207, 0xb010a, 0x8204, 0x2028208, @@ -565,46 +608,46 @@ pub(crate) const FDEFLATE_LITLEN_DECODE_TABLE: [u32; 4096] = [ ]; pub(crate) const FDEFLATE_DIST_DECODE_TABLE: [u32; 512] = [ - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, - 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, 0x0, 0x10001, - 0x0, 0x10001, 0x0, 0x10001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, + 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, 0x0, 0x18001, + 0x0, 0x18001, 0x0, 0x18001, 0x0, ]; pub(crate) const FIXED_CODE_LENGTHS: [u8; 320] = make_fixed_code_lengths();