liblzma/
read.rs

1//! Reader-based compression/decompression streams
2
3use std::io::prelude::*;
4use std::io::{self, BufReader};
5
6use crate::bufread;
7use crate::stream::Stream;
8
9/// A compression stream which wraps an uncompressed stream of data. Compressed
10/// data will be read from the stream.
11pub struct XzEncoder<R: Read> {
12    inner: bufread::XzEncoder<BufReader<R>>,
13}
14
15/// A decompression stream which wraps a compressed stream of data. Decompressed
16/// data will be read from the stream.
17pub struct XzDecoder<R: Read> {
18    inner: bufread::XzDecoder<BufReader<R>>,
19}
20
21impl<R: Read> XzEncoder<R> {
22    /// Create a new compression stream which will compress at the given level
23    /// to read compress output to the give output stream.
24    ///
25    /// The `level` argument here is typically 0-9 with 6 being a good default.
26    /// To use the slower `xz --extreme`-style preset, bitwise-OR a level with
27    /// [`crate::stream::PRESET_EXTREME`] (for example, `6 | crate::stream::PRESET_EXTREME`).
28    #[inline]
29    pub fn new(r: R, level: u32) -> XzEncoder<R> {
30        XzEncoder {
31            inner: bufread::XzEncoder::new(BufReader::new(r), level),
32        }
33    }
34
35    /// Create a new parallel compression stream which will compress at the given level
36    /// to read compress output to the give output stream.
37    ///
38    /// The `level` argument here is typically 0-9 with 6 being a good default.
39    /// To use the slower `xz --extreme`-style preset, bitwise-OR a level with
40    /// [`crate::stream::PRESET_EXTREME`] (for example, `6 | crate::stream::PRESET_EXTREME`).
41    #[cfg(feature = "parallel")]
42    pub fn new_parallel(r: R, level: u32) -> XzEncoder<R> {
43        XzEncoder {
44            inner: bufread::XzEncoder::new_parallel(BufReader::new(r), level),
45        }
46    }
47
48    /// Creates a new encoder with a custom `Stream`.
49    ///
50    /// The `Stream` can be pre-configured for multithreaded encoding, different
51    /// compression options/tuning, etc.
52    #[inline]
53    pub fn new_stream(r: R, stream: Stream) -> XzEncoder<R> {
54        XzEncoder {
55            inner: bufread::XzEncoder::new_stream(BufReader::new(r), stream),
56        }
57    }
58
59    /// Acquires a reference to the underlying stream
60    #[inline]
61    pub fn get_ref(&self) -> &R {
62        self.inner.get_ref().get_ref()
63    }
64
65    /// Acquires a mutable reference to the underlying stream
66    ///
67    /// Note that mutation of the stream may result in surprising results if
68    /// this encoder is continued to be used.
69    #[inline]
70    pub fn get_mut(&mut self) -> &mut R {
71        self.inner.get_mut().get_mut()
72    }
73
74    /// Unwrap the underlying writer, finishing the compression stream.
75    #[inline]
76    pub fn into_inner(self) -> R {
77        self.inner.into_inner().into_inner()
78    }
79
80    /// Returns the number of bytes produced by the compressor
81    /// (e.g. the number of bytes read from this stream)
82    ///
83    /// Note that, due to buffering, this only bears any relation to
84    /// total_in() when the compressor chooses to flush its data
85    /// (unfortunately, this won't happen this won't happen in general
86    /// at the end of the stream, because the compressor doesn't know
87    /// if there's more data to come).  At that point,
88    /// `total_out() / total_in()` would be the compression ratio.
89    #[inline]
90    pub fn total_out(&self) -> u64 {
91        self.inner.total_out()
92    }
93
94    /// Returns the number of bytes consumed by the compressor
95    /// (e.g. the number of bytes read from the underlying stream)
96    #[inline]
97    pub fn total_in(&self) -> u64 {
98        self.inner.total_in()
99    }
100}
101
102impl<R: Read> Read for XzEncoder<R> {
103    #[inline]
104    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
105        self.inner.read(buf)
106    }
107}
108
109impl<W: Write + Read> Write for XzEncoder<W> {
110    #[inline]
111    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
112        self.get_mut().write(buf)
113    }
114
115    #[inline]
116    fn flush(&mut self) -> io::Result<()> {
117        self.get_mut().flush()
118    }
119}
120
121impl<R: Read> XzDecoder<R> {
122    /// Create a new decompression stream, which will read compressed
123    /// data from the given input stream, and decompress one xz stream.
124    /// It may also consume input data that follows the xz stream.
125    /// Use [`bufread::XzDecoder`] instead to
126    /// process a mix of xz and non-xz data.
127    #[inline]
128    pub fn new(r: R) -> XzDecoder<R> {
129        XzDecoder {
130            inner: bufread::XzDecoder::new(BufReader::new(r)),
131        }
132    }
133
134    /// Create a new parallel decompression stream, which will read compressed
135    /// data from the given input stream, and decompress one xz stream.
136    /// It may also consume input data that follows the xz stream.
137    /// Use [`bufread::XzDecoder`] instead to process a mix of xz and non-xz data.
138    #[cfg(feature = "parallel")]
139    #[inline]
140    pub fn new_parallel(r: R) -> XzDecoder<R> {
141        XzDecoder {
142            inner: bufread::XzDecoder::new_parallel(BufReader::new(r)),
143        }
144    }
145
146    /// Create a new decompression stream, which will read compressed
147    /// data from the given input and decompress all the xz stream it contains.
148    #[inline]
149    pub fn new_multi_decoder(r: R) -> XzDecoder<R> {
150        XzDecoder {
151            inner: bufread::XzDecoder::new_multi_decoder(BufReader::new(r)),
152        }
153    }
154
155    /// Creates a new decoder with a custom `Stream`.
156    ///
157    /// The `Stream` can be pre-configured for various checks, different
158    /// decompression options/tuning, etc.
159    #[inline]
160    pub fn new_stream(r: R, stream: Stream) -> XzDecoder<R> {
161        XzDecoder {
162            inner: bufread::XzDecoder::new_stream(BufReader::new(r), stream),
163        }
164    }
165
166    /// Acquires a reference to the underlying stream
167    #[inline]
168    pub fn get_ref(&self) -> &R {
169        self.inner.get_ref().get_ref()
170    }
171
172    /// Acquires a mutable reference to the underlying stream
173    ///
174    /// Note that mutation of the stream may result in surprising results if
175    /// this encoder is continued to be used.
176    #[inline]
177    pub fn get_mut(&mut self) -> &mut R {
178        self.inner.get_mut().get_mut()
179    }
180
181    /// Unwrap the underlying writer, finishing the compression stream.
182    #[inline]
183    pub fn into_inner(self) -> R {
184        self.inner.into_inner().into_inner()
185    }
186
187    /// Returns the number of bytes produced by the decompressor
188    /// (e.g. the number of bytes read from this stream)
189    ///
190    /// Note that, due to buffering, this only bears any relation to
191    /// total_in() when the decompressor reaches a sync point
192    /// (e.g. where the original compressed stream was flushed).
193    /// At that point, `total_in() / total_out()` is the compression ratio.
194    #[inline]
195    pub fn total_out(&self) -> u64 {
196        self.inner.total_out()
197    }
198
199    /// Returns the number of bytes consumed by the decompressor
200    /// (e.g. the number of bytes read from the underlying stream)
201    #[inline]
202    pub fn total_in(&self) -> u64 {
203        self.inner.total_in()
204    }
205}
206
207impl<R: Read> Read for XzDecoder<R> {
208    #[inline]
209    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
210        self.inner.read(buf)
211    }
212}
213
214impl<W: Write + Read> Write for XzDecoder<W> {
215    #[inline]
216    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
217        self.get_mut().write(buf)
218    }
219
220    #[inline]
221    fn flush(&mut self) -> io::Result<()> {
222        self.get_mut().flush()
223    }
224}
225
226#[cfg(test)]
227mod tests {
228    use super::*;
229    use crate::stream::{LzmaOptions, PRESET_EXTREME};
230    use quickcheck::quickcheck;
231    use rand::{thread_rng, Rng};
232    use std::iter;
233    #[cfg(all(target_family = "wasm", target_os = "unknown"))]
234    use wasm_bindgen_test::wasm_bindgen_test as test;
235
236    #[test]
237    fn smoke() {
238        let m: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8];
239        let mut c = XzEncoder::new(m, 6);
240        let mut data = vec![];
241        c.read_to_end(&mut data).unwrap();
242        let mut d = XzDecoder::new(&data[..]);
243        let mut data2 = Vec::new();
244        d.read_to_end(&mut data2).unwrap();
245        assert_eq!(data2, m);
246    }
247
248    #[test]
249    fn smoke2() {
250        let m: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8];
251        let c = XzEncoder::new(m, 6);
252        let mut d = XzDecoder::new(c);
253        let mut data = vec![];
254        d.read_to_end(&mut data).unwrap();
255        assert_eq!(data, [1, 2, 3, 4, 5, 6, 7, 8]);
256    }
257
258    #[test]
259    fn smoke3() {
260        let m = vec![3u8; 128 * 1024 + 1];
261        let c = XzEncoder::new(&m[..], 6);
262        let mut d = XzDecoder::new(c);
263        let mut data = vec![];
264        d.read_to_end(&mut data).unwrap();
265        assert_eq!(data, &m[..]);
266    }
267
268    #[test]
269    fn self_terminating() {
270        let m = vec![3u8; 128 * 1024 + 1];
271        let mut c = XzEncoder::new(&m[..], 6);
272
273        let mut result = Vec::new();
274        c.read_to_end(&mut result).unwrap();
275
276        let mut rng = thread_rng();
277        let v = iter::repeat_with(|| rng.gen::<u8>())
278            .take(1024)
279            .collect::<Vec<_>>();
280        for _ in 0..200 {
281            result.extend(v.iter().map(|x| *x));
282        }
283
284        let mut d = XzDecoder::new(&result[..]);
285        let mut data = Vec::with_capacity(m.len());
286        unsafe {
287            data.set_len(m.len());
288        }
289        assert_eq!(d.read(&mut data).unwrap(), m.len());
290        assert_eq!(data, &m[..]);
291    }
292
293    #[test]
294    fn zero_length_read_at_eof() {
295        let m = Vec::new();
296        let mut c = XzEncoder::new(&m[..], 6);
297
298        let mut result = Vec::new();
299        c.read_to_end(&mut result).unwrap();
300
301        let mut d = XzDecoder::new(&result[..]);
302        let mut data = Vec::new();
303        assert_eq!(d.read(&mut data).unwrap(), 0);
304    }
305
306    #[test]
307    fn zero_length_read_with_data() {
308        let m = vec![3u8; 128 * 1024 + 1];
309        let mut c = XzEncoder::new(&m[..], 6);
310
311        let mut result = Vec::new();
312        c.read_to_end(&mut result).unwrap();
313
314        let mut d = XzDecoder::new(&result[..]);
315        let mut data = Vec::new();
316        assert_eq!(d.read(&mut data).unwrap(), 0);
317    }
318
319    #[test]
320    fn extreme_preset_round_trip() {
321        let m = vec![7u8; 128 * 1024 + 1];
322        let c = XzEncoder::new(&m[..], 6 | PRESET_EXTREME);
323        let mut d = XzDecoder::new(c);
324        let mut data = Vec::new();
325        d.read_to_end(&mut data).unwrap();
326        assert_eq!(data, m);
327    }
328
329    #[test]
330    fn qc_lzma1() {
331        quickcheck(test as fn(_) -> _);
332        fn test(v: Vec<u8>) -> bool {
333            let options = LzmaOptions::new_preset(6).unwrap();
334            let stream = Stream::new_lzma_encoder(&options).unwrap();
335            let r = XzEncoder::new_stream(&v[..], stream);
336            let stream = Stream::new_lzma_decoder(u64::MAX).unwrap();
337            let mut r = XzDecoder::new_stream(r, stream);
338            let mut v2 = Vec::new();
339            r.read_to_end(&mut v2).unwrap();
340            v == v2
341        }
342    }
343
344    #[test]
345    fn qc() {
346        quickcheck(test as fn(_) -> _);
347
348        fn test(v: Vec<u8>) -> bool {
349            let r = XzEncoder::new(&v[..], 6);
350            let mut r = XzDecoder::new(r);
351            let mut v2 = Vec::new();
352            r.read_to_end(&mut v2).unwrap();
353            v == v2
354        }
355    }
356
357    #[cfg(feature = "parallel")]
358    #[test]
359    fn qc_parallel_encode() {
360        quickcheck(test as fn(_) -> _);
361
362        fn test(v: Vec<u8>) -> bool {
363            let r = XzEncoder::new_parallel(&v[..], 6);
364            let mut r = XzDecoder::new(r);
365            let mut v2 = Vec::new();
366            r.read_to_end(&mut v2).unwrap();
367            v == v2
368        }
369    }
370
371    #[cfg(feature = "parallel")]
372    #[test]
373    fn extreme_preset_round_trip_parallel() {
374        let m = vec![9u8; 128 * 1024 + 1];
375        let c = XzEncoder::new_parallel(&m[..], 6 | PRESET_EXTREME);
376        let mut d = XzDecoder::new(c);
377        let mut data = Vec::new();
378        d.read_to_end(&mut data).unwrap();
379        assert_eq!(data, m);
380    }
381
382    #[cfg(feature = "parallel")]
383    #[test]
384    fn qc_parallel_decode() {
385        quickcheck(test as fn(_) -> _);
386
387        fn test(v: Vec<u8>) -> bool {
388            let r = XzEncoder::new(&v[..], 6);
389            let mut r = XzDecoder::new_parallel(r);
390            let mut v2 = Vec::new();
391            r.read_to_end(&mut v2).unwrap();
392            v == v2
393        }
394    }
395
396    #[test]
397    fn two_streams() {
398        let mut input_stream1: Vec<u8> = Vec::new();
399        let mut input_stream2: Vec<u8> = Vec::new();
400        let mut all_input: Vec<u8> = Vec::new();
401
402        // Generate input data.
403        const STREAM1_SIZE: usize = 1024;
404        for num in 0..STREAM1_SIZE {
405            input_stream1.push(num as u8)
406        }
407        const STREAM2_SIZE: usize = 532;
408        for num in 0..STREAM2_SIZE {
409            input_stream2.push((num + 32) as u8)
410        }
411        all_input.extend(&input_stream1);
412        all_input.extend(&input_stream2);
413
414        // Make a vector with compressed data
415        let mut decoder_input = Vec::new();
416        {
417            let mut encoder = XzEncoder::new(&input_stream1[..], 6);
418            encoder.read_to_end(&mut decoder_input).unwrap();
419        }
420        {
421            let mut encoder = XzEncoder::new(&input_stream2[..], 6);
422            encoder.read_to_end(&mut decoder_input).unwrap();
423        }
424
425        // Decoder must be able to read the 2 concatenated xz streams and get the same data as input.
426        let mut decoder_reader = &decoder_input[..];
427        {
428            // using `XzDecoder::new` here would fail because only 1 xz stream would be processed.
429            let mut decoder = XzDecoder::new_multi_decoder(&mut decoder_reader);
430            let mut decompressed_data = vec![0u8; all_input.len()];
431
432            assert_eq!(
433                decoder.read(&mut decompressed_data).unwrap(),
434                all_input.len()
435            );
436            assert_eq!(decompressed_data, &all_input[..]);
437        }
438    }
439}