lzma/
reader.rs

1//! This module implements `LzmaReader`.
2//!
3//! `LzmaReader` implements the LZMA (XZ) compression/decompression algorithm as a generic
4//! Reader.  In other words, it behaves similar to BufReader.  Instead of buffering the `Read`
5//! object passed to it, `LzmaReader` applies compression or decompression to it as it's read.
6//!
7//!
8//! # Examples
9//!
10//! ```no_run
11//! use lzma::LzmaReader;
12//! use std::io::prelude::*;
13//! use std::fs::File;
14//!
15//! let f = File::open("foo.xz").unwrap();
16//! let mut f = LzmaReader::new_decompressor(f).unwrap();
17//! let mut s = String::new();
18//!
19//! f.read_to_string(&mut s).unwrap();
20//! println!("{}", s);
21//! ```
22
23use std::io::{self, Read};
24use lzma_sys::*;
25use std;
26use error::LzmaError;
27use ::Direction;
28use lzma_stream_wrapper::LzmaStreamWrapper;
29
30
31const DEFAULT_BUF_SIZE: usize = 4 * 1024;
32
33
34pub struct LzmaReader<T> {
35	inner: T,
36	stream: LzmaStreamWrapper,
37	buffer: Vec<u8>,
38	buffer_offset: usize,
39	buffer_len: usize,
40	direction: Direction,
41}
42
43
44impl<T: Read> LzmaReader<T> {
45	pub fn new_compressor(inner: T, preset: u32) -> Result<LzmaReader<T>, LzmaError> {
46		LzmaReader::with_capacity(DEFAULT_BUF_SIZE, inner, Direction::Compress, preset)
47	}
48
49	pub fn new_decompressor(inner: T) -> Result<LzmaReader<T>, LzmaError> {
50		LzmaReader::with_capacity(DEFAULT_BUF_SIZE, inner, Direction::Decompress, 0)
51	}
52
53	pub fn with_capacity(capacity: usize, inner: T, direction: Direction, preset: u32) -> Result<LzmaReader<T>, LzmaError> {
54		let mut reader = LzmaReader {
55			inner,
56			stream: LzmaStreamWrapper::new(),
57			buffer: vec![0; capacity],
58			buffer_offset: 0,
59			buffer_len: 0,
60			direction,
61		};
62
63		match reader.direction {
64			Direction::Compress => {
65				reader.stream.easy_encoder(preset, lzma_check::LzmaCheckCrc64)?
66			},
67			Direction::Decompress => {
68				reader.stream.stream_decoder(std::u64::MAX, 0)?
69			},
70		}
71
72		Ok(reader)
73	}
74
75	pub fn into_inner(self) -> T { self.inner }
76}
77
78
79impl<R: Read> Read for LzmaReader<R> {
80	/// Reads data from the wrapped object, applies compression/decompression, and puts the results
81	/// into buf.
82	fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
83		// Our code doesn't handle buf.len() being 0, so exit early
84		if buf.is_empty() {
85			return Ok(0);
86		}
87
88		loop {
89			let mut action = lzma_action::LzmaRun;
90
91			// If our internal read buffer is empty, re-fill it by calling read on the inner Read object.
92			if self.buffer_len == 0 {
93				self.buffer_offset = 0;
94				self.buffer_len = self.inner.read(&mut self.buffer)?;
95
96				if self.buffer_len == 0 {
97					action = lzma_action::LzmaFinish;
98				}
99			}
100
101			// Instruct liblzma to compress/decompress data from the buffer, and write the results to buf
102			let result = self.stream.code(&self.buffer[self.buffer_offset..(self.buffer_offset+self.buffer_len)], buf, action);
103			self.buffer_offset += result.bytes_read;
104			self.buffer_len -= result.bytes_read;
105
106			let stream_end = match result.ret {
107				Ok(lzma_ret::LzmaStreamEnd) => true,
108				Ok(_) => false,
109				Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err)),
110			};
111
112			// We have to loop until we get at least 1 byte or EOF, because most users of
113		 	// Read::read assume that a return value of 0 is EOF.
114			if stream_end || result.bytes_written > 0 {
115				return Ok(result.bytes_written);
116			}
117		}
118	}
119}