Skip to main content

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![0u8; m.len()];
286        assert_eq!(d.read(&mut data).unwrap(), m.len());
287        assert_eq!(data, &m[..]);
288    }
289
290    #[test]
291    fn zero_length_read_at_eof() {
292        let m = Vec::new();
293        let mut c = XzEncoder::new(&m[..], 6);
294
295        let mut result = Vec::new();
296        c.read_to_end(&mut result).unwrap();
297
298        let mut d = XzDecoder::new(&result[..]);
299        let mut data = Vec::new();
300        assert_eq!(d.read(&mut data).unwrap(), 0);
301    }
302
303    #[test]
304    fn zero_length_read_with_data() {
305        let m = vec![3u8; 128 * 1024 + 1];
306        let mut c = XzEncoder::new(&m[..], 6);
307
308        let mut result = Vec::new();
309        c.read_to_end(&mut result).unwrap();
310
311        let mut d = XzDecoder::new(&result[..]);
312        let mut data = Vec::new();
313        assert_eq!(d.read(&mut data).unwrap(), 0);
314    }
315
316    #[test]
317    fn extreme_preset_round_trip() {
318        let m = vec![7u8; 128 * 1024 + 1];
319        let c = XzEncoder::new(&m[..], 6 | PRESET_EXTREME);
320        let mut d = XzDecoder::new(c);
321        let mut data = Vec::new();
322        d.read_to_end(&mut data).unwrap();
323        assert_eq!(data, m);
324    }
325
326    #[test]
327    fn qc_lzma1() {
328        quickcheck(test as fn(_) -> _);
329        fn test(v: Vec<u8>) -> bool {
330            let options = LzmaOptions::new_preset(6).unwrap();
331            let stream = Stream::new_lzma_encoder(&options).unwrap();
332            let r = XzEncoder::new_stream(&v[..], stream);
333            let stream = Stream::new_lzma_decoder(u64::MAX).unwrap();
334            let mut r = XzDecoder::new_stream(r, stream);
335            let mut v2 = Vec::new();
336            r.read_to_end(&mut v2).unwrap();
337            v == v2
338        }
339    }
340
341    #[test]
342    fn qc() {
343        quickcheck(test as fn(_) -> _);
344
345        fn test(v: Vec<u8>) -> bool {
346            let r = XzEncoder::new(&v[..], 6);
347            let mut r = XzDecoder::new(r);
348            let mut v2 = Vec::new();
349            r.read_to_end(&mut v2).unwrap();
350            v == v2
351        }
352    }
353
354    #[cfg(feature = "parallel")]
355    #[test]
356    fn qc_parallel_encode() {
357        quickcheck(test as fn(_) -> _);
358
359        fn test(v: Vec<u8>) -> bool {
360            let r = XzEncoder::new_parallel(&v[..], 6);
361            let mut r = XzDecoder::new(r);
362            let mut v2 = Vec::new();
363            r.read_to_end(&mut v2).unwrap();
364            v == v2
365        }
366    }
367
368    #[cfg(feature = "parallel")]
369    #[test]
370    fn extreme_preset_round_trip_parallel() {
371        let m = vec![9u8; 128 * 1024 + 1];
372        let c = XzEncoder::new_parallel(&m[..], 6 | PRESET_EXTREME);
373        let mut d = XzDecoder::new(c);
374        let mut data = Vec::new();
375        d.read_to_end(&mut data).unwrap();
376        assert_eq!(data, m);
377    }
378
379    #[cfg(feature = "parallel")]
380    #[test]
381    fn qc_parallel_decode() {
382        quickcheck(test as fn(_) -> _);
383
384        fn test(v: Vec<u8>) -> bool {
385            let r = XzEncoder::new(&v[..], 6);
386            let mut r = XzDecoder::new_parallel(r);
387            let mut v2 = Vec::new();
388            r.read_to_end(&mut v2).unwrap();
389            v == v2
390        }
391    }
392
393    #[test]
394    fn two_streams() {
395        let mut input_stream1: Vec<u8> = Vec::new();
396        let mut input_stream2: Vec<u8> = Vec::new();
397        let mut all_input: Vec<u8> = Vec::new();
398
399        // Generate input data.
400        const STREAM1_SIZE: usize = 1024;
401        for num in 0..STREAM1_SIZE {
402            input_stream1.push(num as u8)
403        }
404        const STREAM2_SIZE: usize = 532;
405        for num in 0..STREAM2_SIZE {
406            input_stream2.push((num + 32) as u8)
407        }
408        all_input.extend(&input_stream1);
409        all_input.extend(&input_stream2);
410
411        // Make a vector with compressed data
412        let mut decoder_input = Vec::new();
413        {
414            let mut encoder = XzEncoder::new(&input_stream1[..], 6);
415            encoder.read_to_end(&mut decoder_input).unwrap();
416        }
417        {
418            let mut encoder = XzEncoder::new(&input_stream2[..], 6);
419            encoder.read_to_end(&mut decoder_input).unwrap();
420        }
421
422        // Decoder must be able to read the 2 concatenated xz streams and get the same data as input.
423        let mut decoder_reader = &decoder_input[..];
424        {
425            // using `XzDecoder::new` here would fail because only 1 xz stream would be processed.
426            let mut decoder = XzDecoder::new_multi_decoder(&mut decoder_reader);
427            let mut decompressed_data = vec![0u8; all_input.len()];
428
429            assert_eq!(
430                decoder.read(&mut decompressed_data).unwrap(),
431                all_input.len()
432            );
433            assert_eq!(decompressed_data, &all_input[..]);
434        }
435    }
436}