diff options
Diffstat (limited to 'vendor/xz2/src/stream.rs')
-rw-r--r-- | vendor/xz2/src/stream.rs | 843 |
1 files changed, 843 insertions, 0 deletions
diff --git a/vendor/xz2/src/stream.rs b/vendor/xz2/src/stream.rs new file mode 100644 index 000000000..b005f26d8 --- /dev/null +++ b/vendor/xz2/src/stream.rs @@ -0,0 +1,843 @@ +//! 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 { + io::Error::new(io::ErrorKind::Other, e) + } +} + +impl error::Error for Error { + fn description(&self) -> &str { "lzma data error" } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + error::Error::description(self).fmt(f) + } +} + +impl Drop for Stream { + fn drop(&mut self) { + unsafe { + lzma_sys::lzma_end(&mut self.raw); + } + } +} + |