sevenz_rust2/
encoder_options.rs

1use std::fmt::Debug;
2
3#[cfg(feature = "ppmd")]
4use ppmd_rust::{PPMD7_MAX_MEM_SIZE, PPMD7_MAX_ORDER, PPMD7_MIN_MEM_SIZE, PPMD7_MIN_ORDER};
5
6#[cfg(feature = "compress")]
7use crate::EncoderConfiguration;
8#[cfg(feature = "aes256")]
9use crate::Password;
10
11#[cfg(feature = "compress")]
12#[cfg_attr(docsrs, doc(cfg(feature = "compress")))]
13#[derive(Debug, Clone)]
14/// Options for LZMA compression.
15pub struct LZMAOptions(pub(crate) lzma_rust2::LZMAOptions);
16
17impl Default for LZMAOptions {
18    fn default() -> Self {
19        Self(lzma_rust2::LZMAOptions::with_preset(6))
20    }
21}
22
23#[cfg(feature = "compress")]
24impl LZMAOptions {
25    /// Creates LZMA options with the specified compression level.
26    ///
27    /// # Arguments
28    /// * `level` - Compression level (0-9, clamped to this range)
29    pub fn from_level(level: u32) -> Self {
30        Self(lzma_rust2::LZMAOptions::with_preset(level))
31    }
32}
33
34#[cfg(feature = "compress")]
35#[cfg_attr(docsrs, doc(cfg(feature = "compress")))]
36#[derive(Debug, Clone)]
37/// Options for LZMA2 compression.
38pub struct LZMA2Options {
39    pub(crate) options: lzma_rust2::LZMAOptions,
40    pub(crate) threads: u32,
41    pub(crate) stream_size: Option<u64>,
42}
43
44impl Default for LZMA2Options {
45    fn default() -> Self {
46        Self {
47            options: lzma_rust2::LZMAOptions::with_preset(6),
48            threads: 1,
49            stream_size: None,
50        }
51    }
52}
53
54#[cfg(feature = "compress")]
55impl LZMA2Options {
56    /// Creates LZMA2 options with the specified compression level.
57    /// Encoded using a single thread.
58    ///
59    /// # Arguments
60    /// * `level` - Compression level (0-9, clamped to this range)
61    pub fn from_level(level: u32) -> Self {
62        Self {
63            options: lzma_rust2::LZMAOptions::with_preset(level),
64            threads: 1,
65            stream_size: None,
66        }
67    }
68
69    /// Creates LZMA2 options with the specified compression level.
70    /// Encoded using a multi-threading.
71    ///
72    /// # Arguments
73    /// * `level` - Compression level (0-9, clamped to this range)
74    /// * `threads` - Count of threads used to compress the data
75    /// * `stream_size` - Size of each independent stream of uncompressed data.
76    ///   The more streams can be created, the more effective is
77    ///   the multi threading, but the worse the compression ratio
78    ///   will be (will be clamped to be at least 256 KiB).
79    pub fn from_level_mt(level: u32, threads: u32, stream_size: u64) -> Self {
80        Self {
81            options: lzma_rust2::LZMAOptions::with_preset(level),
82            threads,
83            stream_size: Some(stream_size.max(lzma_rust2::MIN_STREAM_SIZE)),
84        }
85    }
86
87    /// Sets the dictionary size used when encoding.
88    ///
89    /// Will be clamped between 4096..=4294967280.
90    pub fn set_dictionary_size(&mut self, dict_size: u32) {
91        self.options.dict_size =
92            dict_size.clamp(lzma_rust2::DICT_SIZE_MIN, lzma_rust2::DICT_SIZE_MAX);
93    }
94}
95
96#[cfg(feature = "bzip2")]
97#[cfg_attr(docsrs, doc(cfg(feature = "bzip2")))]
98#[derive(Debug, Copy, Clone)]
99/// Options for BZIP2 compression.
100pub struct Bzip2Options(pub(crate) u32);
101
102#[cfg(feature = "bzip2")]
103impl Bzip2Options {
104    /// Creates BZIP2 options with the specified compression level.
105    ///
106    /// # Arguments
107    /// * `level` - Compression level (typically 1-9)
108    pub const fn from_level(level: u32) -> Self {
109        Self(level)
110    }
111}
112
113#[cfg(feature = "bzip2")]
114impl Default for Bzip2Options {
115    fn default() -> Self {
116        Self(6)
117    }
118}
119
120#[cfg(any(feature = "brotli", feature = "lz4"))]
121const MINIMAL_SKIPPABLE_FRAME_SIZE: u32 = 64 * 1024;
122#[cfg(feature = "brotli")]
123const DEFAULT_SKIPPABLE_FRAME_SIZE: u32 = 128 * 1024;
124
125#[cfg(feature = "brotli")]
126#[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
127#[derive(Debug, Copy, Clone)]
128/// Options for Brotli compression.
129pub struct BrotliOptions {
130    pub(crate) quality: u32,
131    pub(crate) window: u32,
132    pub(crate) skippable_frame_size: u32,
133}
134
135#[cfg(feature = "brotli")]
136impl BrotliOptions {
137    /// Creates Brotli options with the specified quality and window size.
138    ///
139    /// # Arguments
140    /// * `quality` - Compression quality (0-11, clamped to this range)
141    /// * `window` - Window size (10-24, clamped to this range)
142    pub const fn from_quality_window(quality: u32, window: u32) -> Self {
143        let quality = if quality > 11 { 11 } else { quality };
144        let window = if window > 24 { 24 } else { window };
145        Self {
146            quality,
147            window,
148            skippable_frame_size: DEFAULT_SKIPPABLE_FRAME_SIZE,
149        }
150    }
151
152    /// Set's the skippable frame size. The size is defined as the size of uncompressed data a frame
153    /// contains. A value of 0 deactivates skippable frames and uses the native brotli bitstream.
154    /// If a value is set, then a similar skippable frame format used by LZ4 and ZSTD is used.
155    ///
156    /// Af value between 1..=64KiB will be set to 64KiB.
157    ///
158    /// This was first implemented by zstdmt. The default value is 128 KiB.
159    pub fn with_skippable_frame_size(mut self, skippable_frame_size: u32) -> Self {
160        if skippable_frame_size == 0 {
161            self.skippable_frame_size = 0;
162        } else {
163            self.skippable_frame_size =
164                u32::max(skippable_frame_size, MINIMAL_SKIPPABLE_FRAME_SIZE);
165        }
166
167        self
168    }
169}
170
171#[cfg(feature = "brotli")]
172impl Default for BrotliOptions {
173    fn default() -> Self {
174        Self {
175            quality: 11,
176            window: 22,
177            skippable_frame_size: DEFAULT_SKIPPABLE_FRAME_SIZE,
178        }
179    }
180}
181
182#[cfg(feature = "compress")]
183#[cfg_attr(docsrs, doc(cfg(feature = "compress")))]
184#[derive(Debug, Copy, Clone)]
185/// Options for Delta filter compression.
186pub struct DeltaOptions(pub(crate) u32);
187
188#[cfg(feature = "compress")]
189impl DeltaOptions {
190    /// Creates Delta options with the specified distance.
191    ///
192    /// # Arguments
193    /// * `distance` - Delta distance (1-256, clamped to this range, 0 becomes 1)
194    pub const fn from_distance(distance: u32) -> Self {
195        let distance = if distance == 0 {
196            1
197        } else if distance > 256 {
198            256
199        } else {
200            distance
201        };
202        Self(distance)
203    }
204}
205
206#[cfg(feature = "compress")]
207impl Default for DeltaOptions {
208    fn default() -> Self {
209        Self(1)
210    }
211}
212
213#[cfg(feature = "deflate")]
214#[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
215#[derive(Debug, Copy, Clone)]
216/// Options for Deflate compression.
217pub struct DeflateOptions(pub(crate) u32);
218
219#[cfg(feature = "deflate")]
220impl DeflateOptions {
221    /// Creates Deflate options with the specified compression level.
222    ///
223    /// # Arguments
224    /// * `level` - Compression level (0-9, clamped to this range)
225    pub const fn from_level(level: u32) -> Self {
226        let level = if level > 9 { 9 } else { level };
227        Self(level)
228    }
229}
230
231#[cfg(feature = "deflate")]
232impl Default for DeflateOptions {
233    fn default() -> Self {
234        Self(6)
235    }
236}
237
238#[cfg(feature = "lz4")]
239#[cfg_attr(docsrs, doc(cfg(feature = "lz4")))]
240#[derive(Debug, Copy, Clone, Default)]
241/// Options for LZ4 compression.
242pub struct LZ4Options {
243    pub(crate) skippable_frame_size: u32,
244}
245
246#[cfg(feature = "lz4")]
247impl LZ4Options {
248    /// Set's the skippable frame size. The size is defined as the size of uncompressed data a frame
249    /// contains. A value of 0 deactivates skippable frames and uses the native LZ4 bitstream.
250    /// If a value is set, then the similar skippable frame format is used.
251    ///
252    /// Af value between 1..=64KiB will be set to 64KiB.
253    ///
254    /// This was first implemented by zstdmt.
255    ///
256    /// Defaults to not use the skippable frame format at all, since LZ4 is extremely fast and will
257    /// most likely saturate IO even on a single thread.
258    pub fn with_skippable_frame_size(mut self, skippable_frame_size: u32) -> Self {
259        if skippable_frame_size == 0 {
260            self.skippable_frame_size = 0;
261        } else {
262            self.skippable_frame_size =
263                u32::max(skippable_frame_size, MINIMAL_SKIPPABLE_FRAME_SIZE);
264        }
265
266        self
267    }
268}
269
270#[cfg(feature = "ppmd")]
271#[cfg_attr(docsrs, doc(cfg(feature = "ppmd")))]
272#[derive(Debug, Copy, Clone)]
273/// Options for PPMD compression.
274pub struct PPMDOptions {
275    pub(crate) order: u32,
276    pub(crate) memory_size: u32,
277}
278
279#[cfg(feature = "ppmd")]
280impl PPMDOptions {
281    /// Creates PPMD options with the specified compression level.
282    ///
283    /// # Arguments
284    /// * `level` - Compression level (0-9, clamped to this range)
285    pub const fn from_level(level: u32) -> Self {
286        const ORDERS: [u32; 10] = [3, 4, 4, 5, 5, 6, 8, 16, 24, 32];
287
288        let level = if level > 9 { 9 } else { level };
289        let order = ORDERS[level as usize];
290        let memory_size = 1 << (level + 19);
291
292        Self { order, memory_size }
293    }
294
295    /// Creates PPMD options with specific order and memory size parameters.
296    ///
297    /// # Arguments
298    /// * `order` - Model order (clamped to valid PPMD range)
299    /// * `memory_size` - Memory size in bytes (clamped to valid PPMD range)
300    pub const fn from_order_memory_size(order: u32, memory_size: u32) -> Self {
301        let order = if order > PPMD7_MAX_ORDER {
302            PPMD7_MAX_ORDER
303        } else if order < PPMD7_MIN_ORDER {
304            PPMD7_MIN_ORDER
305        } else {
306            order
307        };
308        let memory_size = if memory_size > PPMD7_MAX_MEM_SIZE {
309            PPMD7_MAX_MEM_SIZE
310        } else if memory_size < PPMD7_MIN_MEM_SIZE {
311            PPMD7_MIN_MEM_SIZE
312        } else {
313            memory_size
314        };
315        Self { order, memory_size }
316    }
317}
318
319#[cfg(feature = "ppmd")]
320impl Default for PPMDOptions {
321    fn default() -> Self {
322        Self::from_level(6)
323    }
324}
325
326#[cfg(feature = "zstd")]
327#[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
328#[derive(Debug, Copy, Clone)]
329/// Options for Zstandard compression.
330pub struct ZStandardOptions(pub(crate) u32);
331
332#[cfg(feature = "zstd")]
333impl ZStandardOptions {
334    /// Creates Zstandard options with the specified compression level.
335    ///
336    /// # Arguments
337    /// * `level` - Compression level (typically 1-22)
338    pub const fn from_level(level: u32) -> Self {
339        let level = if level > 22 { 22 } else { level };
340        Self(level)
341    }
342}
343
344#[cfg(feature = "zstd")]
345impl Default for ZStandardOptions {
346    fn default() -> Self {
347        Self(3)
348    }
349}
350
351#[cfg_attr(docsrs, doc(cfg(feature = "aes256")))]
352#[cfg(feature = "aes256")]
353#[derive(Debug, Clone)]
354/// Options for AES256 encryption.
355pub struct AesEncoderOptions {
356    /// Password for encryption.
357    pub password: Password,
358    /// Initialization vector for encryption.
359    pub iv: [u8; 16],
360    /// Salt for key derivation.
361    pub salt: [u8; 16],
362    /// Number of cycles power for key derivation.
363    pub num_cycles_power: u8,
364}
365
366#[cfg(feature = "aes256")]
367impl AesEncoderOptions {
368    /// Creates new AES encoder options with the specified password.
369    ///
370    /// Generates random IV and salt values automatically.
371    ///
372    /// # Arguments
373    /// * `password` - Password for encryption
374    pub fn new(password: Password) -> Self {
375        let mut iv = [0; 16];
376        getrandom::fill(&mut iv).expect("Can't generate IV");
377
378        let mut salt = [0; 16];
379        getrandom::fill(&mut salt).expect("Can't generate salt");
380
381        Self {
382            password,
383            iv,
384            salt,
385            num_cycles_power: 8,
386        }
387    }
388
389    pub(crate) fn properties(&self) -> [u8; 34] {
390        let mut props = [0u8; 34];
391        self.write_properties(&mut props);
392        props
393    }
394
395    #[inline]
396    pub(crate) fn write_properties(&self, props: &mut [u8]) {
397        assert!(props.len() >= 34);
398        props[0] = (self.num_cycles_power & 0x3F) | 0xC0;
399        props[1] = 0xFF;
400        props[2..18].copy_from_slice(&self.salt);
401        props[18..34].copy_from_slice(&self.iv);
402    }
403}
404
405/// Encoder-specific options for various compression and encryption methods.
406#[derive(Debug, Clone)]
407pub enum EncoderOptions {
408    #[cfg(feature = "compress")]
409    #[cfg_attr(docsrs, doc(cfg(feature = "compress")))]
410    /// Delta filter options.
411    Delta(DeltaOptions),
412    #[cfg(feature = "compress")]
413    #[cfg_attr(docsrs, doc(cfg(feature = "compress")))]
414    /// LZMA compression options.
415    LZMA(LZMAOptions),
416    #[cfg(feature = "compress")]
417    #[cfg_attr(docsrs, doc(cfg(feature = "compress")))]
418    /// LZMA2 compression options.
419    LZMA2(LZMA2Options),
420    #[cfg(feature = "brotli")]
421    #[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
422    /// Brotli compression options.
423    BROTLI(BrotliOptions),
424    #[cfg(feature = "bzip2")]
425    #[cfg_attr(docsrs, doc(cfg(feature = "bzip2")))]
426    /// BZIP2 compression options.
427    BZIP2(Bzip2Options),
428    #[cfg(feature = "deflate")]
429    #[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
430    /// Deflate compression options.
431    DEFLATE(DeflateOptions),
432    #[cfg(feature = "lz4")]
433    #[cfg_attr(docsrs, doc(cfg(feature = "lz4")))]
434    /// LZ4 compression options.
435    LZ4(LZ4Options),
436    #[cfg(feature = "ppmd")]
437    #[cfg_attr(docsrs, doc(cfg(feature = "ppmd")))]
438    /// PPMD compression options.
439    PPMD(PPMDOptions),
440    #[cfg(feature = "zstd")]
441    #[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
442    /// Zstandard compression options.
443    ZSTD(ZStandardOptions),
444    #[cfg(feature = "aes256")]
445    #[cfg_attr(docsrs, doc(cfg(feature = "aes256")))]
446    /// AES256 encryption options.
447    Aes(AesEncoderOptions),
448}
449
450#[cfg(feature = "aes256")]
451impl From<AesEncoderOptions> for EncoderOptions {
452    fn from(value: AesEncoderOptions) -> Self {
453        Self::Aes(value)
454    }
455}
456
457#[cfg(all(feature = "aes256", feature = "compress"))]
458impl From<AesEncoderOptions> for EncoderConfiguration {
459    fn from(value: AesEncoderOptions) -> Self {
460        Self::new(crate::EncoderMethod::AES256SHA256).with_options(EncoderOptions::Aes(value))
461    }
462}
463
464#[cfg(feature = "compress")]
465impl From<DeltaOptions> for EncoderConfiguration {
466    fn from(options: DeltaOptions) -> Self {
467        Self::new(crate::EncoderMethod::DELTA_FILTER).with_options(EncoderOptions::Delta(options))
468    }
469}
470
471#[cfg(feature = "compress")]
472impl From<LZMA2Options> for EncoderConfiguration {
473    fn from(options: LZMA2Options) -> Self {
474        Self::new(crate::EncoderMethod::LZMA2).with_options(EncoderOptions::LZMA2(options))
475    }
476}
477
478#[cfg(feature = "bzip2")]
479impl From<Bzip2Options> for EncoderConfiguration {
480    fn from(options: Bzip2Options) -> Self {
481        Self::new(crate::EncoderMethod::BZIP2).with_options(EncoderOptions::BZIP2(options))
482    }
483}
484
485#[cfg(feature = "brotli")]
486impl From<BrotliOptions> for EncoderConfiguration {
487    fn from(options: BrotliOptions) -> Self {
488        Self::new(crate::EncoderMethod::BROTLI).with_options(EncoderOptions::BROTLI(options))
489    }
490}
491
492#[cfg(feature = "deflate")]
493impl From<DeflateOptions> for EncoderConfiguration {
494    fn from(options: DeflateOptions) -> Self {
495        Self::new(crate::EncoderMethod::DEFLATE).with_options(EncoderOptions::DEFLATE(options))
496    }
497}
498
499#[cfg(feature = "lz4")]
500impl From<LZ4Options> for EncoderConfiguration {
501    fn from(options: LZ4Options) -> Self {
502        Self::new(crate::EncoderMethod::LZ4).with_options(EncoderOptions::LZ4(options))
503    }
504}
505
506#[cfg(feature = "ppmd")]
507impl From<PPMDOptions> for EncoderConfiguration {
508    fn from(options: PPMDOptions) -> Self {
509        Self::new(crate::EncoderMethod::PPMD).with_options(EncoderOptions::PPMD(options))
510    }
511}
512
513#[cfg(feature = "zstd")]
514impl From<ZStandardOptions> for EncoderConfiguration {
515    fn from(options: ZStandardOptions) -> Self {
516        Self::new(crate::EncoderMethod::ZSTD).with_options(EncoderOptions::ZSTD(options))
517    }
518}
519
520#[cfg(feature = "compress")]
521impl From<DeltaOptions> for EncoderOptions {
522    fn from(o: DeltaOptions) -> Self {
523        Self::Delta(o)
524    }
525}
526
527#[cfg(feature = "compress")]
528impl From<LZMA2Options> for EncoderOptions {
529    fn from(o: LZMA2Options) -> Self {
530        Self::LZMA2(o)
531    }
532}
533
534#[cfg(feature = "bzip2")]
535impl From<Bzip2Options> for EncoderOptions {
536    fn from(o: Bzip2Options) -> Self {
537        Self::BZIP2(o)
538    }
539}
540
541#[cfg(feature = "brotli")]
542impl From<BrotliOptions> for EncoderOptions {
543    fn from(o: BrotliOptions) -> Self {
544        Self::BROTLI(o)
545    }
546}
547
548#[cfg(feature = "deflate")]
549impl From<DeflateOptions> for EncoderOptions {
550    fn from(o: DeflateOptions) -> Self {
551        Self::DEFLATE(o)
552    }
553}
554
555#[cfg(feature = "lz4")]
556impl From<LZ4Options> for EncoderOptions {
557    fn from(o: LZ4Options) -> Self {
558        Self::LZ4(o)
559    }
560}
561
562#[cfg(feature = "ppmd")]
563impl From<PPMDOptions> for EncoderOptions {
564    fn from(o: PPMDOptions) -> Self {
565        Self::PPMD(o)
566    }
567}
568
569#[cfg(feature = "zstd")]
570impl From<ZStandardOptions> for EncoderOptions {
571    fn from(o: ZStandardOptions) -> Self {
572        Self::ZSTD(o)
573    }
574}
575
576impl EncoderOptions {
577    /// Gets the LZMA & LZMA2 dictionary size for this encoder option.
578    ///
579    /// Returns the dictionary size if this is an LZMA & LZMA2 option, or a default value otherwise.
580    pub fn get_lzma_dict_size(&self) -> u32 {
581        match self {
582            #[cfg(feature = "compress")]
583            EncoderOptions::LZMA(o) => o.0.dict_size,
584            #[cfg(feature = "compress")]
585            EncoderOptions::LZMA2(o) => o.options.dict_size,
586            #[allow(unused)]
587            _ => 0,
588        }
589    }
590}