Skip to content

Commit

Permalink
Speed up emit_dist with precomputed length codings for static trees
Browse files Browse the repository at this point in the history
  • Loading branch information
brianpane committed Dec 14, 2024
1 parent 4dda12a commit 2a1bcec
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 24 deletions.
18 changes: 18 additions & 0 deletions test-libz-rs-sys/examples/precompute.rs
Original file line number Diff line number Diff line change
@@ -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!(" ");
}
}
}
100 changes: 78 additions & 22 deletions zlib-rs/src/deflate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -1055,34 +1085,49 @@ 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);

/* Send the distance code */
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;
Expand All @@ -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),
};
Expand Down Expand Up @@ -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) {
Expand Down
3 changes: 1 addition & 2 deletions zlib-rs/src/deflate/algorithm/quick.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
70 changes: 70 additions & 0 deletions zlib-rs/src/deflate/trees_tbl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down

0 comments on commit 2a1bcec

Please sign in to comment.