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/// Encoder-related functions
262impl Stream {
263 /// Initialize .xz stream encoder using a preset number
264 ///
265 /// This is intended to be used by most for encoding data. The `preset`
266 /// argument is a number 0-9 indicating the compression level to use, and
267 /// normally 6 is a reasonable default.
268 ///
269 /// The `check` argument is the integrity check to insert at the end of the
270 /// stream. The default of `Crc64` is typically appropriate.
271 #[inline]
272 pub fn new_easy_encoder(preset: u32, check: Check) -> Result<Stream, Error> {
273 let mut init = unsafe { Stream::zeroed() };
274 cvt(unsafe {
275 liblzma_sys::lzma_easy_encoder(&mut init.raw, preset, check as liblzma_sys::lzma_check)
276 })?;
277 Ok(init)
278 }
279
280 /// Initialize .lzma encoder (legacy file format)
281 ///
282 /// The .lzma format is sometimes called the LZMA_Alone format, which is the
283 /// reason for the name of this function. The .lzma format supports only the
284 /// LZMA1 filter. There is no support for integrity checks like CRC32.
285 ///
286 /// Use this function if and only if you need to create files readable by
287 /// legacy LZMA tools such as LZMA Utils 4.32.x. Moving to the .xz format
288 /// (the `new_easy_encoder` function) is strongly recommended.
289 ///
290 /// The valid action values for [`Stream::process`] are [`Action::Run`] and [`Action::Finish`].
291 /// No flushing is supported, because the file format doesn't support it.
292 #[inline]
293 pub fn new_lzma_encoder(options: &LzmaOptions) -> Result<Stream, Error> {
294 let mut init = unsafe { Stream::zeroed() };
295 cvt(unsafe { liblzma_sys::lzma_alone_encoder(&mut init.raw, &options.raw) })?;
296 Ok(init)
297 }
298
299 /// Initialize .xz Stream encoder using a custom filter chain
300 ///
301 /// This function is similar to `new_easy_encoder` but a custom filter chain
302 /// is specified.
303 #[inline]
304 pub fn new_stream_encoder(filters: &Filters, check: Check) -> Result<Stream, Error> {
305 let mut init = unsafe { Stream::zeroed() };
306 cvt(unsafe {
307 liblzma_sys::lzma_stream_encoder(
308 &mut init.raw,
309 filters.inner.as_ptr(),
310 check as liblzma_sys::lzma_check,
311 )
312 })?;
313 Ok(init)
314 }
315
316 /// Initialize an encoder stream using a custom filter chain.
317 #[inline]
318 pub fn new_raw_encoder(filters: &Filters) -> Result<Stream, Error> {
319 let mut init = unsafe { Self::zeroed() };
320 cvt(unsafe { liblzma_sys::lzma_raw_encoder(&mut init.raw, filters.inner.as_ptr()) })?;
321 Ok(init)
322 }
323}
324
325/// Decoder-related functions
326impl Stream {
327 /// Initialize a decoder which will choose a stream/lzma formats depending
328 /// on the input stream.
329 #[inline]
330 pub fn new_auto_decoder(memlimit: u64, flags: u32) -> Result<Stream, Error> {
331 let mut init = unsafe { Self::zeroed() };
332 cvt(unsafe { liblzma_sys::lzma_auto_decoder(&mut init.raw, memlimit, flags) })?;
333 Ok(init)
334 }
335
336 /// Initialize a .xz stream decoder.
337 ///
338 /// The maximum memory usage can be specified along with flags such as
339 /// [`TELL_ANY_CHECK`], [`TELL_NO_CHECK`], [`TELL_UNSUPPORTED_CHECK`],
340 /// [`IGNORE_CHECK`], or [`CONCATENATED`].
341 #[inline]
342 pub fn new_stream_decoder(memlimit: u64, flags: u32) -> Result<Stream, Error> {
343 let mut init = unsafe { Self::zeroed() };
344 cvt(unsafe { liblzma_sys::lzma_stream_decoder(&mut init.raw, memlimit, flags) })?;
345 Ok(init)
346 }
347
348 /// Initialize a .lzma stream decoder.
349 ///
350 /// The maximum memory usage can also be specified.
351 #[inline]
352 pub fn new_lzma_decoder(memlimit: u64) -> Result<Stream, Error> {
353 let mut init = unsafe { Self::zeroed() };
354 cvt(unsafe { liblzma_sys::lzma_alone_decoder(&mut init.raw, memlimit) })?;
355 Ok(init)
356 }
357
358 /// Initialize a .lz stream decoder.
359 #[inline]
360 pub fn new_lzip_decoder(memlimit: u64, flags: u32) -> Result<Self, Error> {
361 let mut init = unsafe { Self::zeroed() };
362 cvt(unsafe { liblzma_sys::lzma_lzip_decoder(&mut init.raw, memlimit, flags) })?;
363 Ok(init)
364 }
365
366 /// Initialize a decoder stream using a custom filter chain.
367 #[inline]
368 pub fn new_raw_decoder(filters: &Filters) -> Result<Stream, Error> {
369 let mut init = unsafe { Self::zeroed() };
370 cvt(unsafe { liblzma_sys::lzma_raw_decoder(&mut init.raw, filters.inner.as_ptr()) })?;
371 Ok(init)
372 }
373}
374
375/// Generic functions
376impl Stream {
377 #[inline]
378 unsafe fn zeroed() -> Self {
379 Self {
380 raw: unsafe { mem::zeroed() },
381 }
382 }
383
384 #[inline]
385 unsafe fn process_inner(
386 &mut self,
387 input: &[u8],
388 output_ptr: *mut u8,
389 output_len: usize,
390 action: Action,
391 ) -> Result<Status, Error> {
392 self.raw.next_in = input.as_ptr();
393 self.raw.avail_in = input.len();
394 self.raw.next_out = output_ptr;
395 self.raw.avail_out = output_len;
396 let action = action as liblzma_sys::lzma_action;
397 unsafe { cvt(liblzma_sys::lzma_code(&mut self.raw, action)) }
398 }
399
400 /// Processes some data from input into an output buffer.
401 ///
402 /// This will perform the appropriate encoding or decoding operation
403 /// depending on the kind of underlying stream. See [`Action`] for the
404 /// possible actions that can be taken.
405 ///
406 /// After the first use of [`Action::SyncFlush`], [`Action::FullFlush`],
407 /// [`Action::FullBarrier`], or [`Action::Finish`], the same [`Action`]
408 /// must be used until this returns [`Status::StreamEnd`]. Not doing so
409 /// will result in a [`Error::Program`].
410 ///
411 /// The amount of input must not be modified by the application until
412 /// this returns [`Status::StreamEnd`], otherwise [`Error::Program`] will
413 /// be returned.
414 #[inline]
415 pub fn process(
416 &mut self,
417 input: &[u8],
418 output: &mut [u8],
419 action: Action,
420 ) -> Result<Status, Error> {
421 unsafe { self.process_inner(input, output.as_mut_ptr(), output.len(), action) }
422 }
423
424 /// Same as [`Self::process`] but accepts uninitialized buffer.
425 ///
426 /// To retrieve bytes written into the `output`, please call [`Self::total_out()`] before
427 /// and after the call to [`Self::process_uninit`] and the diff of `total_out` would be
428 /// the bytes written to the `output`.
429 #[inline]
430 pub fn process_uninit(
431 &mut self,
432 input: &[u8],
433 output: &mut [mem::MaybeUninit<u8>],
434 action: Action,
435 ) -> Result<Status, Error> {
436 unsafe { self.process_inner(input, output.as_mut_ptr() as *mut _, output.len(), action) }
437 }
438
439 /// Performs the same data as [`Stream::process`], but places output data in a [`Vec`].
440 ///
441 /// This function will use the extra capacity of `output` as a destination
442 /// for bytes to be placed. The length of `output` will automatically get
443 /// updated after the operation has completed.
444 ///
445 /// See [`Stream::process`] for the other arguments.
446 #[inline]
447 pub fn process_vec(
448 &mut self,
449 input: &[u8],
450 output: &mut Vec<u8>,
451 action: Action,
452 ) -> Result<Status, Error> {
453 let len = output.len();
454
455 unsafe {
456 let before = self.total_out();
457 let ret = self.process_uninit(input, output.spare_capacity_mut(), action);
458 output.set_len((self.total_out() - before) as usize + len);
459 ret
460 }
461 }
462
463 /// Returns the total amount of input bytes consumed by this stream.
464 #[inline]
465 pub fn total_in(&self) -> u64 {
466 self.raw.total_in
467 }
468
469 /// Returns the total amount of bytes produced by this stream.
470 #[inline]
471 pub fn total_out(&self) -> u64 {
472 self.raw.total_out
473 }
474
475 /// Get the current memory usage limit.
476 ///
477 /// This is only supported if the underlying stream supports a memlimit.
478 #[inline]
479 pub fn memlimit(&self) -> u64 {
480 unsafe { liblzma_sys::lzma_memlimit_get(&self.raw) }
481 }
482
483 /// Set the current memory usage limit.
484 ///
485 /// This can return [`Error::MemLimit`] if the new limit is too small or
486 /// [`Error::Program`] if this stream doesn't take a memory limit.
487 #[inline]
488 pub fn set_memlimit(&mut self, limit: u64) -> Result<(), Error> {
489 cvt(unsafe { liblzma_sys::lzma_memlimit_set(&mut self.raw, limit) }).map(|_| ())
490 }
491}
492
493impl LzmaOptions {
494 /// Creates a new blank set of options.
495 #[inline]
496 pub fn new() -> LzmaOptions {
497 LzmaOptions {
498 raw: unsafe { mem::zeroed() },
499 }
500 }
501
502 /// Creates a new blank set of options for encoding.
503 ///
504 /// The `preset` argument is the compression level to use, typically in the
505 /// range of 0-9.
506 #[inline]
507 pub fn new_preset(preset: u32) -> Result<LzmaOptions, Error> {
508 unsafe {
509 let mut options = Self::new();
510 let ret = liblzma_sys::lzma_lzma_preset(&mut options.raw, preset);
511 if ret != 0 {
512 Err(Error::Program)
513 } else {
514 Ok(options)
515 }
516 }
517 }
518
519 /// Configures the dictionary size, in bytes
520 ///
521 /// Dictionary size indicates how many bytes of the recently processed
522 /// uncompressed data is kept in memory.
523 ///
524 /// The minimum dictionary size is 4096 bytes and the default is 2^23 = 8MB.
525 #[inline]
526 pub fn dict_size(&mut self, size: u32) -> &mut LzmaOptions {
527 self.raw.dict_size = size;
528 self
529 }
530
531 /// Configures the number of literal context bits.
532 ///
533 /// How many of the highest bits of the previous uncompressed eight-bit byte
534 /// (also known as `literal') are taken into account when predicting the
535 /// bits of the next literal.
536 ///
537 /// The maximum value to this is 4 and the default is 3. It is not currently
538 /// supported if this plus [`LzmaOptions::literal_position_bits`] is greater than 4.
539 #[inline]
540 pub fn literal_context_bits(&mut self, bits: u32) -> &mut LzmaOptions {
541 self.raw.lc = bits;
542 self
543 }
544
545 /// Configures the number of literal position bits.
546 ///
547 /// This affects what kind of alignment in the uncompressed data is assumed
548 /// when encoding literals. A literal is a single 8-bit byte. See
549 /// [`LzmaOptions::position_bits`] for more information about alignment.
550 ///
551 /// The default for this is 0.
552 #[inline]
553 pub fn literal_position_bits(&mut self, bits: u32) -> &mut LzmaOptions {
554 self.raw.lp = bits;
555 self
556 }
557
558 /// Configures the number of position bits.
559 ///
560 /// Position bits affects what kind of alignment in the uncompressed data is
561 /// assumed in general. The default of 2 means four-byte alignment (2^pb
562 /// = 2^2 = 4), which is often a good choice when there's no better guess.
563 ///
564 /// When the alignment is known, setting pb accordingly may reduce the file
565 /// size a little. E.g. with text files having one-byte alignment (US-ASCII,
566 /// ISO-8859-*, UTF-8), setting pb=0 can improve compression slightly. For
567 /// UTF-16 text, pb=1 is a good choice. If the alignment is an odd number
568 /// like 3 bytes, pb=0 might be the best choice.
569 ///
570 /// Even though the assumed alignment can be adjusted with pb and lp, LZMA1
571 /// and LZMA2 still slightly favor 16-byte alignment. It might be worth
572 /// taking into account when designing file formats that are likely to be
573 /// often compressed with LZMA1 or LZMA2.
574 #[inline]
575 pub fn position_bits(&mut self, bits: u32) -> &mut LzmaOptions {
576 self.raw.pb = bits;
577 self
578 }
579
580 /// Configures the compression mode.
581 #[inline]
582 pub fn mode(&mut self, mode: Mode) -> &mut LzmaOptions {
583 self.raw.mode = mode as liblzma_sys::lzma_mode;
584 self
585 }
586
587 /// Configures the nice length of a match.
588 ///
589 /// This determines how many bytes the encoder compares from the match
590 /// candidates when looking for the best match. Once a match of at least
591 /// `len` bytes long is found, the encoder stops looking for better
592 /// candidates and encodes the match. (Naturally, if the found match is
593 /// actually longer than `len`, the actual length is encoded; it's not
594 /// truncated to `len`.)
595 ///
596 /// Bigger values usually increase the compression ratio and compression
597 /// time. For most files, 32 to 128 is a good value, which gives very good
598 /// compression ratio at good speed.
599 ///
600 /// The exact minimum value depends on the match finder. The maximum is 273,
601 /// which is the maximum length of a match that LZMA1 and LZMA2 can encode.
602 #[inline]
603 pub fn nice_len(&mut self, len: u32) -> &mut LzmaOptions {
604 self.raw.nice_len = len;
605 self
606 }
607
608 /// Configures the match finder ID.
609 #[inline]
610 pub fn match_finder(&mut self, mf: MatchFinder) -> &mut LzmaOptions {
611 self.raw.mf = mf as liblzma_sys::lzma_match_finder;
612 self
613 }
614
615 /// Maximum search depth in the match finder.
616 ///
617 /// For every input byte, match finder searches through the hash chain or
618 /// binary tree in a loop, each iteration going one step deeper in the chain
619 /// or tree. The searching stops if
620 ///
621 /// - a match of at least [`LzmaOptions::nice_len`] bytes long is found;
622 /// - all match candidates from the hash chain or binary tree have
623 /// been checked; or
624 /// - maximum search depth is reached.
625 ///
626 /// Maximum search depth is needed to prevent the match finder from wasting
627 /// too much time in case there are lots of short match candidates. On the
628 /// other hand, stopping the search before all candidates have been checked
629 /// can reduce compression ratio.
630 ///
631 /// Setting depth to zero tells liblzma to use an automatic default value,
632 /// that depends on the selected match finder and [`LzmaOptions::nice_len`].
633 /// The default is in the range [4, 200] or so (it may vary between liblzma
634 /// versions).
635 ///
636 /// Using a bigger depth value than the default can increase compression
637 /// ratio in some cases. There is no strict maximum value, but high values
638 /// (thousands or millions) should be used with care: the encoder could
639 /// remain fast enough with typical input, but malicious input could cause
640 /// the match finder to slow down dramatically, possibly creating a denial
641 /// of service attack.
642 #[inline]
643 pub fn depth(&mut self, depth: u32) -> &mut LzmaOptions {
644 self.raw.depth = depth;
645 self
646 }
647}
648
649impl Check {
650 /// Test if this check is supported in this build of liblzma.
651 #[inline]
652 pub fn is_supported(&self) -> bool {
653 let ret = unsafe { liblzma_sys::lzma_check_is_supported(*self as liblzma_sys::lzma_check) };
654 ret != 0
655 }
656}
657
658impl MatchFinder {
659 /// Test if this match finder is supported in this build of liblzma.
660 #[inline]
661 pub fn is_supported(&self) -> bool {
662 let ret =
663 unsafe { liblzma_sys::lzma_mf_is_supported(*self as liblzma_sys::lzma_match_finder) };
664 ret != 0
665 }
666}
667
668impl Filters {
669 /// Creates a new filter chain with no filters.
670 #[inline]
671 pub fn new() -> Filters {
672 Filters {
673 inner: vec![liblzma_sys::lzma_filter {
674 id: liblzma_sys::LZMA_VLI_UNKNOWN,
675 options: std::ptr::null_mut(),
676 }],
677 lzma_opts: LinkedList::new(),
678 }
679 }
680
681 /// Add an LZMA1 filter.
682 ///
683 /// LZMA1 is the very same thing as what was called just LZMA in LZMA Utils,
684 /// 7-Zip, and LZMA SDK. It's called LZMA1 here to prevent developers from
685 /// accidentally using LZMA when they actually want LZMA2.
686 ///
687 /// LZMA1 shouldn't be used for new applications unless you _really_ know
688 /// what you are doing. LZMA2 is almost always a better choice.
689 #[inline]
690 pub fn lzma1(&mut self, opts: &LzmaOptions) -> &mut Filters {
691 self.lzma_opts.push_back(opts.raw);
692 let ptr = self.lzma_opts.back().unwrap() as *const _ as *mut _;
693 self.push(liblzma_sys::lzma_filter {
694 id: liblzma_sys::LZMA_FILTER_LZMA1,
695 options: ptr,
696 })
697 }
698
699 /// Add an LZMA1 filter with properties.
700 #[inline]
701 pub fn lzma1_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
702 let filter = liblzma_sys::lzma_filter {
703 id: liblzma_sys::LZMA_FILTER_LZMA1,
704 options: std::ptr::null_mut(),
705 };
706 self.push_with_properties(filter, properties)
707 }
708
709 /// Add an LZMA2 filter.
710 ///
711 /// Usually you want this instead of LZMA1. Compared to LZMA1, LZMA2 adds
712 /// support for [`Action::SyncFlush`], uncompressed chunks (smaller expansion when
713 /// trying to compress uncompressible data), possibility to change
714 /// [`literal_context_bits`]/[`literal_position_bits`]/[`position_bits`] in the
715 /// middle of encoding, and some other internal improvements.
716 ///
717 /// [`literal_context_bits`]: LzmaOptions::literal_context_bits
718 /// [`literal_position_bits`]: LzmaOptions::literal_position_bits
719 /// [`position_bits`]: LzmaOptions::position_bits
720 #[inline]
721 pub fn lzma2(&mut self, opts: &LzmaOptions) -> &mut Filters {
722 self.lzma_opts.push_back(opts.raw);
723 let ptr = self.lzma_opts.back().unwrap() as *const _ as *mut _;
724 self.push(liblzma_sys::lzma_filter {
725 id: liblzma_sys::LZMA_FILTER_LZMA2,
726 options: ptr,
727 })
728 }
729
730 /// Add an LZMA2 filter with properties.
731 #[inline]
732 pub fn lzma2_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
733 let filter = liblzma_sys::lzma_filter {
734 id: liblzma_sys::LZMA_FILTER_LZMA2,
735 options: std::ptr::null_mut(),
736 };
737 self.push_with_properties(filter, properties)
738 }
739
740 /// Add a DELTA filter.
741 ///
742 /// # Examples
743 /// ```
744 /// use liblzma::stream::{Filters, LzmaOptions};
745 ///
746 /// let dict_size = 0x40000;
747 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
748 /// opts.dict_size(dict_size);
749 /// let mut filters = Filters::new();
750 /// filters.delta();
751 /// filters.lzma2(&opts);
752 /// ```
753 #[inline]
754 pub fn delta(&mut self) -> &mut Filters {
755 self.push(liblzma_sys::lzma_filter {
756 id: liblzma_sys::LZMA_FILTER_DELTA,
757 options: std::ptr::null_mut(),
758 })
759 }
760
761 /// Add a DELTA filter with properties.
762 ///
763 /// # Examples
764 /// ```
765 /// use liblzma::stream::{Filters, LzmaOptions};
766 ///
767 /// let mut filters = Filters::new();
768 /// filters.delta_properties(&[0x00]).unwrap();
769 /// ```
770 #[inline]
771 pub fn delta_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
772 let filter = liblzma_sys::lzma_filter {
773 id: liblzma_sys::LZMA_FILTER_DELTA,
774 options: std::ptr::null_mut(),
775 };
776 self.push_with_properties(filter, properties)
777 }
778
779 /// Add a filter for x86 binaries.
780 ///
781 /// # Examples
782 /// ```
783 /// use liblzma::stream::{Filters, LzmaOptions};
784 ///
785 /// let dict_size = 0x40000;
786 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
787 /// opts.dict_size(dict_size);
788 /// let mut filters = Filters::new();
789 /// filters.x86();
790 /// filters.lzma2(&opts);
791 /// ```
792 #[inline]
793 pub fn x86(&mut self) -> &mut Filters {
794 self.push(liblzma_sys::lzma_filter {
795 id: liblzma_sys::LZMA_FILTER_X86,
796 options: std::ptr::null_mut(),
797 })
798 }
799
800 /// Add a filter for x86 binaries with properties.
801 ///
802 /// # Examples
803 /// ```
804 /// use liblzma::stream::{Filters, LzmaOptions};
805 ///
806 /// let mut filters = Filters::new();
807 /// filters.x86_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
808 /// ```
809 #[inline]
810 pub fn x86_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
811 let filter = liblzma_sys::lzma_filter {
812 id: liblzma_sys::LZMA_FILTER_X86,
813 options: std::ptr::null_mut(),
814 };
815 self.push_with_properties(filter, properties)
816 }
817
818 /// Add a filter for PowerPC binaries.
819 ///
820 /// # Examples
821 /// ```
822 /// use liblzma::stream::{Filters, LzmaOptions};
823 ///
824 /// let dict_size = 0x40000;
825 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
826 /// opts.dict_size(dict_size);
827 /// let mut filters = Filters::new();
828 /// filters.powerpc();
829 /// filters.lzma2(&opts);
830 /// ```
831 #[inline]
832 pub fn powerpc(&mut self) -> &mut Filters {
833 self.push(liblzma_sys::lzma_filter {
834 id: liblzma_sys::LZMA_FILTER_POWERPC,
835 options: std::ptr::null_mut(),
836 })
837 }
838
839 /// Add a filter for PowerPC binaries with properties.
840 ///
841 /// # Examples
842 /// ```
843 /// use liblzma::stream::{Filters, LzmaOptions};
844 ///
845 /// let mut filters = Filters::new();
846 /// filters.powerpc_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
847 /// ```
848 #[inline]
849 pub fn powerpc_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
850 let filter = liblzma_sys::lzma_filter {
851 id: liblzma_sys::LZMA_FILTER_POWERPC,
852 options: std::ptr::null_mut(),
853 };
854 self.push_with_properties(filter, properties)
855 }
856
857 /// Add a filter for IA-64 (itanium) binaries.
858 ///
859 /// # Examples
860 /// ```
861 /// use liblzma::stream::{Filters, LzmaOptions};
862 ///
863 /// let dict_size = 0x40000;
864 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
865 /// opts.dict_size(dict_size);
866 /// let mut filters = Filters::new();
867 /// filters.ia64();
868 /// filters.lzma2(&opts);
869 /// ```
870 #[inline]
871 pub fn ia64(&mut self) -> &mut Filters {
872 self.push(liblzma_sys::lzma_filter {
873 id: liblzma_sys::LZMA_FILTER_IA64,
874 options: std::ptr::null_mut(),
875 })
876 }
877
878 /// Add a filter for IA-64 (itanium) binaries with properties.
879 ///
880 /// # Examples
881 /// ```
882 /// use liblzma::stream::{Filters, LzmaOptions};
883 ///
884 /// let mut filters = Filters::new();
885 /// filters.ia64_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
886 /// ```
887 #[inline]
888 pub fn ia64_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
889 let filter = liblzma_sys::lzma_filter {
890 id: liblzma_sys::LZMA_FILTER_IA64,
891 options: std::ptr::null_mut(),
892 };
893 self.push_with_properties(filter, properties)
894 }
895
896 /// Add a filter for ARM binaries.
897 ///
898 /// # Examples
899 /// ```
900 /// use liblzma::stream::{Filters, LzmaOptions};
901 ///
902 /// let dict_size = 0x40000;
903 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
904 /// opts.dict_size(dict_size);
905 /// let mut filters = Filters::new();
906 /// filters.arm();
907 /// filters.lzma2(&opts);
908 /// ```
909 #[inline]
910 pub fn arm(&mut self) -> &mut Filters {
911 self.push(liblzma_sys::lzma_filter {
912 id: liblzma_sys::LZMA_FILTER_ARM,
913 options: std::ptr::null_mut(),
914 })
915 }
916
917 /// Add a filter for ARM binaries with properties.
918 ///
919 /// # Examples
920 /// ```
921 /// use liblzma::stream::{Filters, LzmaOptions};
922 ///
923 /// let mut filters = Filters::new();
924 /// filters.arm_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
925 /// ```
926 #[inline]
927 pub fn arm_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
928 let filter = liblzma_sys::lzma_filter {
929 id: liblzma_sys::LZMA_FILTER_ARM,
930 options: std::ptr::null_mut(),
931 };
932 self.push_with_properties(filter, properties)
933 }
934
935 /// Add a filter for ARM64 binaries.
936 ///
937 /// # Examples
938 /// ```
939 /// use liblzma::stream::{Filters, LzmaOptions};
940 ///
941 /// let dict_size = 0x40000;
942 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
943 /// opts.dict_size(dict_size);
944 /// let mut filters = Filters::new();
945 /// filters.arm64();
946 /// filters.lzma2(&opts);
947 /// ```
948 #[inline]
949 pub fn arm64(&mut self) -> &mut Filters {
950 self.push(liblzma_sys::lzma_filter {
951 id: liblzma_sys::LZMA_FILTER_ARM64,
952 options: std::ptr::null_mut(),
953 })
954 }
955
956 /// Add a filter for ARM64 binaries with properties.
957 ///
958 /// # Examples
959 /// ```
960 /// use liblzma::stream::{Filters, LzmaOptions};
961 ///
962 /// let mut filters = Filters::new();
963 /// filters.arm64_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
964 /// ```
965 #[inline]
966 pub fn arm64_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
967 let filter = liblzma_sys::lzma_filter {
968 id: liblzma_sys::LZMA_FILTER_ARM64,
969 options: std::ptr::null_mut(),
970 };
971 self.push_with_properties(filter, properties)
972 }
973
974 /// Add a filter for RISCV binaries.
975 ///
976 /// # Examples
977 /// ```
978 /// use liblzma::stream::{Filters, LzmaOptions};
979 ///
980 /// let dict_size = 0x40000;
981 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
982 /// opts.dict_size(dict_size);
983 /// let mut filters = Filters::new();
984 /// filters.riscv();
985 /// filters.lzma2(&opts);
986 /// ```
987 #[inline]
988 pub fn riscv(&mut self) -> &mut Filters {
989 self.push(liblzma_sys::lzma_filter {
990 id: liblzma_sys::LZMA_FILTER_RISCV,
991 options: std::ptr::null_mut(),
992 })
993 }
994
995 /// Add a filter for RISCV binaries with properties.
996 ///
997 /// # Examples
998 /// ```
999 /// use liblzma::stream::{Filters, LzmaOptions};
1000 ///
1001 /// let mut filters = Filters::new();
1002 /// filters.riscv_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
1003 /// ```
1004 #[inline]
1005 pub fn riscv_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
1006 let filter = liblzma_sys::lzma_filter {
1007 id: liblzma_sys::LZMA_FILTER_RISCV,
1008 options: std::ptr::null_mut(),
1009 };
1010 self.push_with_properties(filter, properties)
1011 }
1012
1013 /// Add a filter for ARM-Thumb binaries.
1014 ///
1015 /// # Examples
1016 /// ```
1017 /// use liblzma::stream::{Filters, LzmaOptions};
1018 ///
1019 /// let dict_size = 0x40000;
1020 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
1021 /// opts.dict_size(dict_size);
1022 /// let mut filters = Filters::new();
1023 /// filters.arm_thumb();
1024 /// filters.lzma2(&opts);
1025 /// ```
1026 #[inline]
1027 pub fn arm_thumb(&mut self) -> &mut Filters {
1028 self.push(liblzma_sys::lzma_filter {
1029 id: liblzma_sys::LZMA_FILTER_ARMTHUMB,
1030 options: std::ptr::null_mut(),
1031 })
1032 }
1033
1034 /// Add a filter for ARM-Thumb binaries with properties.
1035 ///
1036 /// # Examples
1037 /// ```
1038 /// use liblzma::stream::{Filters, LzmaOptions};
1039 ///
1040 /// let mut filters = Filters::new();
1041 /// filters.arm_thumb_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
1042 /// ```
1043 #[inline]
1044 pub fn arm_thumb_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
1045 let filter = liblzma_sys::lzma_filter {
1046 id: liblzma_sys::LZMA_FILTER_ARMTHUMB,
1047 options: std::ptr::null_mut(),
1048 };
1049 self.push_with_properties(filter, properties)
1050 }
1051
1052 /// Add a filter for SPARC binaries.
1053 ///
1054 /// # Examples
1055 /// ```
1056 /// use liblzma::stream::{Filters, LzmaOptions};
1057 ///
1058 /// let dict_size = 0x40000;
1059 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
1060 /// opts.dict_size(dict_size);
1061 /// let mut filters = Filters::new();
1062 /// filters.sparc();
1063 /// filters.lzma2(&opts);
1064 /// ```
1065 #[inline]
1066 pub fn sparc(&mut self) -> &mut Filters {
1067 self.push(liblzma_sys::lzma_filter {
1068 id: liblzma_sys::LZMA_FILTER_SPARC,
1069 options: std::ptr::null_mut(),
1070 })
1071 }
1072
1073 /// Add a filter for SPARC binaries with properties.
1074 ///
1075 /// # Examples
1076 /// ```
1077 /// use liblzma::stream::{Filters, LzmaOptions};
1078 ///
1079 /// let mut filters = Filters::new();
1080 /// filters.sparc_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap();
1081 /// ```
1082 #[inline]
1083 pub fn sparc_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
1084 let filter = liblzma_sys::lzma_filter {
1085 id: liblzma_sys::LZMA_FILTER_SPARC,
1086 options: std::ptr::null_mut(),
1087 };
1088 self.push_with_properties(filter, properties)
1089 }
1090
1091 #[inline]
1092 fn push(&mut self, filter: liblzma_sys::lzma_filter) -> &mut Filters {
1093 let pos = self.inner.len() - 1;
1094 self.inner.insert(pos, filter);
1095 self
1096 }
1097
1098 #[inline]
1099 fn push_with_properties(
1100 &mut self,
1101 mut filter: liblzma_sys::lzma_filter,
1102 properties: &[u8],
1103 ) -> Result<&mut Filters, Error> {
1104 cvt(unsafe {
1105 liblzma_sys::lzma_properties_decode(
1106 &mut filter,
1107 std::ptr::null(),
1108 properties.as_ptr(),
1109 properties.len(),
1110 )
1111 })?;
1112 let pos = self.inner.len() - 1;
1113 self.inner.insert(pos, filter);
1114 Ok(self)
1115 }
1116
1117 /// Recommend a Block size for multithreaded encoding
1118 ///
1119 /// # Examples
1120 /// ```
1121 /// use liblzma::stream::{Filters, LzmaOptions};
1122 ///
1123 /// let dict_size = 0x40000;
1124 /// let mut opts = LzmaOptions::new_preset(6).unwrap();
1125 /// opts.dict_size(dict_size);
1126 /// let mut filters = Filters::new();
1127 /// filters.lzma2(&opts);
1128 /// assert_eq!(filters.mt_block_size(), 1 << 20);
1129 /// ```
1130 #[cfg(feature = "parallel")]
1131 #[inline]
1132 pub fn mt_block_size(&self) -> u64 {
1133 unsafe { liblzma_sys::lzma_mt_block_size(self.inner.as_ptr()) }
1134 }
1135}
1136
1137#[cfg(feature = "parallel")]
1138impl MtStreamBuilder {
1139 /// Creates a new blank builder to create a multithreaded encoding [`Stream`].
1140 #[inline]
1141 pub fn new() -> Self {
1142 let mut init = Self {
1143 raw: unsafe { mem::zeroed() },
1144 filters: None,
1145 };
1146 init.raw.threads = 1;
1147 init
1148 }
1149
1150 /// Configures the number of worker threads to use
1151 #[inline]
1152 pub fn threads(&mut self, threads: u32) -> &mut Self {
1153 self.raw.threads = threads;
1154 self
1155 }
1156
1157 /// Configures the maximum uncompressed size of a block
1158 ///
1159 /// The encoder will start a new .xz block every `block_size` bytes.
1160 /// Using [`Action::FullFlush`] or [`Action::FullBarrier`] with
1161 /// [`Stream::process`] the caller may tell liblzma to start a new block earlier.
1162 ///
1163 /// With LZMA2, a recommended block size is 2-4 times the LZMA2 dictionary
1164 /// size. With very small dictionaries, it is recommended to use at least 1
1165 /// MiB block size for good compression ratio, even if this is more than
1166 /// four times the dictionary size. Note that these are only recommendations
1167 /// for typical use cases; feel free to use other values. Just keep in mind
1168 /// that using a block size less than the LZMA2 dictionary size is waste of
1169 /// RAM.
1170 ///
1171 /// Set this to 0 to let liblzma choose the block size depending on the
1172 /// compression options. For LZMA2 it will be 3*`dict_size` or 1 MiB,
1173 /// whichever is more.
1174 ///
1175 /// For each thread, about 3 * `block_size` bytes of memory will be
1176 /// allocated. This may change in later liblzma versions. If so, the memory
1177 /// usage will probably be reduced, not increased.
1178 #[inline]
1179 pub fn block_size(&mut self, block_size: u64) -> &mut Self {
1180 self.raw.block_size = block_size;
1181 self
1182 }
1183
1184 /// Timeout to allow [`Stream::process`] to return early
1185 ///
1186 /// Multithreading can make liblzma to consume input and produce output in a
1187 /// very bursty way: it may first read a lot of input to fill internal
1188 /// buffers, then no input or output occurs for a while.
1189 ///
1190 /// In single-threaded mode, [`Stream::process`] won't return until it has either
1191 /// consumed all the input or filled the output buffer. If this is done in
1192 /// multithreaded mode, it may cause a call [`Stream::process`] to take even tens of
1193 /// seconds, which isn't acceptable in all applications.
1194 ///
1195 /// To avoid very long blocking times in [`Stream::process`], a timeout (in
1196 /// milliseconds) may be set here. If `process would block longer than
1197 /// this number of milliseconds, it will return with `Ok`. Reasonable
1198 /// values are 100 ms or more. The xz command line tool uses 300 ms.
1199 ///
1200 /// If long blocking times are fine for you, set timeout to a special
1201 /// value of 0, which will disable the timeout mechanism and will make
1202 /// [`Stream::process`] block until all the input is consumed or the output
1203 /// buffer has been filled.
1204 #[inline]
1205 pub fn timeout_ms(&mut self, timeout: u32) -> &mut Self {
1206 self.raw.timeout = timeout;
1207 self
1208 }
1209
1210 /// Compression preset (level and possible flags)
1211 ///
1212 /// The preset is set just like with [`Stream::new_easy_encoder`]. The preset
1213 /// is ignored if filters below have been specified.
1214 #[inline]
1215 pub fn preset(&mut self, preset: u32) -> &mut Self {
1216 self.raw.preset = preset;
1217 self
1218 }
1219
1220 /// Configure a custom filter chain
1221 #[inline]
1222 pub fn filters(&mut self, filters: Filters) -> &mut Self {
1223 self.raw.filters = filters.inner.as_ptr();
1224 self.filters = Some(filters);
1225 self
1226 }
1227
1228 /// Configures the integrity check type
1229 #[inline]
1230 pub fn check(&mut self, check: Check) -> &mut Self {
1231 self.raw.check = check as liblzma_sys::lzma_check;
1232 self
1233 }
1234
1235 /// Memory usage limit to reduce the number of threads
1236 #[inline]
1237 pub fn memlimit_threading(&mut self, memlimit: u64) -> &mut Self {
1238 self.raw.memlimit_threading = memlimit;
1239 self
1240 }
1241
1242 /// Memory usage limit that should never be exceeded
1243 #[inline]
1244 pub fn memlimit_stop(&mut self, memlimit: u64) -> &mut Self {
1245 self.raw.memlimit_stop = memlimit;
1246 self
1247 }
1248
1249 /// Calculate approximate memory usage of multithreaded .xz encoder
1250 #[inline]
1251 pub fn memusage(&self) -> u64 {
1252 unsafe { liblzma_sys::lzma_stream_encoder_mt_memusage(&self.raw) }
1253 }
1254
1255 /// Initialize multithreaded .xz stream encoder.
1256 #[inline]
1257 pub fn encoder(&self) -> Result<Stream, Error> {
1258 let mut init = unsafe { Stream::zeroed() };
1259 cvt(unsafe { liblzma_sys::lzma_stream_encoder_mt(&mut init.raw, &self.raw) })?;
1260 Ok(init)
1261 }
1262
1263 /// Initialize multithreaded .xz stream decoder.
1264 #[inline]
1265 pub fn decoder(&self) -> Result<Stream, Error> {
1266 let mut init = unsafe { Stream::zeroed() };
1267 cvt(unsafe { liblzma_sys::lzma_stream_decoder_mt(&mut init.raw, &self.raw) })?;
1268 Ok(init)
1269 }
1270}
1271
1272fn cvt(rc: liblzma_sys::lzma_ret) -> Result<Status, Error> {
1273 match rc {
1274 liblzma_sys::LZMA_OK => Ok(Status::Ok),
1275 liblzma_sys::LZMA_STREAM_END => Ok(Status::StreamEnd),
1276 liblzma_sys::LZMA_NO_CHECK => Err(Error::NoCheck),
1277 liblzma_sys::LZMA_UNSUPPORTED_CHECK => Err(Error::UnsupportedCheck),
1278 liblzma_sys::LZMA_GET_CHECK => Ok(Status::GetCheck),
1279 liblzma_sys::LZMA_MEM_ERROR => Err(Error::Mem),
1280 liblzma_sys::LZMA_MEMLIMIT_ERROR => Err(Error::MemLimit),
1281 liblzma_sys::LZMA_FORMAT_ERROR => Err(Error::Format),
1282 liblzma_sys::LZMA_OPTIONS_ERROR => Err(Error::Options),
1283 liblzma_sys::LZMA_DATA_ERROR => Err(Error::Data),
1284 liblzma_sys::LZMA_BUF_ERROR => Ok(Status::MemNeeded),
1285 liblzma_sys::LZMA_PROG_ERROR => Err(Error::Program),
1286 c => panic!("unknown return code: {}", c),
1287 }
1288}
1289
1290impl From<Error> for io::Error {
1291 #[inline]
1292 fn from(e: Error) -> io::Error {
1293 let kind = match e {
1294 Error::Data => io::ErrorKind::InvalidData,
1295 Error::Options => io::ErrorKind::InvalidInput,
1296 Error::Format => io::ErrorKind::InvalidData,
1297 Error::MemLimit => io::ErrorKind::Other,
1298 Error::Mem => io::ErrorKind::Other,
1299 Error::Program => io::ErrorKind::Other,
1300 Error::NoCheck => io::ErrorKind::InvalidInput,
1301 Error::UnsupportedCheck => io::ErrorKind::Other,
1302 };
1303
1304 io::Error::new(kind, e)
1305 }
1306}
1307
1308impl error::Error for Error {}
1309
1310impl fmt::Display for Error {
1311 #[inline]
1312 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1313 match self {
1314 Error::Data => "lzma data error",
1315 Error::Options => "invalid options",
1316 Error::Format => "stream/file format not recognized",
1317 Error::MemLimit => "memory limit reached",
1318 Error::Mem => "can't allocate memory",
1319 Error::Program => "liblzma internal error",
1320 Error::NoCheck => "no integrity check was available",
1321 Error::UnsupportedCheck => "liblzma not built with check support",
1322 }
1323 .fmt(f)
1324 }
1325}
1326
1327impl Drop for Stream {
1328 #[inline]
1329 fn drop(&mut self) {
1330 unsafe {
1331 liblzma_sys::lzma_end(&mut self.raw);
1332 }
1333 }
1334}