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    if (2..usize::MAX).contains(&src_len) {
88        let src_log = 32 - ((src_len as u32) - 1).leading_zeros();
89        params.hash_log = params.hash_log.min(src_log);
90        params.chain_log = params.chain_log.min(src_log);
91        params.window_log = params.window_log.min(src_log);
92    }
93    Some(params)
94}
95
96/// Returns the maximum hash_log for a given level.
97/// Used by CompressContext to pre-allocate hash tables.
98pub fn max_hash_log(level: i32) -> Option<u32> {
99    let p = level_params_inner(level)?;
100    Some(p.hash_log.max(p.chain_log))
101}
102
103fn level_params_inner(level: i32) -> Option<LevelParams> {
104    Some(match level {
105        0 => return level_params_inner(DEFAULT_LEVEL),
106        -7 => LevelParams {
107            strategy: Strategy::Fast,
108            window_log: 19,
109            hash_log: 13,
110            chain_log: 13,
111            search_log: 0,
112            min_match: 5,
113            target_length: 7,
114            search_strength: 7,
115            force_raw_literals: true,
116            #[cfg(feature = "ldm")]
117            ldm_params: None,
118        },
119        -6 => LevelParams {
120            strategy: Strategy::Fast,
121            window_log: 19,
122            hash_log: 13,
123            chain_log: 13,
124            search_log: 0,
125            min_match: 5,
126            target_length: 7,
127            search_strength: 7,
128            force_raw_literals: false,
129            #[cfg(feature = "ldm")]
130            ldm_params: None,
131        },
132        -5 => LevelParams {
133            strategy: Strategy::Fast,
134            window_log: 19,
135            hash_log: 13,
136            chain_log: 13,
137            search_log: 0,
138            min_match: 5,
139            target_length: 6,
140            search_strength: 7,
141            force_raw_literals: false,
142            #[cfg(feature = "ldm")]
143            ldm_params: None,
144        },
145        -4 => LevelParams {
146            strategy: Strategy::Fast,
147            window_log: 19,
148            hash_log: 13,
149            chain_log: 13,
150            search_log: 0,
151            min_match: 5,
152            target_length: 5,
153            search_strength: 7,
154            force_raw_literals: false,
155            #[cfg(feature = "ldm")]
156            ldm_params: None,
157        },
158        -3 => LevelParams {
159            strategy: Strategy::Fast,
160            window_log: 19,
161            hash_log: 13,
162            chain_log: 13,
163            search_log: 0,
164            min_match: 5,
165            target_length: 4,
166            search_strength: 7,
167            force_raw_literals: false,
168            #[cfg(feature = "ldm")]
169            ldm_params: None,
170        },
171        -2 => LevelParams {
172            strategy: Strategy::Fast,
173            window_log: 19,
174            hash_log: 13,
175            chain_log: 13,
176            search_log: 0,
177            min_match: 5,
178            target_length: 3,
179            search_strength: 7,
180            force_raw_literals: false,
181            #[cfg(feature = "ldm")]
182            ldm_params: None,
183        },
184        -1 => LevelParams {
185            strategy: Strategy::Fast,
186            window_log: 19,
187            hash_log: 13,
188            chain_log: 13,
189            search_log: 0,
190            min_match: 5,
191            target_length: 2,
192            search_strength: 7,
193            force_raw_literals: false,
194            #[cfg(feature = "ldm")]
195            ldm_params: None,
196        },
197        1 => LevelParams {
198            strategy: Strategy::Fast,
199            window_log: 19,
200            hash_log: 14,
201            chain_log: 14,
202            search_log: 0,
203            min_match: 4,
204            target_length: 1,
205            search_strength: 8,
206            force_raw_literals: false,
207            #[cfg(feature = "ldm")]
208            ldm_params: None,
209        },
210        2 => LevelParams {
211            strategy: Strategy::Fast,
212            window_log: 20,
213            hash_log: 16,
214            chain_log: 16,
215            search_log: 0,
216            min_match: 4,
217            target_length: 1,
218            search_strength: 8,
219            force_raw_literals: false,
220            #[cfg(feature = "ldm")]
221            ldm_params: None,
222        },
223        3 => LevelParams {
224            strategy: Strategy::DFast,
225            window_log: 21,
226            hash_log: 18,
227            chain_log: 18,
228            search_log: 1,
229            min_match: 4,
230            target_length: 1,
231            search_strength: 5,
232            force_raw_literals: false,
233            #[cfg(feature = "ldm")]
234            ldm_params: None,
235        },
236        4 => LevelParams {
237            strategy: Strategy::DFast,
238            window_log: 23,
239            hash_log: 19,
240            chain_log: 19,
241            search_log: 1,
242            min_match: 4,
243            target_length: 1,
244            search_strength: 6,
245            force_raw_literals: false,
246            #[cfg(feature = "ldm")]
247            ldm_params: None,
248        },
249        _ => return None,
250    })
251}
252
253/// Options for large-window and LDM compression, orthogonal to level.
254///
255/// Pass to [`compress_opts`](crate::compress_opts) or
256/// [`FrameEncoder::with_options`](crate::streaming::FrameEncoder::with_options).
257#[derive(Debug, Clone, Default)]
258pub struct Options {
259    pub(crate) window_log: Option<u32>,
260    #[cfg_attr(not(feature = "ldm"), allow(dead_code))]
261    pub(crate) ldm: bool,
262}
263
264impl Options {
265    #[must_use]
266    pub fn window_log(mut self, log: u32) -> Self {
267        self.window_log = Some(log);
268        self
269    }
270
271    #[cfg(feature = "ldm")]
272    #[must_use]
273    pub fn ldm(mut self, enable: bool) -> Self {
274        self.ldm = enable;
275        self
276    }
277}
278
279pub fn apply_options(params: &mut LevelParams, opts: &Options) {
280    if let Some(wl) = opts.window_log {
281        params.window_log = wl;
282    }
283    #[cfg(feature = "ldm")]
284    if opts.ldm {
285        params.ldm_params = Some(LdmParams::default_for_window_log(params.window_log));
286    }
287}