Skip to main content

zrip_encode/
strategy.rs

1#![forbid(unsafe_code)]
2
3/// Match-finding strategy.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum Strategy {
6    /// Single hash table (levels -7 through 2).
7    Fast,
8    /// Short + long hash tables (levels 3-4).
9    DFast,
10}
11
12/// Parameters for Long Distance Matching.
13#[derive(Debug, Clone, Copy)]
14pub struct LdmParams {
15    pub hash_log: u32,
16    pub bucket_size_log: u32,
17    pub min_match_length: u32,
18    pub hash_rate_log: u32,
19}
20
21impl LdmParams {
22    pub fn default_for_window_log(window_log: u32) -> Self {
23        let hash_log = 20u32.min(window_log.saturating_sub(1));
24        let hash_rate_log = window_log.saturating_sub(hash_log).max(7);
25        Self {
26            hash_log,
27            bucket_size_log: 4,
28            min_match_length: 64,
29            hash_rate_log,
30        }
31    }
32}
33
34/// Compression parameters for a specific level.
35///
36/// Obtain via [`level_params`] or construct directly for custom tuning.
37/// Pass to [`compress_with_params`](crate::compress_with_params).
38#[derive(Debug, Clone, Copy)]
39pub struct LevelParams {
40    pub strategy: Strategy,
41    pub window_log: u32,
42    pub hash_log: u32,
43    /// DFast short table log. Same as hashLog for Fast strategy.
44    pub chain_log: u32,
45    pub search_log: u32,
46    pub min_match: u32,
47    pub target_length: u32,
48    pub search_strength: u32,
49    pub force_raw_literals: bool,
50    #[cfg(feature = "ldm")]
51    pub ldm_params: Option<LdmParams>,
52}
53
54impl LevelParams {
55    #[must_use]
56    pub fn with_window_log(mut self, window_log: u32) -> Self {
57        self.window_log = window_log;
58        self
59    }
60
61    #[cfg(feature = "ldm")]
62    #[must_use]
63    pub fn with_ldm(mut self, params: LdmParams) -> Self {
64        self.ldm_params = Some(params);
65        self
66    }
67}
68
69/// Default compression level used when level 0 is requested.
70pub const DEFAULT_LEVEL: i32 = 1;
71
72/// Returns the compression parameters for a given level, or `None` if out of range.
73///
74/// Level 0 is treated as "library default" and maps to level 1.
75/// Uses the large-input (>256 KB) parameter tier.
76pub fn level_params(level: i32) -> Option<LevelParams> {
77    level_params_for_size(level, usize::MAX)
78}
79
80/// Returns the compression parameters for a given level, sized for `src_len`.
81///
82/// Uses fixed parameters per level with log values clamped down for small inputs.
83///
84/// Level 0 is treated as "library default" and maps to level 1.
85pub fn level_params_for_size(level: i32, src_len: usize) -> Option<LevelParams> {
86    let mut params = level_params_inner(level)?;
87    params.hash_log = params.hash_log.clamp(HASH_LOG_MIN, HASH_LOG_MAX);
88    params.chain_log = params.chain_log.clamp(HASH_LOG_MIN, HASH_LOG_MAX);
89    if (2..usize::MAX).contains(&src_len) {
90        let src_log = 32 - ((src_len as u32) - 1).leading_zeros();
91        params.hash_log = params.hash_log.min(src_log).max(HASH_LOG_MIN);
92        params.chain_log = params.chain_log.min(src_log).max(HASH_LOG_MIN);
93        params.window_log = params.window_log.min(src_log);
94    }
95    Some(params)
96}
97
98pub const HASH_LOG_MIN: u32 = 6;
99pub const HASH_LOG_MAX: u32 = 30;
100
101/// Returns the maximum hash_log for a given level.
102/// Used by CompressContext to pre-allocate hash tables.
103pub fn max_hash_log(level: i32) -> Option<u32> {
104    let p = level_params_inner(level)?;
105    Some(p.hash_log.max(p.chain_log))
106}
107
108fn level_params_inner(level: i32) -> Option<LevelParams> {
109    Some(match level {
110        0 => return level_params_inner(DEFAULT_LEVEL),
111        -7 => LevelParams {
112            strategy: Strategy::Fast,
113            window_log: 19,
114            hash_log: 13,
115            chain_log: 13,
116            search_log: 0,
117            min_match: 5,
118            target_length: 7,
119            search_strength: 7,
120            force_raw_literals: true,
121            #[cfg(feature = "ldm")]
122            ldm_params: None,
123        },
124        -6 => LevelParams {
125            strategy: Strategy::Fast,
126            window_log: 19,
127            hash_log: 13,
128            chain_log: 13,
129            search_log: 0,
130            min_match: 5,
131            target_length: 7,
132            search_strength: 7,
133            force_raw_literals: false,
134            #[cfg(feature = "ldm")]
135            ldm_params: None,
136        },
137        -5 => LevelParams {
138            strategy: Strategy::Fast,
139            window_log: 19,
140            hash_log: 13,
141            chain_log: 13,
142            search_log: 0,
143            min_match: 5,
144            target_length: 6,
145            search_strength: 7,
146            force_raw_literals: false,
147            #[cfg(feature = "ldm")]
148            ldm_params: None,
149        },
150        -4 => LevelParams {
151            strategy: Strategy::Fast,
152            window_log: 19,
153            hash_log: 13,
154            chain_log: 13,
155            search_log: 0,
156            min_match: 5,
157            target_length: 5,
158            search_strength: 7,
159            force_raw_literals: false,
160            #[cfg(feature = "ldm")]
161            ldm_params: None,
162        },
163        -3 => LevelParams {
164            strategy: Strategy::Fast,
165            window_log: 19,
166            hash_log: 13,
167            chain_log: 13,
168            search_log: 0,
169            min_match: 5,
170            target_length: 4,
171            search_strength: 7,
172            force_raw_literals: false,
173            #[cfg(feature = "ldm")]
174            ldm_params: None,
175        },
176        -2 => LevelParams {
177            strategy: Strategy::Fast,
178            window_log: 19,
179            hash_log: 13,
180            chain_log: 13,
181            search_log: 0,
182            min_match: 5,
183            target_length: 3,
184            search_strength: 7,
185            force_raw_literals: false,
186            #[cfg(feature = "ldm")]
187            ldm_params: None,
188        },
189        -1 => LevelParams {
190            strategy: Strategy::Fast,
191            window_log: 19,
192            hash_log: 13,
193            chain_log: 13,
194            search_log: 0,
195            min_match: 5,
196            target_length: 2,
197            search_strength: 7,
198            force_raw_literals: false,
199            #[cfg(feature = "ldm")]
200            ldm_params: None,
201        },
202        1 => LevelParams {
203            strategy: Strategy::Fast,
204            window_log: 19,
205            hash_log: 14,
206            chain_log: 14,
207            search_log: 0,
208            min_match: 4,
209            target_length: 1,
210            search_strength: 8,
211            force_raw_literals: false,
212            #[cfg(feature = "ldm")]
213            ldm_params: None,
214        },
215        2 => LevelParams {
216            strategy: Strategy::Fast,
217            window_log: 20,
218            hash_log: 16,
219            chain_log: 16,
220            search_log: 0,
221            min_match: 4,
222            target_length: 1,
223            search_strength: 8,
224            force_raw_literals: false,
225            #[cfg(feature = "ldm")]
226            ldm_params: None,
227        },
228        3 => LevelParams {
229            strategy: Strategy::DFast,
230            window_log: 21,
231            hash_log: 18,
232            chain_log: 18,
233            search_log: 1,
234            min_match: 4,
235            target_length: 1,
236            search_strength: 5,
237            force_raw_literals: false,
238            #[cfg(feature = "ldm")]
239            ldm_params: None,
240        },
241        4 => LevelParams {
242            strategy: Strategy::DFast,
243            window_log: 23,
244            hash_log: 19,
245            chain_log: 19,
246            search_log: 1,
247            min_match: 4,
248            target_length: 1,
249            search_strength: 6,
250            force_raw_literals: false,
251            #[cfg(feature = "ldm")]
252            ldm_params: None,
253        },
254        _ => return None,
255    })
256}
257
258/// Options for large-window and LDM compression, orthogonal to level.
259///
260/// Pass to [`compress_opts`](crate::compress_opts) or
261/// [`FrameEncoder::with_options`](crate::streaming::FrameEncoder::with_options).
262#[derive(Debug, Clone, Default)]
263pub struct Options {
264    pub(crate) window_log: Option<u32>,
265    #[cfg_attr(not(feature = "ldm"), allow(dead_code))]
266    pub(crate) ldm: bool,
267}
268
269impl Options {
270    #[must_use]
271    pub fn window_log(mut self, log: u32) -> Self {
272        self.window_log = Some(log);
273        self
274    }
275
276    #[cfg(feature = "ldm")]
277    #[must_use]
278    pub fn ldm(mut self, enable: bool) -> Self {
279        self.ldm = enable;
280        self
281    }
282}
283
284pub fn apply_options(params: &mut LevelParams, opts: &Options) {
285    if let Some(wl) = opts.window_log {
286        params.window_log = wl;
287    }
288    #[cfg(feature = "ldm")]
289    if opts.ldm {
290        params.ldm_params = Some(LdmParams::default_for_window_log(params.window_log));
291    }
292}