1#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
6pub enum Strategy {
7 Fast,
8 DFast,
9 Greedy,
10 Lazy,
11 Lazy2,
12 BtLazy2,
13 BtOpt,
14 BtUltra,
15 BtUltra2,
16}
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub struct CParams {
21 pub window_log: u32,
22 pub chain_log: u32,
23 pub hash_log: u32,
24 pub search_log: u32,
25 pub min_match: u32,
26 pub target_length: u32,
27 pub strategy: Strategy,
28}
29
30use Strategy::*;
31type Row = (u32, u32, u32, u32, u32, u32, Strategy);
32
33#[rustfmt::skip]
34const TABLES: [[Row; 23]; 4] = [
35 [(19,12,13,1,6,1,Fast),(19,13,14,1,7,0,Fast),(20,15,16,1,6,0,Fast),(21,16,17,1,5,0,DFast),(21,18,18,1,5,0,DFast),
36 (21,18,19,3,5,2,Greedy),(21,18,19,3,5,4,Lazy),(21,19,20,4,5,8,Lazy),(21,19,20,4,5,16,Lazy2),(22,20,21,4,5,16,Lazy2),
37 (22,21,22,5,5,16,Lazy2),(22,21,22,6,5,16,Lazy2),(22,22,23,6,5,32,Lazy2),(22,22,22,4,5,32,BtLazy2),(22,22,23,5,5,32,BtLazy2),
38 (22,23,23,6,5,32,BtLazy2),(22,22,22,5,5,48,BtOpt),(23,23,22,5,4,64,BtOpt),(23,23,22,6,3,64,BtUltra),(23,24,22,7,3,256,BtUltra2),
39 (25,25,23,7,3,256,BtUltra2),(26,26,24,7,3,512,BtUltra2),(27,27,25,9,3,999,BtUltra2)],
40 [(18,12,13,1,5,1,Fast),(18,13,14,1,6,0,Fast),(18,14,14,1,5,0,DFast),(18,16,16,1,4,0,DFast),(18,16,17,3,5,2,Greedy),
41 (18,17,18,5,5,2,Greedy),(18,18,19,3,5,4,Lazy),(18,18,19,4,4,4,Lazy),(18,18,19,4,4,8,Lazy2),(18,18,19,5,4,8,Lazy2),
42 (18,18,19,6,4,8,Lazy2),(18,18,19,5,4,12,BtLazy2),(18,19,19,7,4,12,BtLazy2),(18,18,19,4,4,16,BtOpt),(18,18,19,4,3,32,BtOpt),
43 (18,18,19,6,3,128,BtOpt),(18,19,19,6,3,128,BtUltra),(18,19,19,8,3,256,BtUltra),(18,19,19,6,3,128,BtUltra2),(18,19,19,8,3,256,BtUltra2),
44 (18,19,19,10,3,512,BtUltra2),(18,19,19,12,3,512,BtUltra2),(18,19,19,13,3,999,BtUltra2)],
45 [(17,12,12,1,5,1,Fast),(17,12,13,1,6,0,Fast),(17,13,15,1,5,0,Fast),(17,15,16,2,5,0,DFast),(17,17,17,2,4,0,DFast),
46 (17,16,17,3,4,2,Greedy),(17,16,17,3,4,4,Lazy),(17,16,17,3,4,8,Lazy2),(17,16,17,4,4,8,Lazy2),(17,16,17,5,4,8,Lazy2),
47 (17,16,17,6,4,8,Lazy2),(17,17,17,5,4,8,BtLazy2),(17,18,17,7,4,12,BtLazy2),(17,18,17,3,4,12,BtOpt),(17,18,17,4,3,32,BtOpt),
48 (17,18,17,6,3,256,BtOpt),(17,18,17,6,3,128,BtUltra),(17,18,17,8,3,256,BtUltra),(17,18,17,10,3,512,BtUltra),(17,18,17,5,3,256,BtUltra2),
49 (17,18,17,7,3,512,BtUltra2),(17,18,17,9,3,512,BtUltra2),(17,18,17,11,3,999,BtUltra2)],
50 [(14,12,13,1,5,1,Fast),(14,14,15,1,5,0,Fast),(14,14,15,1,4,0,Fast),(14,14,15,2,4,0,DFast),(14,14,14,4,4,2,Greedy),
51 (14,14,14,3,4,4,Lazy),(14,14,14,4,4,8,Lazy2),(14,14,14,6,4,8,Lazy2),(14,14,14,8,4,8,Lazy2),(14,15,14,5,4,8,BtLazy2),
52 (14,15,14,9,4,8,BtLazy2),(14,15,14,3,4,12,BtOpt),(14,15,14,4,3,24,BtOpt),(14,15,14,5,3,32,BtUltra),(14,15,15,6,3,64,BtUltra),
53 (14,15,15,7,3,256,BtUltra),(14,15,15,5,3,48,BtUltra2),(14,15,15,6,3,128,BtUltra2),(14,15,15,7,3,256,BtUltra2),(14,15,15,8,3,256,BtUltra2),
54 (14,15,15,8,3,512,BtUltra2),(14,15,15,9,3,512,BtUltra2),(14,15,15,10,3,999,BtUltra2)],
55];
56
57pub const MIN_LEVEL: i32 = -7;
59pub const MAX_LEVEL: i32 = 22;
61
62fn highbit(v: u64) -> u32 {
63 63 - v.leading_zeros()
64}
65
66pub fn lookup(level: i32, size: u64) -> CParams {
69 let table = (size <= 256 << 10) as usize + (size <= 128 << 10) as usize + (size <= 16 << 10) as usize;
70 let row = if level < 0 { 0 } else { level.clamp(1, MAX_LEVEL) as usize };
71 let (w, c, h, s, m, t, st) = TABLES[table][row];
72 let target_length = if level < 0 { (-level) as u32 } else { t };
73 CParams { window_log: w, chain_log: c, hash_log: h, search_log: s, min_match: m, target_length, strategy: st }
74}
75
76pub fn adjust(mut p: CParams, src: u64, dict: u64) -> CParams {
79 let total = src + dict;
80 let src_log = if total < 64 { 6 } else { highbit(total - 1) + 1 };
81 if p.window_log > src_log {
82 p.window_log = src_log;
83 }
84 let dw_log = if dict == 0 {
85 p.window_log
86 } else {
87 let wsize = 1u64 << p.window_log;
88 if wsize >= dict + src {
89 p.window_log
90 } else {
91 (highbit(dict + wsize - 1) + 1).min(31)
92 }
93 };
94 let cycle_log = p.chain_log - (p.strategy >= Strategy::BtLazy2) as u32;
95 if p.hash_log > dw_log + 1 {
96 p.hash_log = dw_log + 1;
97 }
98 if cycle_log > dw_log {
99 p.chain_log -= cycle_log - dw_log;
100 }
101 p.window_log = p.window_log.max(10);
102 p
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn small_inputs_shrink_tables() {
111 let p = adjust(lookup(3, 100), 100, 0);
112 assert_eq!((p.window_log, p.strategy), (10, Strategy::DFast));
113 assert!(p.hash_log <= 8 && p.chain_log <= 7);
114 let big = adjust(lookup(19, 10 << 20), 10 << 20, 0);
115 assert_eq!((big.window_log, big.strategy), (23, Strategy::BtUltra2));
116 assert_eq!(lookup(-3, 1 << 30).target_length, 3);
117 }
118}