Skip to main content

zstd_complete/encoding/
compression_parameters.rs

1//! Typed advanced compression controls.
2
3/// Stable strategy choices understood by the native Rust compressor.
4#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5pub enum CompressionStrategy {
6    Fast,
7    DoubleFast,
8    Greedy,
9    Lazy,
10    Lazy2,
11    BinaryTreeLazy2,
12    Optimal,
13    Ultra,
14    Ultra2,
15}
16
17/// Controls whether each independently emitted frame declares its decoded size.
18#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
19pub enum ContentSizePolicy {
20    #[default]
21    Include,
22    Omit,
23}
24
25/// Typed long-distance matching controls.
26#[derive(Clone, Copy, Debug, Eq, PartialEq)]
27pub struct LongDistanceMatching {
28    pub(crate) window_log: Option<u8>,
29    pub(crate) hash_log: Option<u8>,
30    pub(crate) min_match: Option<u16>,
31    pub(crate) bucket_size_log: Option<u8>,
32    pub(crate) hash_rate_log: Option<u8>,
33}
34
35impl LongDistanceMatching {
36    pub const fn new() -> Self {
37        Self {
38            window_log: None,
39            hash_log: None,
40            min_match: None,
41            bucket_size_log: None,
42            hash_rate_log: None,
43        }
44    }
45
46    pub const fn with_window_log(mut self, value: u8) -> Self {
47        self.window_log = Some(value);
48        self
49    }
50
51    pub const fn with_hash_log(mut self, value: u8) -> Self {
52        self.hash_log = Some(value);
53        self
54    }
55
56    pub const fn with_min_match(mut self, value: u16) -> Self {
57        self.min_match = Some(value);
58        self
59    }
60
61    pub const fn with_bucket_size_log(mut self, value: u8) -> Self {
62        self.bucket_size_log = Some(value);
63        self
64    }
65
66    pub const fn with_hash_rate_log(mut self, value: u8) -> Self {
67        self.hash_rate_log = Some(value);
68        self
69    }
70
71    #[cfg(feature = "std")]
72    pub(crate) fn validate(self) -> Result<(), &'static str> {
73        if self
74            .window_log
75            .is_some_and(|value| !(10..=maximum_window_log()).contains(&value))
76        {
77            return Err("long-distance window log is outside the platform range");
78        }
79        if self
80            .hash_log
81            .is_some_and(|value| !(6..=30).contains(&value))
82        {
83            return Err("long-distance hash log must be in 6..=30");
84        }
85        if self
86            .min_match
87            .is_some_and(|value| !(4..=4096).contains(&value))
88        {
89            return Err("long-distance minimum match must be in 4..=4096");
90        }
91        if self
92            .bucket_size_log
93            .is_some_and(|value| !(1..=8).contains(&value))
94        {
95            return Err("long-distance bucket-size log must be in 1..=8");
96        }
97        if self.hash_rate_log.is_some_and(|value| value > 30) {
98            return Err("long-distance hash-rate log must be at most 30");
99        }
100        Ok(())
101    }
102}
103
104impl Default for LongDistanceMatching {
105    fn default() -> Self {
106        Self::new()
107    }
108}
109
110/// Optional low-level tuning layered over a [`super::CompressionLevel`] preset.
111///
112/// Unset fields retain the level-derived value. Validation is performed before
113/// the encoder allocates its frame workspace.
114#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
115pub struct CompressionTuning {
116    pub(crate) window_log: Option<u8>,
117    pub(crate) hash_log: Option<u8>,
118    pub(crate) chain_log: Option<u8>,
119    pub(crate) search_log: Option<u8>,
120    pub(crate) min_match: Option<u8>,
121    pub(crate) target_length: Option<u32>,
122    pub(crate) strategy: Option<CompressionStrategy>,
123    pub(crate) target_compressed_block_size: Option<usize>,
124    pub(crate) long_distance_matching: Option<LongDistanceMatching>,
125}
126
127impl CompressionTuning {
128    pub const fn new() -> Self {
129        Self {
130            window_log: None,
131            hash_log: None,
132            chain_log: None,
133            search_log: None,
134            min_match: None,
135            target_length: None,
136            strategy: None,
137            target_compressed_block_size: None,
138            long_distance_matching: None,
139        }
140    }
141
142    pub const fn with_window_log(mut self, value: u8) -> Self {
143        self.window_log = Some(value);
144        self
145    }
146    pub const fn with_hash_log(mut self, value: u8) -> Self {
147        self.hash_log = Some(value);
148        self
149    }
150    pub const fn with_chain_log(mut self, value: u8) -> Self {
151        self.chain_log = Some(value);
152        self
153    }
154    pub const fn with_search_log(mut self, value: u8) -> Self {
155        self.search_log = Some(value);
156        self
157    }
158    pub const fn with_min_match(mut self, value: u8) -> Self {
159        self.min_match = Some(value);
160        self
161    }
162    pub const fn with_target_length(mut self, value: u32) -> Self {
163        self.target_length = Some(value);
164        self
165    }
166    pub const fn with_strategy(mut self, value: CompressionStrategy) -> Self {
167        self.strategy = Some(value);
168        self
169    }
170    pub const fn with_target_compressed_block_size(mut self, bytes: usize) -> Self {
171        self.target_compressed_block_size = Some(bytes);
172        self
173    }
174    pub const fn with_long_distance_matching(mut self, value: LongDistanceMatching) -> Self {
175        self.long_distance_matching = Some(value);
176        self
177    }
178
179    #[cfg(feature = "std")]
180    pub(crate) fn validate(self) -> Result<(), &'static str> {
181        if self
182            .window_log
183            .is_some_and(|value| !(10..=maximum_window_log()).contains(&value))
184        {
185            return Err("window log is outside the platform range");
186        }
187        if self
188            .hash_log
189            .is_some_and(|value| !(6..=30).contains(&value))
190        {
191            return Err("hash log must be in 6..=30");
192        }
193        if self
194            .chain_log
195            .is_some_and(|value| !(6..=30).contains(&value))
196        {
197            return Err("chain log must be in 6..=30");
198        }
199        if self
200            .search_log
201            .is_some_and(|value| !(1..=30).contains(&value))
202        {
203            return Err("search log must be in 1..=30");
204        }
205        if self
206            .min_match
207            .is_some_and(|value| !(3..=7).contains(&value))
208        {
209            return Err("minimum match must be in 3..=7");
210        }
211        if self
212            .target_compressed_block_size
213            .is_some_and(|value| !(1340..=128 * 1024).contains(&value))
214        {
215            return Err("target compressed block size must be in 1340..=131072");
216        }
217        if let Some(ldm) = self.long_distance_matching {
218            ldm.validate()?;
219        }
220        Ok(())
221    }
222}
223
224#[cfg(feature = "std")]
225const fn maximum_window_log() -> u8 {
226    if usize::BITS >= 64 {
227        31
228    } else {
229        30
230    }
231}