use std::collections::LinkedList;
use std::error;
use std::fmt;
use std::io;
use std::mem;
use std::slice;
pub struct Stream {
raw: liblzma_sys::lzma_stream,
}
unsafe impl Send for Stream {}
unsafe impl Sync for Stream {}
pub struct LzmaOptions {
raw: liblzma_sys::lzma_options_lzma,
}
#[cfg(feature = "parallel")]
pub struct MtStreamBuilder {
raw: liblzma_sys::lzma_mt,
filters: Option<Filters>,
}
pub struct Filters {
inner: Vec<liblzma_sys::lzma_filter>,
lzma_opts: LinkedList<liblzma_sys::lzma_options_lzma>,
}
#[derive(Copy, Clone)]
pub enum Action {
Run = liblzma_sys::LZMA_RUN as isize,
SyncFlush = liblzma_sys::LZMA_SYNC_FLUSH as isize,
FullFlush = liblzma_sys::LZMA_FULL_FLUSH as isize,
FullBarrier = liblzma_sys::LZMA_FULL_BARRIER as isize,
Finish = liblzma_sys::LZMA_FINISH as isize,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Status {
Ok,
StreamEnd,
GetCheck,
MemNeeded,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Error {
Data,
Options,
Format,
MemLimit,
Mem,
Program,
NoCheck,
UnsupportedCheck,
}
#[allow(missing_docs)] #[derive(Copy, Clone)]
pub enum Check {
None = liblzma_sys::LZMA_CHECK_NONE as isize,
Crc32 = liblzma_sys::LZMA_CHECK_CRC32 as isize,
Crc64 = liblzma_sys::LZMA_CHECK_CRC64 as isize,
Sha256 = liblzma_sys::LZMA_CHECK_SHA256 as isize,
}
#[derive(Copy, Clone)]
pub enum Mode {
Fast = liblzma_sys::LZMA_MODE_FAST as isize,
Normal = liblzma_sys::LZMA_MODE_NORMAL as isize,
}
#[derive(Copy, Clone)]
pub enum MatchFinder {
HashChain3 = liblzma_sys::LZMA_MF_HC3 as isize,
HashChain4 = liblzma_sys::LZMA_MF_HC4 as isize,
BinaryTree2 = liblzma_sys::LZMA_MF_BT2 as isize,
BinaryTree3 = liblzma_sys::LZMA_MF_BT3 as isize,
BinaryTree4 = liblzma_sys::LZMA_MF_BT4 as isize,
}
pub const TELL_ANY_CHECK: u32 = liblzma_sys::LZMA_TELL_ANY_CHECK;
pub const TELL_NO_CHECK: u32 = liblzma_sys::LZMA_TELL_NO_CHECK;
pub const TELL_UNSUPPORTED_CHECK: u32 = liblzma_sys::LZMA_TELL_UNSUPPORTED_CHECK;
pub const IGNORE_CHECK: u32 = liblzma_sys::LZMA_TELL_UNSUPPORTED_CHECK;
pub const CONCATENATED: u32 = liblzma_sys::LZMA_CONCATENATED;
impl Stream {
#[inline]
unsafe fn zeroed() -> Self {
Self {
raw: unsafe { mem::zeroed() },
}
}
#[inline]
pub fn new_easy_encoder(preset: u32, check: Check) -> Result<Stream, Error> {
let mut init = unsafe { Stream::zeroed() };
cvt(unsafe {
liblzma_sys::lzma_easy_encoder(&mut init.raw, preset, check as liblzma_sys::lzma_check)
})?;
Ok(init)
}
#[inline]
pub fn new_lzma_encoder(options: &LzmaOptions) -> Result<Stream, Error> {
let mut init = unsafe { Stream::zeroed() };
cvt(unsafe { liblzma_sys::lzma_alone_encoder(&mut init.raw, &options.raw) })?;
Ok(init)
}
#[inline]
pub fn new_stream_encoder(filters: &Filters, check: Check) -> Result<Stream, Error> {
let mut init = unsafe { Stream::zeroed() };
cvt(unsafe {
liblzma_sys::lzma_stream_encoder(
&mut init.raw,
filters.inner.as_ptr(),
check as liblzma_sys::lzma_check,
)
})?;
Ok(init)
}
#[inline]
pub fn new_stream_decoder(memlimit: u64, flags: u32) -> Result<Stream, Error> {
let mut init = unsafe { Self::zeroed() };
cvt(unsafe { liblzma_sys::lzma_stream_decoder(&mut init.raw, memlimit, flags) })?;
Ok(init)
}
#[inline]
pub fn new_lzma_decoder(memlimit: u64) -> Result<Stream, Error> {
let mut init = unsafe { Self::zeroed() };
cvt(unsafe { liblzma_sys::lzma_alone_decoder(&mut init.raw, memlimit) })?;
Ok(init)
}
#[inline]
pub fn new_auto_decoder(memlimit: u64, flags: u32) -> Result<Stream, Error> {
let mut init = unsafe { Self::zeroed() };
cvt(unsafe { liblzma_sys::lzma_auto_decoder(&mut init.raw, memlimit, flags) })?;
Ok(init)
}
#[inline]
pub fn new_lzip_decoder(memlimit: u64, flags: u32) -> Result<Self, Error> {
let mut init = unsafe { Self::zeroed() };
cvt(unsafe { liblzma_sys::lzma_lzip_decoder(&mut init.raw, memlimit, flags) })?;
Ok(init)
}
#[inline]
pub fn new_raw_decoder(filters: &Filters) -> Result<Stream, Error> {
let mut init = unsafe { Self::zeroed() };
cvt(unsafe { liblzma_sys::lzma_raw_decoder(&mut init.raw, filters.inner.as_ptr()) })?;
Ok(init)
}
#[inline]
pub fn new_raw_encoder(filters: &Filters) -> Result<Stream, Error> {
let mut init = unsafe { Self::zeroed() };
cvt(unsafe { liblzma_sys::lzma_raw_encoder(&mut init.raw, filters.inner.as_ptr()) })?;
Ok(init)
}
#[inline]
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 liblzma_sys::lzma_action;
unsafe { cvt(liblzma_sys::lzma_code(&mut self.raw, action)) }
}
#[inline]
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().add(len);
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);
ret
}
}
#[inline]
pub fn total_in(&self) -> u64 {
self.raw.total_in
}
#[inline]
pub fn total_out(&self) -> u64 {
self.raw.total_out
}
#[inline]
pub fn memlimit(&self) -> u64 {
unsafe { liblzma_sys::lzma_memlimit_get(&self.raw) }
}
#[inline]
pub fn set_memlimit(&mut self, limit: u64) -> Result<(), Error> {
cvt(unsafe { liblzma_sys::lzma_memlimit_set(&mut self.raw, limit) }).map(|_| ())
}
}
impl LzmaOptions {
#[inline]
pub fn new() -> LzmaOptions {
LzmaOptions {
raw: unsafe { mem::zeroed() },
}
}
#[inline]
pub fn new_preset(preset: u32) -> Result<LzmaOptions, Error> {
unsafe {
let mut options = Self::new();
let ret = liblzma_sys::lzma_lzma_preset(&mut options.raw, preset);
if ret != 0 {
Err(Error::Program)
} else {
Ok(options)
}
}
}
#[inline]
pub fn dict_size(&mut self, size: u32) -> &mut LzmaOptions {
self.raw.dict_size = size;
self
}
#[inline]
pub fn literal_context_bits(&mut self, bits: u32) -> &mut LzmaOptions {
self.raw.lc = bits;
self
}
#[inline]
pub fn literal_position_bits(&mut self, bits: u32) -> &mut LzmaOptions {
self.raw.lp = bits;
self
}
#[inline]
pub fn position_bits(&mut self, bits: u32) -> &mut LzmaOptions {
self.raw.pb = bits;
self
}
#[inline]
pub fn mode(&mut self, mode: Mode) -> &mut LzmaOptions {
self.raw.mode = mode as liblzma_sys::lzma_mode;
self
}
#[inline]
pub fn nice_len(&mut self, len: u32) -> &mut LzmaOptions {
self.raw.nice_len = len;
self
}
#[inline]
pub fn match_finder(&mut self, mf: MatchFinder) -> &mut LzmaOptions {
self.raw.mf = mf as liblzma_sys::lzma_match_finder;
self
}
#[inline]
pub fn depth(&mut self, depth: u32) -> &mut LzmaOptions {
self.raw.depth = depth;
self
}
}
impl Check {
#[inline]
pub fn is_supported(&self) -> bool {
let ret = unsafe { liblzma_sys::lzma_check_is_supported(*self as liblzma_sys::lzma_check) };
ret != 0
}
}
impl MatchFinder {
#[inline]
pub fn is_supported(&self) -> bool {
let ret =
unsafe { liblzma_sys::lzma_mf_is_supported(*self as liblzma_sys::lzma_match_finder) };
ret != 0
}
}
impl Filters {
#[inline]
pub fn new() -> Filters {
Filters {
inner: vec![liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_VLI_UNKNOWN,
options: std::ptr::null_mut(),
}],
lzma_opts: LinkedList::new(),
}
}
#[inline]
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(liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_LZMA1,
options: ptr,
})
}
#[inline]
pub fn lzma1_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
let filter = liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_LZMA1,
options: std::ptr::null_mut(),
};
self.push_with_properties(filter, properties)
}
#[inline]
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(liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_LZMA2,
options: ptr,
})
}
#[inline]
pub fn lzma2_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
let filter = liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_LZMA2,
options: std::ptr::null_mut(),
};
self.push_with_properties(filter, properties)
}
#[inline]
pub fn delta(&mut self) -> &mut Filters {
self.push(liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_DELTA,
options: std::ptr::null_mut(),
})
}
#[inline]
pub fn delta_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
let filter = liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_DELTA,
options: std::ptr::null_mut(),
};
self.push_with_properties(filter, properties)
}
#[inline]
pub fn x86(&mut self) -> &mut Filters {
self.push(liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_X86,
options: std::ptr::null_mut(),
})
}
#[inline]
pub fn x86_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
let filter = liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_X86,
options: std::ptr::null_mut(),
};
self.push_with_properties(filter, properties)
}
#[inline]
pub fn powerpc(&mut self) -> &mut Filters {
self.push(liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_POWERPC,
options: std::ptr::null_mut(),
})
}
#[inline]
pub fn powerpc_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
let filter = liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_POWERPC,
options: std::ptr::null_mut(),
};
self.push_with_properties(filter, properties)
}
#[inline]
pub fn ia64(&mut self) -> &mut Filters {
self.push(liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_IA64,
options: std::ptr::null_mut(),
})
}
#[inline]
pub fn ia64_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
let filter = liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_IA64,
options: std::ptr::null_mut(),
};
self.push_with_properties(filter, properties)
}
#[inline]
pub fn arm(&mut self) -> &mut Filters {
self.push(liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_ARM,
options: std::ptr::null_mut(),
})
}
#[inline]
pub fn arm_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
let filter = liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_ARM,
options: std::ptr::null_mut(),
};
self.push_with_properties(filter, properties)
}
#[inline]
pub fn arm64(&mut self) -> &mut Filters {
self.push(liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_ARM64,
options: std::ptr::null_mut(),
})
}
#[inline]
pub fn arm64_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
let filter = liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_ARM64,
options: std::ptr::null_mut(),
};
self.push_with_properties(filter, properties)
}
#[inline]
pub fn riscv(&mut self) -> &mut Filters {
self.push(liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_RISCV,
options: std::ptr::null_mut(),
})
}
#[inline]
pub fn riscv_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
let filter = liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_RISCV,
options: std::ptr::null_mut(),
};
self.push_with_properties(filter, properties)
}
#[inline]
pub fn arm_thumb(&mut self) -> &mut Filters {
self.push(liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_ARMTHUMB,
options: std::ptr::null_mut(),
})
}
#[inline]
pub fn arm_thumb_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
let filter = liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_ARMTHUMB,
options: std::ptr::null_mut(),
};
self.push_with_properties(filter, properties)
}
#[inline]
pub fn sparc(&mut self) -> &mut Filters {
self.push(liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_SPARC,
options: std::ptr::null_mut(),
})
}
#[inline]
pub fn sparc_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> {
let filter = liblzma_sys::lzma_filter {
id: liblzma_sys::LZMA_FILTER_SPARC,
options: std::ptr::null_mut(),
};
self.push_with_properties(filter, properties)
}
#[inline]
fn push(&mut self, filter: liblzma_sys::lzma_filter) -> &mut Filters {
let pos = self.inner.len() - 1;
self.inner.insert(pos, filter);
self
}
#[inline]
fn push_with_properties(
&mut self,
mut filter: liblzma_sys::lzma_filter,
properties: &[u8],
) -> Result<&mut Filters, Error> {
cvt(unsafe {
liblzma_sys::lzma_properties_decode(
&mut filter,
std::ptr::null(),
properties.as_ptr(),
properties.len(),
)
})?;
let pos = self.inner.len() - 1;
self.inner.insert(pos, filter);
Ok(self)
}
#[cfg(feature = "parallel")]
#[inline]
pub fn mt_block_size(&self) -> u64 {
unsafe { liblzma_sys::lzma_mt_block_size(self.inner.as_ptr()) }
}
}
#[cfg(feature = "parallel")]
impl MtStreamBuilder {
#[inline]
pub fn new() -> Self {
let mut init = Self {
raw: unsafe { mem::zeroed() },
filters: None,
};
init.raw.threads = 1;
init
}
#[inline]
pub fn threads(&mut self, threads: u32) -> &mut Self {
self.raw.threads = threads;
self
}
#[inline]
pub fn block_size(&mut self, block_size: u64) -> &mut Self {
self.raw.block_size = block_size;
self
}
#[inline]
pub fn timeout_ms(&mut self, timeout: u32) -> &mut Self {
self.raw.timeout = timeout;
self
}
#[inline]
pub fn preset(&mut self, preset: u32) -> &mut Self {
self.raw.preset = preset;
self
}
#[inline]
pub fn filters(&mut self, filters: Filters) -> &mut Self {
self.raw.filters = filters.inner.as_ptr();
self.filters = Some(filters);
self
}
#[inline]
pub fn check(&mut self, check: Check) -> &mut Self {
self.raw.check = check as liblzma_sys::lzma_check;
self
}
#[inline]
pub fn memlimit_threading(&mut self, memlimit: u64) -> &mut Self {
self.raw.memlimit_threading = memlimit;
self
}
#[inline]
pub fn memlimit_stop(&mut self, memlimit: u64) -> &mut Self {
self.raw.memlimit_stop = memlimit;
self
}
#[inline]
pub fn memusage(&self) -> u64 {
unsafe { liblzma_sys::lzma_stream_encoder_mt_memusage(&self.raw) }
}
#[inline]
pub fn encoder(&self) -> Result<Stream, Error> {
let mut init = unsafe { Stream::zeroed() };
cvt(unsafe { liblzma_sys::lzma_stream_encoder_mt(&mut init.raw, &self.raw) })?;
Ok(init)
}
#[inline]
pub fn decoder(&self) -> Result<Stream, Error> {
let mut init = unsafe { Stream::zeroed() };
cvt(unsafe { liblzma_sys::lzma_stream_decoder_mt(&mut init.raw, &self.raw) })?;
Ok(init)
}
}
fn cvt(rc: liblzma_sys::lzma_ret) -> Result<Status, Error> {
match rc {
liblzma_sys::LZMA_OK => Ok(Status::Ok),
liblzma_sys::LZMA_STREAM_END => Ok(Status::StreamEnd),
liblzma_sys::LZMA_NO_CHECK => Err(Error::NoCheck),
liblzma_sys::LZMA_UNSUPPORTED_CHECK => Err(Error::UnsupportedCheck),
liblzma_sys::LZMA_GET_CHECK => Ok(Status::GetCheck),
liblzma_sys::LZMA_MEM_ERROR => Err(Error::Mem),
liblzma_sys::LZMA_MEMLIMIT_ERROR => Err(Error::MemLimit),
liblzma_sys::LZMA_FORMAT_ERROR => Err(Error::Format),
liblzma_sys::LZMA_OPTIONS_ERROR => Err(Error::Options),
liblzma_sys::LZMA_DATA_ERROR => Err(Error::Data),
liblzma_sys::LZMA_BUF_ERROR => Ok(Status::MemNeeded),
liblzma_sys::LZMA_PROG_ERROR => Err(Error::Program),
c => panic!("unknown return code: {}", c),
}
}
impl From<Error> for io::Error {
#[inline]
fn from(e: Error) -> io::Error {
let kind = match e {
Error::Data => io::ErrorKind::InvalidData,
Error::Options => io::ErrorKind::InvalidInput,
Error::Format => io::ErrorKind::InvalidData,
Error::MemLimit => io::ErrorKind::Other,
Error::Mem => io::ErrorKind::Other,
Error::Program => io::ErrorKind::Other,
Error::NoCheck => io::ErrorKind::InvalidInput,
Error::UnsupportedCheck => io::ErrorKind::Other,
};
io::Error::new(kind, e)
}
}
impl error::Error for Error {}
impl fmt::Display for Error {
#[inline]
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 {
#[inline]
fn drop(&mut self) {
unsafe {
liblzma_sys::lzma_end(&mut self.raw);
}
}
}