liblzma/stream.rs
1//! Raw in-memory LZMA streams.
2//!
3//! The [`Stream`] type in this module is the primary type which performs
4//! encoding/decoding of LZMA streams. Each [`Stream`] is either an encoder or
5//! decoder and processes data in a streaming fashion.
6
7use std::collections::LinkedList;
8use std::error;
9use std::fmt;
10use std::io;
11use std::mem;
12
13/// Representation of an in-memory LZMA encoding or decoding stream.
14///
15/// Wraps the raw underlying `lzma_stream` type and provides the ability to
16/// create streams which can either decode or encode various LZMA-based formats.
17pub struct Stream {
18 raw: liblzma_sys::lzma_stream,
19}
20
21unsafe impl Send for Stream {}
22unsafe impl Sync for Stream {}
23
24/// Options that can be used to configure how LZMA encoding happens.
25///
26/// This builder is consumed by a number of other methods.
27pub struct LzmaOptions {
28 raw: liblzma_sys::lzma_options_lzma,
29}
30
31/// Builder to create a multithreaded stream encoder.
32#[cfg(feature = "parallel")]
33pub struct MtStreamBuilder {
34 raw: liblzma_sys::lzma_mt,
35 filters: Option<Filters>,
36}
37
38/// A custom chain of filters to configure an encoding stream.
39pub struct Filters {
40 inner: Vec<liblzma_sys::lzma_filter>,
41 lzma_opts: LinkedList<liblzma_sys::lzma_options_lzma>,
42}
43
44/// The `action` argument for [`Stream::process`],
45#[derive(Debug, Copy, Clone)]
46pub enum Action {
47 /// Continue processing
48 ///
49 /// When encoding, encode as much input as possible. Some internal buffering
50 /// will probably be done (depends on the filter chain in use), which causes
51 /// latency: the input used won't usually be decodeable from the output of
52 /// the same [`Stream::process`] call.
53 ///
54 /// When decoding, decode as much input as possible and produce as much
55 /// output as possible.
56 Run = liblzma_sys::LZMA_RUN as isize,
57
58 /// Make all the input available at output
59 ///
60 /// Normally the encoder introduces some latency. `SyncFlush` forces all the
61 /// buffered data to be available at output without resetting the internal
62 /// state of the encoder. This way it is possible to use compressed stream
63 /// for example for communication over network.
64 ///
65 /// Only some filters support `SyncFlush`. Trying to use `SyncFlush` with
66 /// filters that don't support it will make [`Stream::process`] return
67 /// `Error::Options`. For example, LZMA1 doesn't support `SyncFlush` but
68 /// LZMA2 does.
69 ///
70 /// Using `SyncFlush` very often can dramatically reduce the compression
71 /// ratio. With some filters (for example, LZMA2), fine-tuning the
72 /// compression options may help mitigate this problem significantly (for
73 /// example, match finder with LZMA2).
74 ///
75 /// Decoders don't support `SyncFlush`.
76 SyncFlush = liblzma_sys::LZMA_SYNC_FLUSH as isize,
77
78 /// Finish encoding of the current block.
79 ///
80 /// All the input data going to the current block must have been given to
81 /// the encoder. Call [`Stream::process`] with `FullFlush` until it returns
82 /// `Status::StreamEnd`. Then continue normally with `Run` or finish the
83 /// Stream with `Finish`.
84 ///
85 /// This action is currently supported only by stream encoder and easy
86 /// encoder (which uses stream encoder). If there is no unfinished block, no
87 /// empty block is created.
88 FullFlush = liblzma_sys::LZMA_FULL_FLUSH as isize,
89
90 /// Finish encoding of the current block.
91 ///
92 /// This is like `FullFlush` except that this doesn't necessarily wait until
93 /// all the input has been made available via the output buffer. That is,
94 /// [`Stream::process`] might return `Status::StreamEnd` as soon as all the input has
95 /// been consumed.
96 ///
97 /// `FullBarrier` is useful with a threaded encoder if one wants to split
98 /// the .xz Stream into blocks at specific offsets but doesn't care if the
99 /// output isn't flushed immediately. Using `FullBarrier` allows keeping the
100 /// threads busy while `FullFlush` would make [`Stream::process`] wait until all the
101 /// threads have finished until more data could be passed to the encoder.
102 ///
103 /// With a `Stream` initialized with the single-threaded
104 /// `new_stream_encoder` or `new_easy_encoder`, `FullBarrier` is an alias
105 /// for `FullFlush`.
106 FullBarrier = liblzma_sys::LZMA_FULL_BARRIER as isize,
107
108 /// Finish the current operation
109 ///
110 /// All the input data must have been given to the encoder (the last bytes
111 /// can still be pending in next_in). Call [`Stream::process`] with `Finish` until it
112 /// returns `Status::StreamEnd`. Once `Finish` has been used, the amount of
113 /// input must no longer be changed by the application.
114 ///
115 /// When decoding, using `Finish` is optional unless the concatenated flag
116 /// was used when the decoder was initialized. When concatenated was not
117 /// used, the only effect of `Finish` is that the amount of input must not
118 /// be changed just like in the encoder.
119 Finish = liblzma_sys::LZMA_FINISH as isize,
120}
121
122/// Return value of a [`Stream::process`] operation.
123#[derive(Debug, Copy, Clone, Eq, PartialEq)]
124pub enum Status {
125 /// Operation completed successfully.
126 Ok,
127
128 /// End of stream was reached.
129 ///
130 /// When encoding, this means that a sync/full flush or `Finish` was
131 /// completed. When decoding, this indicates that all data was decoded
132 /// successfully.
133 StreamEnd,
134
135 /// If the TELL_ANY_CHECK flags is specified when constructing a decoder,
136 /// this informs that the `check` method will now return the underlying
137 /// integrity check algorithm.
138 GetCheck,
139
140 /// An error has not been encountered, but no progress is possible.
141 ///
142 /// Processing can be continued normally by providing more input and/or more
143 /// output space, if possible.
144 ///
145 /// Typically the first call to [`Stream::process`] that can do no progress returns
146 /// `Ok` instead of `MemNeeded`. Only the second consecutive call doing no
147 /// progress will return `MemNeeded`.
148 MemNeeded,
149}
150
151/// Possible error codes that can be returned from a processing operation.
152#[derive(Debug, Clone, Copy, Eq, PartialEq)]
153pub enum Error {
154 /// The underlying data was corrupt.
155 Data,
156
157 /// Invalid or unsupported options were specified.
158 Options,
159
160 /// File format wasn't recognized.
161 Format,
162
163 /// Memory usage limit was reached.
164 ///
165 /// The memory limit can be increased with `set_memlimit`
166 MemLimit,
167
168 /// Memory couldn't be allocated.
169 Mem,
170
171 /// A programming error was encountered.
172 Program,
173
174 /// The `TELL_NO_CHECK` flag was specified and no integrity check was
175 /// available for this stream.
176 NoCheck,
177
178 /// The `TELL_UNSUPPORTED_CHECK` flag was specified and no integrity check
179 /// isn't implemented in this build of liblzma for this stream.
180 UnsupportedCheck,
181}
182
183/// Possible integrity checks that can be part of a .xz stream.
184#[allow(missing_docs)] // self-explanatory mostly
185#[derive(Debug, Copy, Clone)]
186pub enum Check {
187 None = liblzma_sys::LZMA_CHECK_NONE as isize,
188 Crc32 = liblzma_sys::LZMA_CHECK_CRC32 as isize,
189 Crc64 = liblzma_sys::LZMA_CHECK_CRC64 as isize,
190 Sha256 = liblzma_sys::LZMA_CHECK_SHA256 as isize,
191}
192
193/// Compression modes
194///
195/// This selects the function used to analyze the data produced by the match
196/// finder.
197#[derive(Debug, Copy, Clone)]
198pub enum Mode {
199 /// Fast compression.
200 ///
201 /// Fast mode is usually at its best when combined with a hash chain match
202 /// finder.
203 Fast = liblzma_sys::LZMA_MODE_FAST as isize,
204
205 /// Normal compression.
206 ///
207 /// This is usually notably slower than fast mode. Use this together with
208 /// binary tree match finders to expose the full potential of the LZMA1 or
209 /// LZMA2 encoder.
210 Normal = liblzma_sys::LZMA_MODE_NORMAL as isize,
211}
212
213/// Match finders
214///
215/// Match finder has major effect on both speed and compression ratio. Usually
216/// hash chains are faster than binary trees.
217///
218/// If you will use `SyncFlush` often, the hash chains may be a better choice,
219/// because binary trees get much higher compression ratio penalty with
220/// `SyncFlush`.
221///
222/// The memory usage formulas are only rough estimates, which are closest to
223/// reality when dict_size is a power of two. The formulas are more complex in
224/// reality, and can also change a little between liblzma versions.
225#[derive(Debug, Copy, Clone)]
226pub enum MatchFinder {
227 /// Hash Chain with 2- and 3-byte hashing
228 HashChain3 = liblzma_sys::LZMA_MF_HC3 as isize,
229 /// Hash Chain with 2-, 3-, and 4-byte hashing
230 HashChain4 = liblzma_sys::LZMA_MF_HC4 as isize,
231
232 /// Binary Tree with 2-byte hashing
233 BinaryTree2 = liblzma_sys::LZMA_MF_BT2 as isize,
234 /// Binary Tree with 2- and 3-byte hashing
235 BinaryTree3 = liblzma_sys::LZMA_MF_BT3 as isize,
236 /// Binary Tree with 2-, 3-, and 4-byte hashing
237 BinaryTree4 = liblzma_sys::LZMA_MF_BT4 as isize,
238}
239
240/// A flag passed when initializing a decoder, causes [`Stream::process`] to return
241/// [`Status::GetCheck`] as soon as the integrity check is known.
242pub const TELL_ANY_CHECK: u32 = liblzma_sys::LZMA_TELL_ANY_CHECK;
243
244/// A flag passed when initializing a decoder, causes [`Stream::process`] to return
245/// [`Error::NoCheck`] if the stream being decoded has no integrity check.
246pub const TELL_NO_CHECK: u32 = liblzma_sys::LZMA_TELL_NO_CHECK;
247
248/// A flag passed when initializing a decoder, causes [`Stream::process`] to return
249/// [`Error::UnsupportedCheck`] if the stream being decoded has an integrity check
250/// that cannot be verified by this build of liblzma.
251pub const TELL_UNSUPPORTED_CHECK: u32 = liblzma_sys::LZMA_TELL_UNSUPPORTED_CHECK;
252
253/// A flag passed when initializing a decoder, causes the decoder to ignore any
254/// integrity checks listed.
255pub const IGNORE_CHECK: u32 = liblzma_sys::LZMA_TELL_UNSUPPORTED_CHECK;
256
257/// A flag passed when initializing a decoder, indicates that the stream may be
258/// multiple concatenated xz files.
259pub const CONCATENATED: u32 = liblzma_sys::LZMA_CONCATENATED;
260
261/// Default compression preset level.
262pub const PRESET_DEFAULT: u32 = liblzma_sys::LZMA_PRESET_DEFAULT;
263
264/// Mask for extracting the preset level bits from a preset value.
265pub const PRESET_LEVEL_MASK: u32 = liblzma_sys::LZMA_PRESET_LEVEL_MASK;
266
267/// Flag to request the slower "extreme" variant of a preset.
268///
269/// Combine this with a preset level using bitwise-OR.
270/// For example: `6 | PRESET_EXTREME`.
271pub const PRESET_EXTREME: u32 = liblzma_sys::LZMA_PRESET_EXTREME;
272
273/// Encoder-related functions
274impl Stream {
275 /// Initialize .xz stream encoder using a preset number
276 ///
277 /// This is intended to be used by most for encoding data. The `preset`
278 /// argument is usually a level in the range 0-9 with 6 being a good
279 /// default. You may also bitwise-OR a level with [`PRESET_EXTREME`] to use
280 /// the slower extreme variant, for example `6 | PRESET_EXTREME`.
281 ///
282 /// The `check` argument is the integrity check to insert at the end of the
283 /// stream. The default of `Crc64` is typically appropriate.
284 #[inline]
285 pub fn new_easy_encoder(preset: u32, check: Check) -> Result<Stream, Error> {
286 let mut init = unsafe { Stream::zeroed() };
287 cvt(unsafe {
288 liblzma_sys::lzma_easy_encoder(&mut init.raw, preset, check as liblzma_sys::lzma_check)
289 })?;
290 Ok(init)
291 }
292
293 /// Initialize .lzma encoder (legacy file format)
294 ///
295 /// The .lzma format is sometimes called the LZMA_Alone format, which is the
296 /// reason for the name of this function. The .lzma format supports only the
297 /// LZMA1 filter. There is no support for integrity checks like CRC32.
298 ///
299 /// Use this function if and only if you need to create files readable by
300 /// legacy LZMA tools such as LZMA Utils 4.32.x. Moving to the .xz format
301 /// (the `new_easy_encoder` function) is strongly recommended.
302 ///
303 /// The valid action values for [`Stream::process`] are [`Action::Run`] and [`Action::Finish`].
304 /// No flushing is supported, because the file format doesn't support it.
305 #[inline]
306 pub fn new_lzma_encoder(options: &LzmaOptions) -> Result<Stream, Error> {
307 let mut init = unsafe { Stream::zeroed() };
308 cvt(unsafe { liblzma_sys::lzma_alone_encoder(&mut init.raw, &options.raw) })?;
309 Ok(init)
310 }
311
312 /// Initialize .xz Stream encoder using a custom filter chain
313 ///
314 /// This function is similar to `new_easy_encoder` but a custom filter chain
315 /// is specified.
316 #[inline]
317 pub fn new_stream_encoder(filters: &Filters, check: Check) -> Result<Stream, Error> {
318 let mut init = unsafe { Stream::zeroed() };
319 cvt(unsafe {
320 liblzma_sys::lzma_stream_encoder(
321 &mut init.raw,
322 filters.inner.as_ptr(),
323 check as liblzma_sys::lzma_check,
324 )
325 })?;
326 Ok(init)
327 }
328
329 /// Initialize an encoder stream using a custom filter chain.
330 #[inline]
331 pub fn new_raw_encoder(filters: &Filters) -> Result<Stream, Error> {
332 let mut init = unsafe { Self::zeroed() };
333 cvt(unsafe { liblzma_sys::lzma_raw_encoder(&mut init.raw, filters.inner.as_ptr()) })?;
334 Ok(init)
335 }
336}
337
338/// Decoder-related functions
339impl Stream {
340 /// Initialize a decoder which will choose a stream/lzma formats depending
341 /// on the input stream.
342 #[inline]
343 pub fn new_auto_decoder(memlimit: u64, flags: u32) -> Result<Stream, Error> {
344 let mut init = unsafe { Self::zeroed() };
345 cvt(unsafe { liblzma_sys::lzma_auto_decoder(&mut init.raw, memlimit, flags) })?;
346 Ok(init)
347 }
348
349 /// Initialize a .xz stream decoder.
350 ///
351 /// The maximum memory usage can be specified along with flags such as
352 /// [`TELL_ANY_CHECK`], [`TELL_NO_CHECK`], [`TELL_UNSUPPORTED_CHECK`],
353 /// [`IGNORE_CHECK`], or [`CONCATENATED`].
354 #[inline]
355 pub fn new_stream_decoder(memlimit: u64, flags: u32) -> Result<Stream, Error> {
356 let mut init = unsafe { Self::zeroed() };
357 cvt(unsafe { liblzma_sys::lzma_stream_decoder(&mut init.raw, memlimit, flags) })?;
358 Ok(init)
359 }
360
361 /// Initialize a .lzma stream decoder.
362 ///
363 /// The maximum memory usage can also be specified.
364 #[inline]
365 pub fn new_lzma_decoder(memlimit: u64) -> Result<Stream, Error> {
366 let mut init = unsafe { Self::zeroed() };
367 cvt(unsafe { liblzma_sys::lzma_alone_decoder(&mut init.raw, memlimit) })?;
368 Ok(init)
369 }
370
371 /// Initialize a .lz stream decoder.
372 #[inline]
373 pub fn new_lzip_decoder(memlimit: u64, flags: u32) -> Result<Self, Error> {
374 let mut init = unsafe { Self::zeroed() };
375 cvt(unsafe { liblzma_sys::lzma_lzip_decoder(&mut init.raw, memlimit, flags) })?;
376 Ok(init)
377 }
378
379 /// Initialize a decoder stream using a custom filter chain.
380 #[inline]
381 pub fn new_raw_decoder(filters: &Filters) -> Result<Stream, Error> {
382 let mut init = unsafe { Self::zeroed() };
383 cvt(unsafe { liblzma_sys::lzma_raw_decoder(&mut init.raw, filters.inner.as_ptr()) })?;
384 Ok(init)
385 }
386}
387
388/// Generic functions
389impl Stream {
390 #[inline]
391 unsafe fn zeroed() -> Self {
392 Self {
393 raw: unsafe { mem::zeroed() },
394 }
395 }
396
397 #[inline]
398 unsafe fn process_inner(
399 &mut self,
400 input: &[u8],
401 output_ptr: *mut u8,
402 output_len: usize,
403 action: Action,
404 ) -> Result<Status, Error> {
405 self.raw.next_in = input.as_ptr();
406 self.raw.avail_in = input.len();
407 self.raw.next_out = output_ptr;
408 self.raw.avail_out = output_len;
409 let action = action as liblzma_sys::lzma_action;
410 unsafe { cvt(liblzma_sys::lzma_code(&mut self.raw, action)) }
411 }
412
413 /// Processes some data from input into an output buffer.
414 ///
415 /// This will perform the appropriate encoding or decoding operation
416 /// depending on the kind of underlying stream. See [`Action`] for the
417 /// possible actions that can be taken.
418 ///
419 /// After the first use of [`Action::SyncFlush`], [`Action::FullFlush`],
420 /// [`Action::FullBarrier`], or [`Action::Finish`], the same [`Action`]
421 /// must be used until this returns [`Status::StreamEnd`]. Not doing so
422 /// will result in a [`Error::Program`].
423 ///
424 /// The amount of input must not be modified by the application until
425 /// this returns [`Status::StreamEnd`], otherwise [`Error::Program`] will
426 /// be returned.
427 #[inline]
428 pub fn process(
429 &mut self,
430 input: &[u8],
431 output: &mut [u8],
432 action: Action,
433 ) -> Result<Status, Error> {
434 unsafe { self.process_inner(input, output.as_mut_ptr(), output.len(), action) }
435 }
436
437 /// Same as [`Self::process`] but accepts uninitialized buffer.
438 ///
439 /// To retrieve bytes written into the `output`, please call [`Self::total_out()`] before
440 /// and after the call to [`Self::process_uninit`] and the diff of `total_out` would be
441 /// the bytes written to the `output`.
442 #[inline]
443 pub fn process_uninit(
444 &mut self,
445 input: &[u8],
446 output: &mut [mem::MaybeUninit<u8>],
447 action: Action,
448 ) -> Result<Status, Error> {
449 unsafe { self.process_inner(input, output.as_mut_ptr() as *mut _, output.len(), action) }
450 }
451
452 /// Performs the same data as [`Stream::process`], but places output data in a [`Vec`].
453 ///
454 /// This function will use the extra capacity of `output` as a destination
455 /// for bytes to be placed. The length of `output` will automatically get
456 /// updated after the operation has completed.
457 ///
458 /// See [`Stream::process`] for the other arguments.
459 #[inline]
460 pub fn process_vec(
461 &mut self,
462 input: &[u8],
463 output: &mut Vec<u8>,
464 action: Action,
465 ) -> Result<Status, Error> {
466 let len = output.len();
467
468 unsafe {
469 let before = self.total_out();
470 let ret = self.process_uninit(input, output.spare_capacity_mut(), action);
471 output.set_len((self.total_out() - before) as usize + len);
472 ret
473 }
474 }
475
476 /// Returns the total amount of input bytes consumed by this stream.
477 #[inline]
478 pub fn total_in(&self) -> u64 {
479 self.raw.total_in
480 }
481
482 /// Returns the total amount of bytes produced by this stream.
483 #[inline]
484 pub fn total_out(&self) -> u64 {
485 self.raw.total_out
486 }
487
488 /// Get the current memory usage limit.
489 ///
490 /// This is only supported if the underlying stream supports a memlimit.
491 #[inline]
492 pub fn memlimit(&self) -> u64 {
493 unsafe { liblzma_sys::lzma_memlimit_get(&self.raw) }
494 }
495
496 /// Set the current memory usage limit.
497 ///
498 /// This can return [`Error::MemLimit`] if the new limit is too small or
499 /// [`Error::Program`] if this stream doesn't take a memory limit.
500 #[inline]
501 pub fn set_memlimit(&mut self, limit: u64) -> Result<(), Error> {
502 cvt(unsafe { liblzma_sys::lzma_memlimit_set(&mut self.raw, limit) }).map(|_| ())
503 }
504}
505
506impl LzmaOptions {
507 /// Creates a new blank set of options.
508 #[inline]
509 pub fn new() -> LzmaOptions {
510 LzmaOptions {
511 raw: unsafe { mem::zeroed() },
512 }
513 }
514
515 /// Creates a new blank set of options for encoding.
516 ///
517 /// The `preset` argument is usually a level in the range 0-9. You may also
518 /// bitwise-OR a level with [`PRESET_EXTREME`] to use the slower extreme
519 /// variant, for example `6 | PRESET_EXTREME`.
520 #[inline]
521 pub fn new_preset(preset: u32) -> Result<LzmaOptions, Error> {
522 unsafe {
523 let mut options = Self::new();
524 let ret = liblzma_sys::lzma_lzma_preset(&mut options.raw, preset);
525 if ret != 0 {
526 Err(Error::Program)
527 } else {
528 Ok(options)
529 }
530 }
531 }
532
533 /// Configures the dictionary size, in bytes
534 ///
535 /// Dictionary size indicates how many bytes of the recently processed
536 /// uncompressed data is kept in memory.
537 ///
538 /// The minimum dictionary size is 4096 bytes and the default is 2^23 = 8MB.
539 #[inline]
540 pub fn dict_size(&mut self, size: u32) -> &mut LzmaOptions {
541 self.raw.dict_size = size;
542 self
543 }
544
545 /// Configures the number of literal context bits.
546 ///
547 /// How many of the highest bits of the previous uncompressed eight-bit byte
548 /// (also known as `literal') are taken into account when predicting the
549 /// bits of the next literal.
550 ///
551 /// The maximum value to this is 4 and the default is 3. It is not currently
552 /// supported if this plus [`LzmaOptions::literal_position_bits`] is greater than 4.
553 #[inline]
554 pub fn literal_context_bits(&mut self, bits: u32) -> &mut LzmaOptions {
555 self.raw.lc = bits;
556 self
557 }
558
559 /// Configures the number of literal position bits.
560 ///
561 /// This affects what kind of alignment in the uncompressed data is assumed
562 /// when encoding literals. A literal is a single 8-bit byte. See
563 /// [`LzmaOptions::position_bits`] for more information about alignment.
564 ///
565 /// The default for this is 0.
566 #[inline]
567 pub fn literal_position_bits(&mut self, bits: u32) -> &mut LzmaOptions {
568 self.raw.lp = bits;
569 self
570 }
571
572 /// Configures the number of position bits.
573 ///
574 /// Position bits affects what kind of alignment in the uncompressed data is
575 /// assumed in general. The default of 2 means four-byte alignment (2^pb
576 /// = 2^2 = 4), which is often a good choice when there's no better guess.
577 ///
578 /// When the alignment is known, setting pb accordingly may reduce the file
579 /// size a little. E.g. with text files having one-byte alignment (US-ASCII,
580 /// ISO-8859-*, UTF-8), setting pb=0 can improve compression slightly. For
581 /// UTF-16 text, pb=1 is a good choice. If the alignment is an odd number
582 /// like 3 bytes, pb=0 might be the best choice.
583 ///
584 /// Even though the assumed alignment can be adjusted with pb and lp, LZMA1
585 /// and LZMA2 still slightly favor 16-byte alignment. It might be worth
586 /// taking into account when designing file formats that are likely to be
587 /// often compressed with LZMA1 or LZMA2.
588 #[inline]
589 pub fn position_bits(&mut self, bits: u32) -> &mut LzmaOptions {
590 self.raw.pb = bits;
591 self
592 }
593
594 /// Configures the compression mode.
595 #[inline]
596 pub fn mode(&mut self, mode: Mode) -> &mut LzmaOptions {
597 self.raw.mode = mode as liblzma_sys::lzma_mode;
598 self
599 }
600
601 /// Configures the nice length of a match.
602 ///
603 /// This determines how many bytes the encoder compares from the match
604 /// candidates when looking for the best match. Once a match of at least
605 /// `len` bytes long is found, the encoder stops looking for better
606 /// candidates and encodes the match. (Naturally, if the found match is
607 /// actually longer than `len`, the actual length is encoded; it's not
608 /// truncated to `len`.)
609 ///
610 /// Bigger values usually increase the compression ratio and compression
611 /// time. For most files, 32 to 128 is a good value, which gives very good
612 /// compression ratio at good speed.
613 ///
614 /// The exact minimum value depends on the match finder. The maximum is 273,
615 /// which is the maximum length of a match that LZMA1 and LZMA2 can encode.
616 #[inline]
617 pub fn nice_len(&mut self, len: u32) -> &mut LzmaOptions {
618 self.raw.nice_len = len;
619 self
620 }
621
622 /// Configures the match finder ID.
623 #[inline]
624 pub fn match_finder(&mut self, mf: MatchFinder) -> &mut LzmaOptions {
625 self.raw.mf = mf as liblzma_sys::lzma_match_finder;
626 self
627 }
628
629 /// Maximum search depth in the match finder.
630 ///
631 /// For every input byte, match finder searches through the hash chain or
632 /// binary tree in a loop, each iteration going one step deeper in the chain
633 /// or tree. The searching stops if
634 ///
635 /// - a match of at least [`LzmaOptions::nice_len`] bytes long is found;
636 /// - all match candidates from the hash chain or binary tree have
637 /// been checked; or
638 /// - maximum search depth is reached.
639 ///
640 /// Maximum search depth is needed to prevent the match finder from wasting
641 /// too much time in case there are lots of short match candidates. On the
642 /// other hand, stopping the search before all candidates have been checked
643 /// can reduce compression ratio.
644 ///
645 /// Setting depth to zero tells liblzma to use an automatic default value,
646 /// that depends on the selected match finder and [`LzmaOptions::nice_len`].
647 /// The default is in the range [4, 200] or so (it may vary between liblzma
648 /// versions).
649 ///
650 /// Using a bigger depth value than the default can increase compression
651 /// ratio in some cases. There is no strict maximum value, but high values
652 /// (thousands or millions) should be used with care: the encoder could
653 /// remain fast enough with typical input, but malicious input could cause
654 /// the match finder to slow down dramatically, possibly creating a denial
655 /// of service attack.
656 #[inline]
657 pub fn depth(&mut self, depth: u32) -> &mut LzmaOptions {
658 self.raw.depth = depth;
659 self
660 }
661}
662
663impl Check {
664 /// Test if this check is supported in this build of liblzma.
665 #[inline]
666 pub fn is_supported(&self) -> bool {
667 let ret = unsafe { liblzma_sys::lzma_check_is_supported(*self as liblzma_sys::lzma_check) };
668 ret != 0
669 }
670}
671
672impl MatchFinder {
673 /// Test if this match finder is supported in this build of liblzma.
674 #[inline]
675 pub fn is_supported(&self) -> bool {
676 let ret =
677 unsafe { liblzma_sys::lzma_mf_is_supported(*self as liblzma_sys::lzma_match_finder) };
678 ret != 0
679 }
680}
681
682impl Filters {
683 /// Creates a new filter chain with no filters.
684 #[inline]
685 pub fn new() -> Filters {
686 Filters {
687 inner: vec![liblzma_sys::lzma_filter {
688 id: liblzma_sys::LZMA_VLI_UNKNOWN,
689 options: std::ptr::null_mut(),
690 }],
691 lzma_opts: LinkedList::new(),
692 }
693 }
694
695 /// Add an LZMA1 filter.
696 ///
697 /// LZMA1 is the very same thing as what was called just LZMA in LZMA Utils,
698 /// 7-Zip, and LZMA SDK. It's called LZMA1 here to prevent developers from
699 /// accidentally using LZMA when they actually want LZMA2.
700 ///
701 /// LZMA1 shouldn't be used for new applications unless you _really_ know
702 /// what you are doing. LZMA2 is almost always a better choice.
703 #[inline]
704 pub fn lzma1(&mut self, opts: &LzmaOptions) -> &mut Filters {
705 self.lzma_opts.push_back(opts.raw);
706 let ptr = self.lzma_opts.back().unwrap() as *const _ as *mut _;
707 self.push(liblzma_sys::lzma_filter {
708 id: liblzma_sys::LZMA_FILTER_LZMA1,
709 options: ptr,
710 })
711 }
712
713 /// Add an LZMA1 filter with properties.
714 #[inline]
715 pub fn lzma1_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
716 let filter = liblzma_sys::lzma_filter {
717 id: liblzma_sys::LZMA_FILTER_LZMA1,
718 options: std::ptr::null_mut(),
719 };
720 self.push_with_properties(filter, properties)
721 }
722
723 /// Add an LZMA2 filter.
724 ///
725 /// Usually you want this instead of LZMA1. Compared to LZMA1, LZMA2 adds
726 /// support for [`Action::SyncFlush`], uncompressed chunks (smaller expansion when
727 /// trying to compress uncompressible data), possibility to change
728 /// [`literal_context_bits`]/[`literal_position_bits`]/[`position_bits`] in the
729 /// middle of encoding, and some other internal improvements.
730 ///
731 /// [`literal_context_bits`]: LzmaOptions::literal_context_bits
732 /// [`literal_position_bits`]: LzmaOptions::literal_position_bits
733 /// [`position_bits`]: LzmaOptions::position_bits
734 #[inline]
735 pub fn lzma2(&mut self, opts: &LzmaOptions) -> &mut Filters {
736 self.lzma_opts.push_back(opts.raw);
737 let ptr = self.lzma_opts.back().unwrap() as *const _ as *mut _;
738 self.push(liblzma_sys::lzma_filter {
739 id: liblzma_sys::LZMA_FILTER_LZMA2,
740 options: ptr,
741 })
742 }
743
744 /// Add an LZMA2 filter with properties.
745 #[inline]
746 pub fn lzma2_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
747 let filter = liblzma_sys::lzma_filter {
748 id: liblzma_sys::LZMA_FILTER_LZMA2,
749 options: std::ptr::null_mut(),
750 };
751 self.push_with_properties(filter, properties)
752 }
753
754 /// Add a DELTA filter.
755 ///
756 /// # Examples
757 /// ```
758 /// use liblzma::stream::{Filters, LzmaOptions};
759 ///
760 /// let dict_size = 0x40000;
761 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
762 /// opts.dict_size(dict_size);
763 /// let mut filters = Filters::new();
764 /// filters.delta();
765 /// filters.lzma2(&opts);
766 /// ```
767 #[inline]
768 pub fn delta(&mut self) -> &mut Filters {
769 self.push(liblzma_sys::lzma_filter {
770 id: liblzma_sys::LZMA_FILTER_DELTA,
771 options: std::ptr::null_mut(),
772 })
773 }
774
775 /// Add a DELTA filter with properties.
776 ///
777 /// # Examples
778 /// ```
779 /// use liblzma::stream::{Filters, LzmaOptions};
780 ///
781 /// let mut filters = Filters::new();
782 /// filters.delta_properties(&[0x00]).unwrap();
783 /// ```
784 #[inline]
785 pub fn delta_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
786 let filter = liblzma_sys::lzma_filter {
787 id: liblzma_sys::LZMA_FILTER_DELTA,
788 options: std::ptr::null_mut(),
789 };
790 self.push_with_properties(filter, properties)
791 }
792
793 /// Add a filter for x86 binaries.
794 ///
795 /// # Examples
796 /// ```
797 /// use liblzma::stream::{Filters, LzmaOptions};
798 ///
799 /// let dict_size = 0x40000;
800 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
801 /// opts.dict_size(dict_size);
802 /// let mut filters = Filters::new();
803 /// filters.x86();
804 /// filters.lzma2(&opts);
805 /// ```
806 #[inline]
807 pub fn x86(&mut self) -> &mut Filters {
808 self.push(liblzma_sys::lzma_filter {
809 id: liblzma_sys::LZMA_FILTER_X86,
810 options: std::ptr::null_mut(),
811 })
812 }
813
814 /// Add a filter for x86 binaries with properties.
815 ///
816 /// # Examples
817 /// ```
818 /// use liblzma::stream::{Filters, LzmaOptions};
819 ///
820 /// let mut filters = Filters::new();
821 /// filters.x86_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
822 /// ```
823 #[inline]
824 pub fn x86_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
825 let filter = liblzma_sys::lzma_filter {
826 id: liblzma_sys::LZMA_FILTER_X86,
827 options: std::ptr::null_mut(),
828 };
829 self.push_with_properties(filter, properties)
830 }
831
832 /// Add a filter for PowerPC binaries.
833 ///
834 /// # Examples
835 /// ```
836 /// use liblzma::stream::{Filters, LzmaOptions};
837 ///
838 /// let dict_size = 0x40000;
839 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
840 /// opts.dict_size(dict_size);
841 /// let mut filters = Filters::new();
842 /// filters.powerpc();
843 /// filters.lzma2(&opts);
844 /// ```
845 #[inline]
846 pub fn powerpc(&mut self) -> &mut Filters {
847 self.push(liblzma_sys::lzma_filter {
848 id: liblzma_sys::LZMA_FILTER_POWERPC,
849 options: std::ptr::null_mut(),
850 })
851 }
852
853 /// Add a filter for PowerPC binaries with properties.
854 ///
855 /// # Examples
856 /// ```
857 /// use liblzma::stream::{Filters, LzmaOptions};
858 ///
859 /// let mut filters = Filters::new();
860 /// filters.powerpc_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
861 /// ```
862 #[inline]
863 pub fn powerpc_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
864 let filter = liblzma_sys::lzma_filter {
865 id: liblzma_sys::LZMA_FILTER_POWERPC,
866 options: std::ptr::null_mut(),
867 };
868 self.push_with_properties(filter, properties)
869 }
870
871 /// Add a filter for IA-64 (itanium) binaries.
872 ///
873 /// # Examples
874 /// ```
875 /// use liblzma::stream::{Filters, LzmaOptions};
876 ///
877 /// let dict_size = 0x40000;
878 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
879 /// opts.dict_size(dict_size);
880 /// let mut filters = Filters::new();
881 /// filters.ia64();
882 /// filters.lzma2(&opts);
883 /// ```
884 #[inline]
885 pub fn ia64(&mut self) -> &mut Filters {
886 self.push(liblzma_sys::lzma_filter {
887 id: liblzma_sys::LZMA_FILTER_IA64,
888 options: std::ptr::null_mut(),
889 })
890 }
891
892 /// Add a filter for IA-64 (itanium) binaries with properties.
893 ///
894 /// # Examples
895 /// ```
896 /// use liblzma::stream::{Filters, LzmaOptions};
897 ///
898 /// let mut filters = Filters::new();
899 /// filters.ia64_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
900 /// ```
901 #[inline]
902 pub fn ia64_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
903 let filter = liblzma_sys::lzma_filter {
904 id: liblzma_sys::LZMA_FILTER_IA64,
905 options: std::ptr::null_mut(),
906 };
907 self.push_with_properties(filter, properties)
908 }
909
910 /// Add a filter for ARM binaries.
911 ///
912 /// # Examples
913 /// ```
914 /// use liblzma::stream::{Filters, LzmaOptions};
915 ///
916 /// let dict_size = 0x40000;
917 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
918 /// opts.dict_size(dict_size);
919 /// let mut filters = Filters::new();
920 /// filters.arm();
921 /// filters.lzma2(&opts);
922 /// ```
923 #[inline]
924 pub fn arm(&mut self) -> &mut Filters {
925 self.push(liblzma_sys::lzma_filter {
926 id: liblzma_sys::LZMA_FILTER_ARM,
927 options: std::ptr::null_mut(),
928 })
929 }
930
931 /// Add a filter for ARM binaries with properties.
932 ///
933 /// # Examples
934 /// ```
935 /// use liblzma::stream::{Filters, LzmaOptions};
936 ///
937 /// let mut filters = Filters::new();
938 /// filters.arm_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
939 /// ```
940 #[inline]
941 pub fn arm_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
942 let filter = liblzma_sys::lzma_filter {
943 id: liblzma_sys::LZMA_FILTER_ARM,
944 options: std::ptr::null_mut(),
945 };
946 self.push_with_properties(filter, properties)
947 }
948
949 /// Add a filter for ARM64 binaries.
950 ///
951 /// # Examples
952 /// ```
953 /// use liblzma::stream::{Filters, LzmaOptions};
954 ///
955 /// let dict_size = 0x40000;
956 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
957 /// opts.dict_size(dict_size);
958 /// let mut filters = Filters::new();
959 /// filters.arm64();
960 /// filters.lzma2(&opts);
961 /// ```
962 #[inline]
963 pub fn arm64(&mut self) -> &mut Filters {
964 self.push(liblzma_sys::lzma_filter {
965 id: liblzma_sys::LZMA_FILTER_ARM64,
966 options: std::ptr::null_mut(),
967 })
968 }
969
970 /// Add a filter for ARM64 binaries with properties.
971 ///
972 /// # Examples
973 /// ```
974 /// use liblzma::stream::{Filters, LzmaOptions};
975 ///
976 /// let mut filters = Filters::new();
977 /// filters.arm64_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
978 /// ```
979 #[inline]
980 pub fn arm64_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
981 let filter = liblzma_sys::lzma_filter {
982 id: liblzma_sys::LZMA_FILTER_ARM64,
983 options: std::ptr::null_mut(),
984 };
985 self.push_with_properties(filter, properties)
986 }
987
988 /// Add a filter for RISCV binaries.
989 ///
990 /// # Examples
991 /// ```
992 /// use liblzma::stream::{Filters, LzmaOptions};
993 ///
994 /// let dict_size = 0x40000;
995 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
996 /// opts.dict_size(dict_size);
997 /// let mut filters = Filters::new();
998 /// filters.riscv();
999 /// filters.lzma2(&opts);
1000 /// ```
1001 #[inline]
1002 pub fn riscv(&mut self) -> &mut Filters {
1003 self.push(liblzma_sys::lzma_filter {
1004 id: liblzma_sys::LZMA_FILTER_RISCV,
1005 options: std::ptr::null_mut(),
1006 })
1007 }
1008
1009 /// Add a filter for RISCV binaries with properties.
1010 ///
1011 /// # Examples
1012 /// ```
1013 /// use liblzma::stream::{Filters, LzmaOptions};
1014 ///
1015 /// let mut filters = Filters::new();
1016 /// filters.riscv_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
1017 /// ```
1018 #[inline]
1019 pub fn riscv_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
1020 let filter = liblzma_sys::lzma_filter {
1021 id: liblzma_sys::LZMA_FILTER_RISCV,
1022 options: std::ptr::null_mut(),
1023 };
1024 self.push_with_properties(filter, properties)
1025 }
1026
1027 /// Add a filter for ARM-Thumb binaries.
1028 ///
1029 /// # Examples
1030 /// ```
1031 /// use liblzma::stream::{Filters, LzmaOptions};
1032 ///
1033 /// let dict_size = 0x40000;
1034 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
1035 /// opts.dict_size(dict_size);
1036 /// let mut filters = Filters::new();
1037 /// filters.arm_thumb();
1038 /// filters.lzma2(&opts);
1039 /// ```
1040 #[inline]
1041 pub fn arm_thumb(&mut self) -> &mut Filters {
1042 self.push(liblzma_sys::lzma_filter {
1043 id: liblzma_sys::LZMA_FILTER_ARMTHUMB,
1044 options: std::ptr::null_mut(),
1045 })
1046 }
1047
1048 /// Add a filter for ARM-Thumb binaries with properties.
1049 ///
1050 /// # Examples
1051 /// ```
1052 /// use liblzma::stream::{Filters, LzmaOptions};
1053 ///
1054 /// let mut filters = Filters::new();
1055 /// filters.arm_thumb_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
1056 /// ```
1057 #[inline]
1058 pub fn arm_thumb_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
1059 let filter = liblzma_sys::lzma_filter {
1060 id: liblzma_sys::LZMA_FILTER_ARMTHUMB,
1061 options: std::ptr::null_mut(),
1062 };
1063 self.push_with_properties(filter, properties)
1064 }
1065
1066 /// Add a filter for SPARC binaries.
1067 ///
1068 /// # Examples
1069 /// ```
1070 /// use liblzma::stream::{Filters, LzmaOptions};
1071 ///
1072 /// let dict_size = 0x40000;
1073 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
1074 /// opts.dict_size(dict_size);
1075 /// let mut filters = Filters::new();
1076 /// filters.sparc();
1077 /// filters.lzma2(&opts);
1078 /// ```
1079 #[inline]
1080 pub fn sparc(&mut self) -> &mut Filters {
1081 self.push(liblzma_sys::lzma_filter {
1082 id: liblzma_sys::LZMA_FILTER_SPARC,
1083 options: std::ptr::null_mut(),
1084 })
1085 }
1086
1087 /// Add a filter for SPARC binaries with properties.
1088 ///
1089 /// # Examples
1090 /// ```
1091 /// use liblzma::stream::{Filters, LzmaOptions};
1092 ///
1093 /// let mut filters = Filters::new();
1094 /// filters.sparc_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
1095 /// ```
1096 #[inline]
1097 pub fn sparc_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
1098 let filter = liblzma_sys::lzma_filter {
1099 id: liblzma_sys::LZMA_FILTER_SPARC,
1100 options: std::ptr::null_mut(),
1101 };
1102 self.push_with_properties(filter, properties)
1103 }
1104
1105 #[inline]
1106 fn push(&mut self, filter: liblzma_sys::lzma_filter) -> &mut Filters {
1107 let pos = self.inner.len() - 1;
1108 self.inner.insert(pos, filter);
1109 self
1110 }
1111
1112 #[inline]
1113 fn push_with_properties(
1114 &mut self,
1115 mut filter: liblzma_sys::lzma_filter,
1116 properties: &[u8],
1117 ) -> Result<&mut Filters, Error> {
1118 cvt(unsafe {
1119 liblzma_sys::lzma_properties_decode(
1120 &mut filter,
1121 std::ptr::null(),
1122 properties.as_ptr(),
1123 properties.len(),
1124 )
1125 })?;
1126 let pos = self.inner.len() - 1;
1127 self.inner.insert(pos, filter);
1128 Ok(self)
1129 }
1130
1131 /// Recommend a Block size for multithreaded encoding
1132 ///
1133 /// # Examples
1134 /// ```
1135 /// use liblzma::stream::{Filters, LzmaOptions};
1136 ///
1137 /// let dict_size = 0x40000;
1138 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
1139 /// opts.dict_size(dict_size);
1140 /// let mut filters = Filters::new();
1141 /// filters.lzma2(&opts);
1142 /// assert_eq!(filters.mt_block_size(), 1 << 20);
1143 /// ```
1144 #[cfg(feature = "parallel")]
1145 #[inline]
1146 pub fn mt_block_size(&self) -> u64 {
1147 unsafe { liblzma_sys::lzma_mt_block_size(self.inner.as_ptr()) }
1148 }
1149}
1150
1151#[cfg(feature = "parallel")]
1152impl MtStreamBuilder {
1153 /// Creates a new blank builder to create a multithreaded encoding [`Stream`].
1154 #[inline]
1155 pub fn new() -> Self {
1156 let mut init = Self {
1157 raw: unsafe { mem::zeroed() },
1158 filters: None,
1159 };
1160 init.raw.threads = 1;
1161 init
1162 }
1163
1164 /// Configures the number of worker threads to use
1165 #[inline]
1166 pub fn threads(&mut self, threads: u32) -> &mut Self {
1167 self.raw.threads = threads;
1168 self
1169 }
1170
1171 /// Configures the maximum uncompressed size of a block
1172 ///
1173 /// The encoder will start a new .xz block every `block_size` bytes.
1174 /// Using [`Action::FullFlush`] or [`Action::FullBarrier`] with
1175 /// [`Stream::process`] the caller may tell liblzma to start a new block earlier.
1176 ///
1177 /// With LZMA2, a recommended block size is 2-4 times the LZMA2 dictionary
1178 /// size. With very small dictionaries, it is recommended to use at least 1
1179 /// MiB block size for good compression ratio, even if this is more than
1180 /// four times the dictionary size. Note that these are only recommendations
1181 /// for typical use cases; feel free to use other values. Just keep in mind
1182 /// that using a block size less than the LZMA2 dictionary size is waste of
1183 /// RAM.
1184 ///
1185 /// Set this to 0 to let liblzma choose the block size depending on the
1186 /// compression options. For LZMA2 it will be 3*`dict_size` or 1 MiB,
1187 /// whichever is more.
1188 ///
1189 /// For each thread, about 3 * `block_size` bytes of memory will be
1190 /// allocated. This may change in later liblzma versions. If so, the memory
1191 /// usage will probably be reduced, not increased.
1192 #[inline]
1193 pub fn block_size(&mut self, block_size: u64) -> &mut Self {
1194 self.raw.block_size = block_size;
1195 self
1196 }
1197
1198 /// Timeout to allow [`Stream::process`] to return early
1199 ///
1200 /// Multithreading can make liblzma to consume input and produce output in a
1201 /// very bursty way: it may first read a lot of input to fill internal
1202 /// buffers, then no input or output occurs for a while.
1203 ///
1204 /// In single-threaded mode, [`Stream::process`] won't return until it has either
1205 /// consumed all the input or filled the output buffer. If this is done in
1206 /// multithreaded mode, it may cause a call [`Stream::process`] to take even tens of
1207 /// seconds, which isn't acceptable in all applications.
1208 ///
1209 /// To avoid very long blocking times in [`Stream::process`], a timeout (in
1210 /// milliseconds) may be set here. If `process would block longer than
1211 /// this number of milliseconds, it will return with `Ok`. Reasonable
1212 /// values are 100 ms or more. The xz command line tool uses 300 ms.
1213 ///
1214 /// If long blocking times are fine for you, set timeout to a special
1215 /// value of 0, which will disable the timeout mechanism and will make
1216 /// [`Stream::process`] block until all the input is consumed or the output
1217 /// buffer has been filled.
1218 #[inline]
1219 pub fn timeout_ms(&mut self, timeout: u32) -> &mut Self {
1220 self.raw.timeout = timeout;
1221 self
1222 }
1223
1224 /// Compression preset (level and possible flags)
1225 ///
1226 /// The preset is set just like with [`Stream::new_easy_encoder`], including
1227 /// support for [`PRESET_EXTREME`]. The preset is ignored if filters below
1228 /// have been specified.
1229 #[inline]
1230 pub fn preset(&mut self, preset: u32) -> &mut Self {
1231 self.raw.preset = preset;
1232 self
1233 }
1234
1235 /// Configure a custom filter chain
1236 #[inline]
1237 pub fn filters(&mut self, filters: Filters) -> &mut Self {
1238 self.raw.filters = filters.inner.as_ptr();
1239 self.filters = Some(filters);
1240 self
1241 }
1242
1243 /// Configures the integrity check type
1244 #[inline]
1245 pub fn check(&mut self, check: Check) -> &mut Self {
1246 self.raw.check = check as liblzma_sys::lzma_check;
1247 self
1248 }
1249
1250 /// Memory usage limit to reduce the number of threads
1251 #[inline]
1252 pub fn memlimit_threading(&mut self, memlimit: u64) -> &mut Self {
1253 self.raw.memlimit_threading = memlimit;
1254 self
1255 }
1256
1257 /// Memory usage limit that should never be exceeded
1258 #[inline]
1259 pub fn memlimit_stop(&mut self, memlimit: u64) -> &mut Self {
1260 self.raw.memlimit_stop = memlimit;
1261 self
1262 }
1263
1264 /// Calculate approximate memory usage of multithreaded .xz encoder
1265 #[inline]
1266 pub fn memusage(&self) -> u64 {
1267 unsafe { liblzma_sys::lzma_stream_encoder_mt_memusage(&self.raw) }
1268 }
1269
1270 /// Initialize multithreaded .xz stream encoder.
1271 #[inline]
1272 pub fn encoder(&self) -> Result<Stream, Error> {
1273 let mut init = unsafe { Stream::zeroed() };
1274 cvt(unsafe { liblzma_sys::lzma_stream_encoder_mt(&mut init.raw, &self.raw) })?;
1275 Ok(init)
1276 }
1277
1278 /// Initialize multithreaded .xz stream decoder.
1279 #[inline]
1280 pub fn decoder(&self) -> Result<Stream, Error> {
1281 let mut init = unsafe { Stream::zeroed() };
1282 cvt(unsafe { liblzma_sys::lzma_stream_decoder_mt(&mut init.raw, &self.raw) })?;
1283 Ok(init)
1284 }
1285}
1286
1287fn cvt(rc: liblzma_sys::lzma_ret) -> Result<Status, Error> {
1288 match rc {
1289 liblzma_sys::LZMA_OK => Ok(Status::Ok),
1290 liblzma_sys::LZMA_STREAM_END => Ok(Status::StreamEnd),
1291 liblzma_sys::LZMA_NO_CHECK => Err(Error::NoCheck),
1292 liblzma_sys::LZMA_UNSUPPORTED_CHECK => Err(Error::UnsupportedCheck),
1293 liblzma_sys::LZMA_GET_CHECK => Ok(Status::GetCheck),
1294 liblzma_sys::LZMA_MEM_ERROR => Err(Error::Mem),
1295 liblzma_sys::LZMA_MEMLIMIT_ERROR => Err(Error::MemLimit),
1296 liblzma_sys::LZMA_FORMAT_ERROR => Err(Error::Format),
1297 liblzma_sys::LZMA_OPTIONS_ERROR => Err(Error::Options),
1298 liblzma_sys::LZMA_DATA_ERROR => Err(Error::Data),
1299 liblzma_sys::LZMA_BUF_ERROR => Ok(Status::MemNeeded),
1300 liblzma_sys::LZMA_PROG_ERROR => Err(Error::Program),
1301 c => panic!("unknown return code: {}", c),
1302 }
1303}
1304
1305impl From<Error> for io::Error {
1306 #[inline]
1307 fn from(e: Error) -> io::Error {
1308 let kind = match e {
1309 Error::Data => io::ErrorKind::InvalidData,
1310 Error::Options => io::ErrorKind::InvalidInput,
1311 Error::Format => io::ErrorKind::InvalidData,
1312 Error::MemLimit => io::ErrorKind::Other,
1313 Error::Mem => io::ErrorKind::Other,
1314 Error::Program => io::ErrorKind::Other,
1315 Error::NoCheck => io::ErrorKind::InvalidInput,
1316 Error::UnsupportedCheck => io::ErrorKind::Other,
1317 };
1318
1319 io::Error::new(kind, e)
1320 }
1321}
1322
1323impl error::Error for Error {}
1324
1325impl fmt::Display for Error {
1326 #[inline]
1327 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1328 match self {
1329 Error::Data => "lzma data error",
1330 Error::Options => "invalid options",
1331 Error::Format => "stream/file format not recognized",
1332 Error::MemLimit => "memory limit reached",
1333 Error::Mem => "can't allocate memory",
1334 Error::Program => "liblzma internal error",
1335 Error::NoCheck => "no integrity check was available",
1336 Error::UnsupportedCheck => "liblzma not built with check support",
1337 }
1338 .fmt(f)
1339 }
1340}
1341
1342impl Drop for Stream {
1343 #[inline]
1344 fn drop(&mut self) {
1345 unsafe {
1346 liblzma_sys::lzma_end(&mut self.raw);
1347 }
1348 }
1349}