sevenz_rust2/
encoder_options.rs

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