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