liblzma/
lib.rs

1//! LZMA/XZ encoding and decoding streams
2//!
3//! This library is a binding to liblzma currently to provide LZMA and xz
4//! encoding/decoding streams. I/O streams are provided in the `read`, `write`,
5//! and `bufread` modules (same types, different bounds). Raw in-memory
6//! compression/decompression is provided via the `stream` module and contains
7//! many of the raw APIs in liblzma.
8//!
9//! # Examples
10//!
11//! ```no_run
12//! use liblzma::read::{XzDecoder, XzEncoder};
13//! use std::io::prelude::*;
14//!
15//! // Round trip some bytes from a byte source, into a compressor, into a
16//! // decompressor, and finally into a vector.
17//! let data = "Hello, World!".as_bytes();
18//! let compressor = XzEncoder::new(data, 9);
19//! let mut decompressor = XzDecoder::new(compressor);
20//!
21//! let mut contents = String::new();
22//! decompressor.read_to_string(&mut contents).unwrap();
23//! assert_eq!(contents, "Hello, World!");
24//! ```
25//! # Static linking
26//!
27//! You can enable static-linking using the `static` feature, so that the XZ
28//! library is not required at runtime:
29//!
30//! ```toml
31//! liblzma = { version = "0.4", features = ["static"] }
32//! ```
33//!
34//! # Multithreading
35//!
36//! This crate optionally can support multithreading using the `parallel`
37//! feature of this crate:
38//!
39//! ```toml
40//! liblzma = { version = "0.4", features = ["parallel"] }
41//! ```
42//!
43//! # Async I/O
44//!
45//! Dropped `tokio` support since 0.4.0.
46//! If you need to use async I/O, use `async-compression` crate with `lzma` feature flag.
47//!
48//! ```toml
49//! async-compression = { version = "0.4", features = ["lzma"] }
50//! ```
51
52#![doc(html_root_url = "https://docs.rs/liblzma/0.4.5")]
53#![deny(missing_docs)]
54
55use std::io::{self, prelude::*};
56
57pub mod stream;
58
59pub mod bufread;
60pub mod read;
61pub mod write;
62
63/// Decompress from the given source as if using a [read::XzDecoder].
64///
65/// Result will be in the xz format.
66pub fn decode_all<R: Read>(source: R) -> io::Result<Vec<u8>> {
67    let mut vec = Vec::new();
68    let mut r = read::XzDecoder::new(source);
69    r.read_to_end(&mut vec)?;
70    Ok(vec)
71}
72
73/// Compress from the given source as if using a [read::XzEncoder].
74///
75/// The input data must be in the xz format.
76pub fn encode_all<R: Read>(source: R, level: u32) -> io::Result<Vec<u8>> {
77    let mut vec = Vec::new();
78    let mut r = read::XzEncoder::new(source, level);
79    r.read_to_end(&mut vec)?;
80    Ok(vec)
81}
82
83/// Compress all data from the given source as if using a [read::XzEncoder].
84///
85/// Compressed data will be appended to `destination`.
86pub fn copy_encode<R: Read, W: Write>(source: R, mut destination: W, level: u32) -> io::Result<()> {
87    io::copy(&mut read::XzEncoder::new(source, level), &mut destination)?;
88    Ok(())
89}
90
91/// Decompress all data from the given source as if using a [read::XzDecoder].
92///
93/// Decompressed data will be appended to `destination`.
94pub fn copy_decode<R: Read, W: Write>(source: R, mut destination: W) -> io::Result<()> {
95    io::copy(&mut read::XzDecoder::new(source), &mut destination)?;
96    Ok(())
97}
98
99/// Find the size in bytes of uncompressed data from xz file.
100#[cfg(feature = "bindgen")]
101pub fn uncompressed_size<R: Read + Seek>(mut source: R) -> io::Result<u64> {
102    use std::mem::MaybeUninit;
103    let mut footer = [0u8; liblzma_sys::LZMA_STREAM_HEADER_SIZE as usize];
104
105    source.seek(io::SeekFrom::End(
106        0 - (liblzma_sys::LZMA_STREAM_HEADER_SIZE as i64),
107    ))?;
108    source.read_exact(&mut footer)?;
109
110    let lzma_stream_flags = unsafe {
111        let mut lzma_stream_flags = MaybeUninit::uninit();
112        let ret =
113            liblzma_sys::lzma_stream_footer_decode(lzma_stream_flags.as_mut_ptr(), footer.as_ptr());
114
115        if ret != liblzma_sys::LZMA_OK {
116            return Err(io::Error::new(
117                io::ErrorKind::Other,
118                "Failed to parse lzma footer",
119            ));
120        }
121
122        lzma_stream_flags.assume_init()
123    };
124
125    let index_plus_footer =
126        liblzma_sys::LZMA_STREAM_HEADER_SIZE as usize + lzma_stream_flags.backward_size as usize;
127
128    source.seek(io::SeekFrom::End(0 - index_plus_footer as i64))?;
129
130    let buf = source
131        .bytes()
132        .take(index_plus_footer)
133        .collect::<io::Result<Vec<u8>>>()?;
134
135    let uncompressed_size = unsafe {
136        let mut i: MaybeUninit<*mut liblzma_sys::lzma_index> = MaybeUninit::uninit();
137        let mut memlimit = u64::MAX;
138        let mut in_pos = 0usize;
139
140        let ret = liblzma_sys::lzma_index_buffer_decode(
141            i.as_mut_ptr(),
142            &mut memlimit,
143            std::ptr::null(),
144            buf.as_ptr(),
145            &mut in_pos,
146            buf.len(),
147        );
148
149        if ret != liblzma_sys::LZMA_OK {
150            return Err(io::Error::new(
151                io::ErrorKind::Other,
152                "Failed to parse lzma footer",
153            ));
154        }
155
156        let i = i.assume_init();
157
158        let uncompressed_size = liblzma_sys::lzma_index_uncompressed_size(i);
159
160        liblzma_sys::lzma_index_end(i, std::ptr::null());
161
162        uncompressed_size
163    };
164
165    Ok(uncompressed_size)
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171    use quickcheck::quickcheck;
172    #[cfg(all(target_family = "wasm", target_os = "unknown"))]
173    use wasm_bindgen_test::wasm_bindgen_test as test;
174
175    #[test]
176    fn all() {
177        quickcheck(test as fn(_) -> _);
178
179        fn test(v: Vec<u8>) -> bool {
180            let e = encode_all(&v[..], 6).unwrap();
181            let d = decode_all(&e[..]).unwrap();
182            v == d
183        }
184    }
185
186    #[test]
187    fn copy() {
188        quickcheck(test as fn(_) -> _);
189
190        fn test(v: Vec<u8>) -> bool {
191            let mut e = Vec::new();
192            copy_encode(&v[..], &mut e, 6).unwrap();
193            let mut d = Vec::new();
194            copy_decode(&e[..], &mut d).unwrap();
195            v == d
196        }
197    }
198
199    #[test]
200    #[cfg(feature = "bindgen")]
201    fn size() {
202        quickcheck(test as fn(_) -> _);
203
204        fn test(v: Vec<u8>) -> bool {
205            let mut e = Vec::new();
206            copy_encode(&v[..], &mut e, 6).unwrap();
207
208            let s = super::uncompressed_size(std::io::Cursor::new(e)).unwrap();
209
210            (s as usize) == v.len()
211        }
212    }
213}