blob: 88681f1fbc61f500e53557537756f710573e697d [file] [log] [blame] [edit]
//! Raw in-memory LZMA streams.
//!
//! The `Stream` type exported by this module is the primary type which performs
//! encoding/decoding of LZMA streams. Each `Stream` is either an encoder or
//! decoder and processes data in a streaming fashion.
use std::collections::LinkedList;
use std::error;
use std::fmt;
use std::io;
use std::mem;
use std::slice;
use lzma_sys;
/// Representation of an in-memory LZMA encoding or decoding stream.
///
/// Wraps the raw underlying `lzma_stream` type and provides the ability to
/// create streams which can either decode or encode various LZMA-based formats.
pub struct Stream {
raw: lzma_sys::lzma_stream,
}
unsafe impl Send for Stream {}
unsafe impl Sync for Stream {}
/// Options that can be used to configure how LZMA encoding happens.
///
/// This builder is consumed by a number of other methods.
pub struct LzmaOptions {
raw: lzma_sys::lzma_options_lzma,
}
/// Builder to create a multi-threaded stream encoder.
pub struct MtStreamBuilder {
raw: lzma_sys::lzma_mt,
filters: Option<Filters>,
}
/// A custom chain of filters to configure an encoding stream.
pub struct Filters {
inner: Vec<lzma_sys::lzma_filter>,
lzma_opts: LinkedList<lzma_sys::lzma_options_lzma>,
}
/// The `action` argument for `process`,
///
/// After the first use of SyncFlush, FullFlush, FullBarrier, or Finish, the
/// same `action' must is used until `process` returns `Status::StreamEnd`.
/// Also, the amount of input must not be modified by the application until
/// `process` returns `Status::StreamEnd`. Changing the `action' or modifying
/// the amount of input will make `process` return `Error::Program`.
#[derive(Copy, Clone)]
pub enum Action {
/// Continue processing
///
/// When encoding, encode as much input as possible. Some internal buffering
/// will probably be done (depends on the filter chain in use), which causes
/// latency: the input used won't usually be decodeable from the output of
/// the same `process` call.
///
/// When decoding, decode as much input as possible and produce as much
/// output as possible.
Run = lzma_sys::LZMA_RUN as isize,
/// Make all the input available at output
///
/// Normally the encoder introduces some latency. `SyncFlush` forces all the
/// buffered data to be available at output without resetting the internal
/// state of the encoder. This way it is possible to use compressed stream
/// for example for communication over network.
///
/// Only some filters support `SyncFlush`. Trying to use `SyncFlush` with
/// filters that don't support it will make `process` return
/// `Error::Options`. For example, LZMA1 doesn't support `SyncFlush` but
/// LZMA2 does.
///
/// Using `SyncFlush` very often can dramatically reduce the compression
/// ratio. With some filters (for example, LZMA2), fine-tuning the
/// compression options may help mitigate this problem significantly (for
/// example, match finder with LZMA2).
///
/// Decoders don't support `SyncFlush`.
SyncFlush = lzma_sys::LZMA_SYNC_FLUSH as isize,
/// Finish encoding of the current block.
///
/// All the input data going to the current block must have been given to
/// the encoder. Call `process` with `FullFlush` until it returns
/// `Status::StreamEnd`. Then continue normally with `Run` or finish the
/// Stream with `Finish`.
///
/// This action is currently supported only by stream encoder and easy
/// encoder (which uses stream encoder). If there is no unfinished block, no
/// empty block is created.
FullFlush = lzma_sys::LZMA_FULL_FLUSH as isize,
/// Finish encoding of the current block.
///
/// This is like `FullFlush` except that this doesn't necessarily wait until
/// all the input has been made available via the output buffer. That is,
/// `process` might return `Status::StreamEnd` as soon as all the input has
/// been consumed.
///
/// `FullBarrier` is useful with a threaded encoder if one wants to split
/// the .xz Stream into blocks at specific offsets but doesn't care if the
/// output isn't flushed immediately. Using `FullBarrier` allows keeping the
/// threads busy while `FullFlush` would make `process` wait until all the
/// threads have finished until more data could be passed to the encoder.
///
/// With a `Stream` initialized with the single-threaded
/// `new_stream_encoder` or `new_easy_encoder`, `FullBarrier` is an alias
/// for `FullFlush`.
FullBarrier = lzma_sys::LZMA_FULL_BARRIER as isize,
/// Finish the current operation
///
/// All the input data must have been given to the encoder (the last bytes
/// can still be pending in next_in). Call `process` with `Finish` until it
/// returns `Status::StreamEnd`. Once `Finish` has been used, the amount of
/// input must no longer be changed by the application.
///
/// When decoding, using `Finish` is optional unless the concatenated flag
/// was used when the decoder was initialized. When concatenated was not
/// used, the only effect of `Finish` is that the amount of input must not
/// be changed just like in the encoder.
Finish = lzma_sys::LZMA_FINISH as isize,
}
/// Return value of a `process` operation.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Status {
/// Operation completed successfully.
Ok,
/// End of stream was reached.
///
/// When encoding, this means that a sync/full flush or `Finish` was
/// completed. When decoding, this indicates that all data was decoded
/// successfully.
StreamEnd,
/// If the TELL_ANY_CHECK flags is specified when constructing a decoder,
/// this informs that the `check` method will now return the underlying
/// integrity check algorithm.
GetCheck,
/// An error has not been encountered, but no progress is possible.
///
/// Processing can be continued normally by providing more input and/or more
/// output space, if possible.
///
/// Typically the first call to `process` that can do no progress returns
/// `Ok` instead of `MemNeeded`. Only the second consecutive call doing no
/// progress will return `MemNeeded`.
MemNeeded,
}
/// Possible error codes that can be returned from a processing operation.
#[derive(Debug, Clone, PartialEq)]
pub enum Error {
/// The underlying data was corrupt.
Data,
/// Invalid or unsupported options were specified.
Options,
/// File format wasn't recognized.
Format,
/// Memory usage limit was reached.
///
/// The memory limit can be increased with `set_memlimit`
MemLimit,
/// Memory couldn't be allocated.
Mem,
/// A programming error was encountered.
Program,
/// The `TELL_NO_CHECK` flag was specified and no integrity check was
/// available for this stream.
NoCheck,
/// The `TELL_UNSUPPORTED_CHECK` flag was specified and no integrity check
/// isn't implemented in this build of liblzma for this stream.
UnsupportedCheck,
}
/// Possible integrity checks that can be part of a .xz stream.
#[allow(missing_docs)] // self explanatory mostly
#[derive(Copy, Clone)]
pub enum Check {
None = lzma_sys::LZMA_CHECK_NONE as isize,
Crc32 = lzma_sys::LZMA_CHECK_CRC32 as isize,
Crc64 = lzma_sys::LZMA_CHECK_CRC64 as isize,
Sha256 = lzma_sys::LZMA_CHECK_SHA256 as isize,
}
/// Compression modes
///
/// This selects the function used to analyze the data produced by the match
/// finder.
#[derive(Copy, Clone)]
pub enum Mode {
/// Fast compression.
///
/// Fast mode is usually at its best when combined with a hash chain match
/// finder.
Fast = lzma_sys::LZMA_MODE_FAST as isize,
/// Normal compression.
///
/// This is usually notably slower than fast mode. Use this together with
/// binary tree match finders to expose the full potential of the LZMA1 or
/// LZMA2 encoder.
Normal = lzma_sys::LZMA_MODE_NORMAL as isize,
}
/// Match finders
///
/// Match finder has major effect on both speed and compression ratio. Usually
/// hash chains are faster than binary trees.
///
/// If you will use `SyncFlush` often, the hash chains may be a better choice,
/// because binary trees get much higher compression ratio penalty with
/// `SyncFlush`.
///
/// The memory usage formulas are only rough estimates, which are closest to
/// reality when dict_size is a power of two. The formulas are more complex in
/// reality, and can also change a little between liblzma versions.
#[derive(Copy, Clone)]
pub enum MatchFinder {
/// Hash Chain with 2- and 3-byte hashing
HashChain3 = lzma_sys::LZMA_MF_HC3 as isize,
/// Hash Chain with 2-, 3-, and 4-byte hashing
HashChain4 = lzma_sys::LZMA_MF_HC4 as isize,
/// Binary Tree with 2-byte hashing
BinaryTree2 = lzma_sys::LZMA_MF_BT2 as isize,
/// Binary Tree with 2- and 3-byte hashing
BinaryTree3 = lzma_sys::LZMA_MF_BT3 as isize,
/// Binary Tree with 2-, 3-, and 4-byte hashing
BinaryTree4 = lzma_sys::LZMA_MF_BT4 as isize,
}
/// A flag passed when initializing a decoder, causes `process` to return
/// `Status::GetCheck` as soon as the integrity check is known.
pub const TELL_ANY_CHECK: u32 = lzma_sys::LZMA_TELL_ANY_CHECK;
/// A flag passed when initializing a decoder, causes `process` to return
/// `Error::NoCheck` if the stream being decoded has no integrity check.
pub const TELL_NO_CHECK: u32 = lzma_sys::LZMA_TELL_NO_CHECK;
/// A flag passed when initializing a decoder, causes `process` to return
/// `Error::UnsupportedCheck` if the stream being decoded has an integrity check
/// that cannot be verified by this build of liblzma.
pub const TELL_UNSUPPORTED_CHECK: u32 = lzma_sys::LZMA_TELL_UNSUPPORTED_CHECK;
/// A flag passed when initializing a decoder, causes the decoder to ignore any
/// integrity checks listed.
pub const IGNORE_CHECK: u32 = lzma_sys::LZMA_TELL_UNSUPPORTED_CHECK;
/// A flag passed when initializing a decoder, indicates that the stream may be
/// multiple concatenated xz files.
pub const CONCATENATED: u32 = lzma_sys::LZMA_CONCATENATED;
impl Stream {
/// Initialize .xz stream encoder using a preset number
///
/// This is intended to be used by most for encoding data. The `preset`
/// argument is a number 0-9 indicating the compression level to use, and
/// normally 6 is a reasonable default.
///
/// The `check` argument is the integrity check to insert at the end of the
/// stream. The default of `Crc64` is typically appropriate.
pub fn new_easy_encoder(preset: u32, check: Check) -> Result<Stream, Error> {
unsafe {
let mut init = Stream { raw: mem::zeroed() };
cvt(lzma_sys::lzma_easy_encoder(
&mut init.raw,
preset,
check as lzma_sys::lzma_check,
))?;
Ok(init)
}
}
/// Initialize .lzma encoder (legacy file format)
///
/// The .lzma format is sometimes called the LZMA_Alone format, which is the
/// reason for the name of this function. The .lzma format supports only the
/// LZMA1 filter. There is no support for integrity checks like CRC32.
///
/// Use this function if and only if you need to create files readable by
/// legacy LZMA tools such as LZMA Utils 4.32.x. Moving to the .xz format
/// (the `new_easy_encoder` function) is strongly recommended.
///
/// The valid action values for `process` are `Run` and `Finish`. No kind
/// of flushing is supported, because the file format doesn't make it
/// possible.
pub fn new_lzma_encoder(options: &LzmaOptions) -> Result<Stream, Error> {
unsafe {
let mut init = Stream { raw: mem::zeroed() };
cvt(lzma_sys::lzma_alone_encoder(&mut init.raw, &options.raw))?;
Ok(init)
}
}
/// Initialize .xz Stream encoder using a custom filter chain
///
/// This function is similar to `new_easy_encoder` but a custom filter chain
/// is specified.
pub fn new_stream_encoder(filters: &Filters, check: Check) -> Result<Stream, Error> {
unsafe {
let mut init = Stream { raw: mem::zeroed() };
cvt(lzma_sys::lzma_stream_encoder(
&mut init.raw,
filters.inner.as_ptr(),
check as lzma_sys::lzma_check,
))?;
Ok(init)
}
}
/// Initialize a .xz stream decoder.
///
/// The maximum memory usage can be specified along with flags such as
/// `TELL_ANY_CHECK`, `TELL_NO_CHECK`, `TELL_UNSUPPORTED_CHECK`,
/// `TELL_IGNORE_CHECK`, or `CONCATENATED`.
pub fn new_stream_decoder(memlimit: u64, flags: u32) -> Result<Stream, Error> {
unsafe {
let mut init = Stream { raw: mem::zeroed() };
cvt(lzma_sys::lzma_stream_decoder(
&mut init.raw,
memlimit,
flags,
))?;
Ok(init)
}
}
/// Initialize a .lzma stream decoder.
///
/// The maximum memory usage can also be specified.
pub fn new_lzma_decoder(memlimit: u64) -> Result<Stream, Error> {
unsafe {
let mut init = Stream { raw: mem::zeroed() };
cvt(lzma_sys::lzma_alone_decoder(&mut init.raw, memlimit))?;
Ok(init)
}
}
/// Initialize a decoder which will choose a stream/lzma formats depending
/// on the input stream.
pub fn new_auto_decoder(memlimit: u64, flags: u32) -> Result<Stream, Error> {
unsafe {
let mut init = Stream { raw: mem::zeroed() };
cvt(lzma_sys::lzma_auto_decoder(&mut init.raw, memlimit, flags))?;
Ok(init)
}
}
/// Processes some data from input into an output buffer.
///
/// This will perform the appropriate encoding or decoding operation
/// depending on the kind of underlying stream. Documentation for the
/// various `action` arguments can be found on the respective variants.
pub fn process(
&mut self,
input: &[u8],
output: &mut [u8],
action: Action,
) -> Result<Status, Error> {
self.raw.next_in = input.as_ptr();
self.raw.avail_in = input.len();
self.raw.next_out = output.as_mut_ptr();
self.raw.avail_out = output.len();
let action = action as lzma_sys::lzma_action;
unsafe { cvt(lzma_sys::lzma_code(&mut self.raw, action)) }
}
/// Performs the same data as `process`, but places output data in a `Vec`.
///
/// This function will use the extra capacity of `output` as a destination
/// for bytes to be placed. The length of `output` will automatically get
/// updated after the operation has completed.
pub fn process_vec(
&mut self,
input: &[u8],
output: &mut Vec<u8>,
action: Action,
) -> Result<Status, Error> {
let cap = output.capacity();
let len = output.len();
unsafe {
let before = self.total_out();
let ret = {
let ptr = output.as_mut_ptr().offset(len as isize);
let out = slice::from_raw_parts_mut(ptr, cap - len);
self.process(input, out, action)
};
output.set_len((self.total_out() - before) as usize + len);
return ret;
}
}
/// Returns the total amount of input bytes consumed by this stream.
pub fn total_in(&self) -> u64 {
self.raw.total_in
}
/// Returns the total amount of bytes produced by this stream.
pub fn total_out(&self) -> u64 {
self.raw.total_out
}
/// Get the current memory usage limit.
///
/// This is only supported if the underlying stream supports a memlimit.
pub fn memlimit(&self) -> u64 {
unsafe { lzma_sys::lzma_memlimit_get(&self.raw) }
}
/// Set the current memory usage limit.
///
/// This can return `Error::MemLimit` if the new limit is too small or
/// `Error::Program` if this stream doesn't take a memory limit.
pub fn set_memlimit(&mut self, limit: u64) -> Result<(), Error> {
cvt(unsafe { lzma_sys::lzma_memlimit_set(&mut self.raw, limit) }).map(|_| ())
}
}
impl LzmaOptions {
/// Creates a new blank set of options for encoding.
///
/// The `preset` argument is the compression level to use, typically in the
/// range of 0-9.
pub fn new_preset(preset: u32) -> Result<LzmaOptions, Error> {
unsafe {
let mut options = LzmaOptions { raw: mem::zeroed() };
let ret = lzma_sys::lzma_lzma_preset(&mut options.raw, preset);
if ret != 0 {
Err(Error::Program)
} else {
Ok(options)
}
}
}
/// Configures the dictionary size, in bytes
///
/// Dictionary size indicates how many bytes of the recently processed
/// uncompressed data is kept in memory.
///
/// The minimum dictionary size is 4096 bytes and the default is 2^23, 8MB.
pub fn dict_size(&mut self, size: u32) -> &mut LzmaOptions {
self.raw.dict_size = size;
self
}
/// Configures the number of literal context bits.
///
/// How many of the highest bits of the previous uncompressed eight-bit byte
/// (also known as `literal') are taken into account when predicting the
/// bits of the next literal.
///
/// The maximum value to this is 4 and the default is 3. It is not currently
/// supported if this plus `literal_position_bits` is greater than 4.
pub fn literal_context_bits(&mut self, bits: u32) -> &mut LzmaOptions {
self.raw.lc = bits;
self
}
/// Configures the number of literal position bits.
///
/// This affects what kind of alignment in the uncompressed data is assumed
/// when encoding literals. A literal is a single 8-bit byte. See
/// `position_bits` for more information about alignment.
///
/// The default for this is 0.
pub fn literal_position_bits(&mut self, bits: u32) -> &mut LzmaOptions {
self.raw.lp = bits;
self
}
/// Configures the number of position bits.
///
/// Position bits affects what kind of alignment in the uncompressed data is
/// assumed in general. The default of 2 means four-byte alignment (2^ pb
/// =2^2=4), which is often a good choice when there's no better guess.
///
/// When the aligment is known, setting pb accordingly may reduce the file
/// size a little. E.g. with text files having one-byte alignment (US-ASCII,
/// ISO-8859-*, UTF-8), setting pb=0 can improve compression slightly. For
/// UTF-16 text, pb=1 is a good choice. If the alignment is an odd number
/// like 3 bytes, pb=0 might be the best choice.
///
/// Even though the assumed alignment can be adjusted with pb and lp, LZMA1
/// and LZMA2 still slightly favor 16-byte alignment. It might be worth
/// taking into account when designing file formats that are likely to be
/// often compressed with LZMA1 or LZMA2.
pub fn position_bits(&mut self, bits: u32) -> &mut LzmaOptions {
self.raw.pb = bits;
self
}
/// Configures the compression mode.
pub fn mode(&mut self, mode: Mode) -> &mut LzmaOptions {
self.raw.mode = mode as lzma_sys::lzma_mode;
self
}
/// Configures the nice length of a match.
///
/// This determines how many bytes the encoder compares from the match
/// candidates when looking for the best match. Once a match of at least
/// `nice_len` bytes long is found, the encoder stops looking for better
/// candidates and encodes the match. (Naturally, if the found match is
/// actually longer than `nice_len`, the actual length is encoded; it's not
/// truncated to `nice_len`.)
///
/// Bigger values usually increase the compression ratio and compression
/// time. For most files, 32 to 128 is a good value, which gives very good
/// compression ratio at good speed.
///
/// The exact minimum value depends on the match finder. The maximum is 273,
/// which is the maximum length of a match that LZMA1 and LZMA2 can encode.
pub fn nice_len(&mut self, len: u32) -> &mut LzmaOptions {
self.raw.nice_len = len;
self
}
/// Configures the match finder ID.
pub fn match_finder(&mut self, mf: MatchFinder) -> &mut LzmaOptions {
self.raw.mf = mf as lzma_sys::lzma_match_finder;
self
}
/// Maximum search depth in the match finder.
///
/// For every input byte, match finder searches through the hash chain or
/// binary tree in a loop, each iteration going one step deeper in the chain
/// or tree. The searching stops if
///
/// - a match of at least `nice_len` bytes long is found;
/// - all match candidates from the hash chain or binary tree have
/// been checked; or
/// - maximum search depth is reached.
///
/// Maximum search depth is needed to prevent the match finder from wasting
/// too much time in case there are lots of short match candidates. On the
/// other hand, stopping the search before all candidates have been checked
/// can reduce compression ratio.
///
/// Setting depth to zero tells liblzma to use an automatic default value,
/// that depends on the selected match finder and nice_len. The default is
/// in the range [4, 200] or so (it may vary between liblzma versions).
///
/// Using a bigger depth value than the default can increase compression
/// ratio in some cases. There is no strict maximum value, but high values
/// (thousands or millions) should be used with care: the encoder could
/// remain fast enough with typical input, but malicious input could cause
/// the match finder to slow down dramatically, possibly creating a denial
/// of service attack.
pub fn depth(&mut self, depth: u32) -> &mut LzmaOptions {
self.raw.depth = depth;
self
}
}
impl Check {
/// Test if this check is supported in this build of liblzma.
pub fn is_supported(&self) -> bool {
let ret = unsafe { lzma_sys::lzma_check_is_supported(*self as lzma_sys::lzma_check) };
ret != 0
}
}
impl MatchFinder {
/// Test if this match finder is supported in this build of liblzma.
pub fn is_supported(&self) -> bool {
let ret = unsafe { lzma_sys::lzma_mf_is_supported(*self as lzma_sys::lzma_match_finder) };
ret != 0
}
}
impl Filters {
/// Creates a new filter chain with no filters.
pub fn new() -> Filters {
Filters {
inner: vec![lzma_sys::lzma_filter {
id: lzma_sys::LZMA_VLI_UNKNOWN,
options: 0 as *mut _,
}],
lzma_opts: LinkedList::new(),
}
}
/// Add an LZMA1 filter.
///
/// LZMA1 is the very same thing as what was called just LZMA in LZMA Utils,
/// 7-Zip, and LZMA SDK. It's called LZMA1 here to prevent developers from
/// accidentally using LZMA when they actually want LZMA2.
///
/// LZMA1 shouldn't be used for new applications unless you _really_ know
/// what you are doing. LZMA2 is almost always a better choice.
pub fn lzma1(&mut self, opts: &LzmaOptions) -> &mut Filters {
self.lzma_opts.push_back(opts.raw);
let ptr = self.lzma_opts.back().unwrap() as *const _ as *mut _;
self.push(lzma_sys::lzma_filter {
id: lzma_sys::LZMA_FILTER_LZMA1,
options: ptr,
})
}
/// Add an LZMA2 filter.
///
/// Usually you want this instead of LZMA1. Compared to LZMA1, LZMA2 adds
/// support for `SyncFlush`, uncompressed chunks (smaller expansion when
/// trying to compress uncompressible data), possibility to change
/// `literal_context_bits`/`literal_position_bits`/`position_bits` in the
/// middle of encoding, and some other internal improvements.
pub fn lzma2(&mut self, opts: &LzmaOptions) -> &mut Filters {
self.lzma_opts.push_back(opts.raw);
let ptr = self.lzma_opts.back().unwrap() as *const _ as *mut _;
self.push(lzma_sys::lzma_filter {
id: lzma_sys::LZMA_FILTER_LZMA2,
options: ptr,
})
}
// TODO: delta filter
/// Add a filter for x86 binaries.
pub fn x86(&mut self) -> &mut Filters {
self.push(lzma_sys::lzma_filter {
id: lzma_sys::LZMA_FILTER_X86,
options: 0 as *mut _,
})
}
/// Add a filter for PowerPC binaries.
pub fn powerpc(&mut self) -> &mut Filters {
self.push(lzma_sys::lzma_filter {
id: lzma_sys::LZMA_FILTER_POWERPC,
options: 0 as *mut _,
})
}
/// Add a filter for IA-64 (itanium) binaries.
pub fn ia64(&mut self) -> &mut Filters {
self.push(lzma_sys::lzma_filter {
id: lzma_sys::LZMA_FILTER_IA64,
options: 0 as *mut _,
})
}
/// Add a filter for ARM binaries.
pub fn arm(&mut self) -> &mut Filters {
self.push(lzma_sys::lzma_filter {
id: lzma_sys::LZMA_FILTER_ARM,
options: 0 as *mut _,
})
}
/// Add a filter for ARM-Thumb binaries.
pub fn arm_thumb(&mut self) -> &mut Filters {
self.push(lzma_sys::lzma_filter {
id: lzma_sys::LZMA_FILTER_ARMTHUMB,
options: 0 as *mut _,
})
}
/// Add a filter for SPARC binaries.
pub fn sparc(&mut self) -> &mut Filters {
self.push(lzma_sys::lzma_filter {
id: lzma_sys::LZMA_FILTER_SPARC,
options: 0 as *mut _,
})
}
fn push(&mut self, filter: lzma_sys::lzma_filter) -> &mut Filters {
let pos = self.inner.len() - 1;
self.inner.insert(pos, filter);
self
}
}
impl MtStreamBuilder {
/// Creates a new blank builder to create a multithreaded encoding `Stream`.
pub fn new() -> MtStreamBuilder {
unsafe {
let mut init = MtStreamBuilder {
raw: mem::zeroed(),
filters: None,
};
init.raw.threads = 1;
return init;
}
}
/// Configures the number of worker threads to use
pub fn threads(&mut self, threads: u32) -> &mut Self {
self.raw.threads = threads;
self
}
/// Configures the maximum uncompressed size of a block
///
/// The encoder will start a new .xz block every `block_size` bytes.
/// Using `FullFlush` or `FullBarrier` with `process` the caller may tell
/// liblzma to start a new block earlier.
///
/// With LZMA2, a recommended block size is 2-4 times the LZMA2 dictionary
/// size. With very small dictionaries, it is recommended to use at least 1
/// MiB block size for good compression ratio, even if this is more than
/// four times the dictionary size. Note that these are only recommendations
/// for typical use cases; feel free to use other values. Just keep in mind
/// that using a block size less than the LZMA2 dictionary size is waste of
/// RAM.
///
/// Set this to 0 to let liblzma choose the block size depending on the
/// compression options. For LZMA2 it will be 3*`dict_size` or 1 MiB,
/// whichever is more.
///
/// For each thread, about 3 * `block_size` bytes of memory will be
/// allocated. This may change in later liblzma versions. If so, the memory
/// usage will probably be reduced, not increased.
pub fn block_size(&mut self, block_size: u64) -> &mut Self {
self.raw.block_size = block_size;
self
}
/// Timeout to allow `process` to return early
///
/// Multithreading can make liblzma to consume input and produce output in a
/// very bursty way: it may first read a lot of input to fill internal
/// buffers, then no input or output occurs for a while.
///
/// In single-threaded mode, `process` won't return until it has either
/// consumed all the input or filled the output buffer. If this is done in
/// multithreaded mode, it may cause a call `process` to take even tens of
/// seconds, which isn't acceptable in all applications.
///
/// To avoid very long blocking times in `process`, a timeout (in
/// milliseconds) may be set here. If `process would block longer than
/// this number of milliseconds, it will return with `Ok`. Reasonable
/// values are 100 ms or more. The xz command line tool uses 300 ms.
///
/// If long blocking times are fine for you, set timeout to a special
/// value of 0, which will disable the timeout mechanism and will make
/// `process` block until all the input is consumed or the output
/// buffer has been filled.
pub fn timeout_ms(&mut self, timeout: u32) -> &mut Self {
self.raw.timeout = timeout;
self
}
/// Compression preset (level and possible flags)
///
/// The preset is set just like with `Stream::new_easy_encoder`. The preset
/// is ignored if filters below have been specified.
pub fn preset(&mut self, preset: u32) -> &mut Self {
self.raw.preset = preset;
self
}
/// Configure a custom filter chain
pub fn filters(&mut self, filters: Filters) -> &mut Self {
self.raw.filters = filters.inner.as_ptr();
self.filters = Some(filters);
self
}
/// Configures the integrity check type
pub fn check(&mut self, check: Check) -> &mut Self {
self.raw.check = check as lzma_sys::lzma_check;
self
}
/// Calculate approximate memory usage of multithreaded .xz encoder
pub fn memusage(&self) -> u64 {
unsafe { lzma_sys::lzma_stream_encoder_mt_memusage(&self.raw) }
}
/// Initialize multithreaded .xz stream encoder.
pub fn encoder(&self) -> Result<Stream, Error> {
unsafe {
let mut init = Stream { raw: mem::zeroed() };
cvt(lzma_sys::lzma_stream_encoder_mt(&mut init.raw, &self.raw))?;
Ok(init)
}
}
}
fn cvt(rc: lzma_sys::lzma_ret) -> Result<Status, Error> {
match rc {
lzma_sys::LZMA_OK => Ok(Status::Ok),
lzma_sys::LZMA_STREAM_END => Ok(Status::StreamEnd),
lzma_sys::LZMA_NO_CHECK => Err(Error::NoCheck),
lzma_sys::LZMA_UNSUPPORTED_CHECK => Err(Error::UnsupportedCheck),
lzma_sys::LZMA_GET_CHECK => Ok(Status::GetCheck),
lzma_sys::LZMA_MEM_ERROR => Err(Error::Mem),
lzma_sys::LZMA_MEMLIMIT_ERROR => Err(Error::MemLimit),
lzma_sys::LZMA_FORMAT_ERROR => Err(Error::Format),
lzma_sys::LZMA_OPTIONS_ERROR => Err(Error::Options),
lzma_sys::LZMA_DATA_ERROR => Err(Error::Data),
lzma_sys::LZMA_BUF_ERROR => Ok(Status::MemNeeded),
lzma_sys::LZMA_PROG_ERROR => Err(Error::Program),
c => panic!("unknown return code: {}", c),
}
}
impl From<Error> for io::Error {
fn from(e: Error) -> io::Error {
let kind = match e {
Error::Data => std::io::ErrorKind::InvalidData,
Error::Options => std::io::ErrorKind::InvalidInput,
Error::Format => std::io::ErrorKind::InvalidData,
Error::MemLimit => std::io::ErrorKind::Other,
Error::Mem => std::io::ErrorKind::Other,
Error::Program => std::io::ErrorKind::Other,
Error::NoCheck => std::io::ErrorKind::InvalidInput,
Error::UnsupportedCheck => std::io::ErrorKind::Other,
};
io::Error::new(kind, e)
}
}
impl error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Data => "lzma data error",
Error::Options => "invalid options",
Error::Format => "stream/file format not recognized",
Error::MemLimit => "memory limit reached",
Error::Mem => "can't allocate memory",
Error::Program => "liblzma internal error",
Error::NoCheck => "no integrity check was available",
Error::UnsupportedCheck => "liblzma not built with check support",
}
.fmt(f)
}
}
impl Drop for Stream {
fn drop(&mut self) {
unsafe {
lzma_sys::lzma_end(&mut self.raw);
}
}
}