diff --git a/test-libz-rs-sys/examples/precompute.rs b/test-libz-rs-sys/examples/precompute.rs new file mode 100644 index 00000000..78173c8a --- /dev/null +++ b/test-libz-rs-sys/examples/precompute.rs @@ -0,0 +1,18 @@ +use zlib_rs::deflate::encode_static_len; + +// Program to compute the lookup table STATIC_LENGTH_ENCODINGS +fn main() { + const COLUMNS: usize = 4; + let mut column: usize = 0; + for lc in 0..256 { + let (encoding, len) = encode_static_len(lc as u8); + print!("h({:>4}, {:>2}), ", encoding, len); + column += 1; + if column == COLUMNS { + println!(""); + column = 0; + } else { + print!(" "); + } + } +} diff --git a/zlib-rs/src/deflate.rs b/zlib-rs/src/deflate.rs index 4055e90f..e998f213 100644 --- a/zlib-rs/src/deflate.rs +++ b/zlib-rs/src/deflate.rs @@ -893,6 +893,36 @@ struct BitWriter<'a> { bits_sent: usize, } +#[inline] +fn encode_len( + ltree: &[Value], + lc: u8 +) -> (u64, usize) { + let mut lc = lc as usize; + + /* Send the length code, len is the match length - STD_MIN_MATCH */ + let code = self::trees_tbl::LENGTH_CODE[lc] as usize; + let c = code + LITERALS + 1; + assert!(c < L_CODES, "bad l_code"); + // send_code_trace(s, c); + + let lnode = ltree[c]; + let mut match_bits: u64 = lnode.code() as u64; + let mut match_bits_len = lnode.len() as usize; + let extra = StaticTreeDesc::EXTRA_LBITS[code] as usize; + if extra != 0 { + lc -= self::trees_tbl::BASE_LENGTH[code] as usize; + match_bits |= (lc as u64) << match_bits_len; + match_bits_len += extra; + } + + (match_bits, match_bits_len) +} + +pub fn encode_static_len(lc: u8) -> (u64, usize) { + encode_len(&STATIC_LTREE, lc) +} + impl<'a> BitWriter<'a> { pub(crate) const BIT_BUF_SIZE: u8 = 64; @@ -1055,26 +1085,41 @@ impl<'a> BitWriter<'a> { lc: u8, mut dist: usize, ) -> usize { - let mut lc = lc as usize; - - /* Send the length code, len is the match length - STD_MIN_MATCH */ - let mut code = self::trees_tbl::LENGTH_CODE[lc] as usize; - let c = code + LITERALS + 1; - assert!(c < L_CODES, "bad l_code"); - // send_code_trace(s, c); - - let lnode = ltree[c]; - let mut match_bits: u64 = lnode.code() as u64; - let mut match_bits_len = lnode.len() as usize; - let mut extra = StaticTreeDesc::EXTRA_LBITS[code] as usize; + let (mut match_bits, mut match_bits_len) = encode_len(ltree, lc); + + dist -= 1; /* dist is now the match distance - 1 */ + let code = State::d_code(dist) as usize; + assert!(code < D_CODES, "bad d_code"); + // send_code_trace(s, code); + + /* Send the distance code */ + let dnode = dtree[code]; + match_bits |= (dnode.code() as u64) << match_bits_len; + match_bits_len += dnode.len() as usize; + let extra = StaticTreeDesc::EXTRA_DBITS[code] as usize; if extra != 0 { - lc -= self::trees_tbl::BASE_LENGTH[code] as usize; - match_bits |= (lc as u64) << match_bits_len; + dist -= self::trees_tbl::BASE_DIST[code] as usize; + match_bits |= (dist as u64) << match_bits_len; match_bits_len += extra; } + self.send_bits(match_bits, match_bits_len as u8); + + match_bits_len + } + + pub(crate) fn emit_dist_static( + &mut self, + dtree: &[Value], + lc: u8, + mut dist: usize, + ) -> usize { + let precomputed_len = trees_tbl::STATIC_LENGTH_ENCODINGS[lc as usize]; + let mut match_bits = precomputed_len.a as u64; + let mut match_bits_len = precomputed_len.b as usize; + dist -= 1; /* dist is now the match distance - 1 */ - code = State::d_code(dist) as usize; + let code = State::d_code(dist) as usize; assert!(code < D_CODES, "bad d_code"); // send_code_trace(s, code); @@ -1082,7 +1127,7 @@ impl<'a> BitWriter<'a> { let dnode = dtree[code]; match_bits |= (dnode.code() as u64) << match_bits_len; match_bits_len += dnode.len() as usize; - extra = StaticTreeDesc::EXTRA_DBITS[code] as usize; + let extra = StaticTreeDesc::EXTRA_DBITS[code] as usize; if extra != 0 { dist -= self::trees_tbl::BASE_DIST[code] as usize; match_bits |= (dist as u64) << match_bits_len; @@ -1100,7 +1145,7 @@ impl<'a> BitWriter<'a> { unreachable!("out of bound access on the symbol buffer"); }; - match u16::from_be_bytes([dist_high, dist_low]) as usize { + match u16::from_le_bytes([dist_low, dist_high]) as usize { 0 => self.emit_lit(ltree, lc) as usize, dist => self.emit_dist(ltree, dtree, lc, dist), }; @@ -1458,11 +1503,22 @@ impl<'a> State<'a> { } fn compress_block_static_trees(&mut self) { - self.bit_writer.compress_block_help( - self.sym_buf.filled(), - self::trees_tbl::STATIC_LTREE.as_slice(), - self::trees_tbl::STATIC_DTREE.as_slice(), - ) + let ltree = self::trees_tbl::STATIC_LTREE.as_slice(); + let dtree = self::trees_tbl::STATIC_DTREE.as_slice(); + for chunk in self.sym_buf.filled().chunks_exact(3) { + let [dist_low, dist_high, lc] = *chunk else { + unreachable!("out of bound access on the symbol buffer"); + }; + + //match u16::from_le_bytes([dist_low, dist_high]) as usize { + match u16::from_le_bytes([dist_low, dist_high]) as usize { + 0 => self.bit_writer.emit_lit(ltree, lc) as usize, + dist => self.bit_writer.emit_dist_static(dtree, lc, dist), + }; + } + + self.bit_writer.emit_end_block(ltree, false) + } fn compress_block_dynamic_trees(&mut self) { diff --git a/zlib-rs/src/deflate/algorithm/quick.rs b/zlib-rs/src/deflate/algorithm/quick.rs index d7bee734..35b1a26d 100644 --- a/zlib-rs/src/deflate/algorithm/quick.rs +++ b/zlib-rs/src/deflate/algorithm/quick.rs @@ -118,8 +118,7 @@ pub fn deflate_quick(stream: &mut DeflateStream, flush: DeflateFlush) -> BlockSt // TODO do this with a debug_assert? // check_match(s, state.strstart, hash_head, match_len); - state.bit_writer.emit_dist( - StaticTreeDesc::L.static_tree, + state.bit_writer.emit_dist_static( StaticTreeDesc::D.static_tree, (match_len - STD_MIN_MATCH) as u8, dist as usize, diff --git a/zlib-rs/src/deflate/trees_tbl.rs b/zlib-rs/src/deflate/trees_tbl.rs index 07abb66b..9bbc7021 100644 --- a/zlib-rs/src/deflate/trees_tbl.rs +++ b/zlib-rs/src/deflate/trees_tbl.rs @@ -70,6 +70,76 @@ pub const STATIC_LTREE: [Value; L_CODES + 2] = [ h(163,8), h( 99,8), h(227,8) ]; +// precomputed lengths (as would be generated by the encode_len function) +// for all possible u8 values using STATIC_LTREE +#[rustfmt::skip] +pub const STATIC_LENGTH_ENCODINGS: [Value; 256] = [ + h( 64, 7), h( 32, 7), h( 96, 7), h( 16, 7), + h( 80, 7), h( 48, 7), h( 112, 7), h( 8, 7), + h( 72, 8), h( 200, 8), h( 40, 8), h( 168, 8), + h( 104, 8), h( 232, 8), h( 24, 8), h( 152, 8), + h( 88, 9), h( 216, 9), h( 344, 9), h( 472, 9), + h( 56, 9), h( 184, 9), h( 312, 9), h( 440, 9), + h( 120, 9), h( 248, 9), h( 376, 9), h( 504, 9), + h( 4, 9), h( 132, 9), h( 260, 9), h( 388, 9), + h( 68, 10), h( 196, 10), h( 324, 10), h( 452, 10), + h( 580, 10), h( 708, 10), h( 836, 10), h( 964, 10), + h( 36, 10), h( 164, 10), h( 292, 10), h( 420, 10), + h( 548, 10), h( 676, 10), h( 804, 10), h( 932, 10), + h( 100, 10), h( 228, 10), h( 356, 10), h( 484, 10), + h( 612, 10), h( 740, 10), h( 868, 10), h( 996, 10), + h( 20, 10), h( 148, 10), h( 276, 10), h( 404, 10), + h( 532, 10), h( 660, 10), h( 788, 10), h( 916, 10), + h( 84, 11), h( 212, 11), h( 340, 11), h( 468, 11), + h( 596, 11), h( 724, 11), h( 852, 11), h( 980, 11), + h(1108, 11), h(1236, 11), h(1364, 11), h(1492, 11), + h(1620, 11), h(1748, 11), h(1876, 11), h(2004, 11), + h( 52, 11), h( 180, 11), h( 308, 11), h( 436, 11), + h( 564, 11), h( 692, 11), h( 820, 11), h( 948, 11), + h(1076, 11), h(1204, 11), h(1332, 11), h(1460, 11), + h(1588, 11), h(1716, 11), h(1844, 11), h(1972, 11), + h( 116, 11), h( 244, 11), h( 372, 11), h( 500, 11), + h( 628, 11), h( 756, 11), h( 884, 11), h(1012, 11), + h(1140, 11), h(1268, 11), h(1396, 11), h(1524, 11), + h(1652, 11), h(1780, 11), h(1908, 11), h(2036, 11), + h( 3, 12), h( 259, 12), h( 515, 12), h( 771, 12), + h(1027, 12), h(1283, 12), h(1539, 12), h(1795, 12), + h(2051, 12), h(2307, 12), h(2563, 12), h(2819, 12), + h(3075, 12), h(3331, 12), h(3587, 12), h(3843, 12), + h( 131, 13), h( 387, 13), h( 643, 13), h( 899, 13), + h(1155, 13), h(1411, 13), h(1667, 13), h(1923, 13), + h(2179, 13), h(2435, 13), h(2691, 13), h(2947, 13), + h(3203, 13), h(3459, 13), h(3715, 13), h(3971, 13), + h(4227, 13), h(4483, 13), h(4739, 13), h(4995, 13), + h(5251, 13), h(5507, 13), h(5763, 13), h(6019, 13), + h(6275, 13), h(6531, 13), h(6787, 13), h(7043, 13), + h(7299, 13), h(7555, 13), h(7811, 13), h(8067, 13), + h( 67, 13), h( 323, 13), h( 579, 13), h( 835, 13), + h(1091, 13), h(1347, 13), h(1603, 13), h(1859, 13), + h(2115, 13), h(2371, 13), h(2627, 13), h(2883, 13), + h(3139, 13), h(3395, 13), h(3651, 13), h(3907, 13), + h(4163, 13), h(4419, 13), h(4675, 13), h(4931, 13), + h(5187, 13), h(5443, 13), h(5699, 13), h(5955, 13), + h(6211, 13), h(6467, 13), h(6723, 13), h(6979, 13), + h(7235, 13), h(7491, 13), h(7747, 13), h(8003, 13), + h( 195, 13), h( 451, 13), h( 707, 13), h( 963, 13), + h(1219, 13), h(1475, 13), h(1731, 13), h(1987, 13), + h(2243, 13), h(2499, 13), h(2755, 13), h(3011, 13), + h(3267, 13), h(3523, 13), h(3779, 13), h(4035, 13), + h(4291, 13), h(4547, 13), h(4803, 13), h(5059, 13), + h(5315, 13), h(5571, 13), h(5827, 13), h(6083, 13), + h(6339, 13), h(6595, 13), h(6851, 13), h(7107, 13), + h(7363, 13), h(7619, 13), h(7875, 13), h(8131, 13), + h( 35, 13), h( 291, 13), h( 547, 13), h( 803, 13), + h(1059, 13), h(1315, 13), h(1571, 13), h(1827, 13), + h(2083, 13), h(2339, 13), h(2595, 13), h(2851, 13), + h(3107, 13), h(3363, 13), h(3619, 13), h(3875, 13), + h(4131, 13), h(4387, 13), h(4643, 13), h(4899, 13), + h(5155, 13), h(5411, 13), h(5667, 13), h(5923, 13), + h(6179, 13), h(6435, 13), h(6691, 13), h(6947, 13), + h(7203, 13), h(7459, 13), h(7715, 13), h( 163, 8) +]; + #[rustfmt::skip] pub const STATIC_DTREE: [Value; D_CODES] = [ h( 0,5), h(16,5), h( 8,5), h(24,5), h( 4,5),