use std::io::{self, Write};
use lzma_sys::*;
use std;
use error::LzmaError;
use ::Direction;
use lzma_stream_wrapper::{LzmaStreamWrapper, LzmaCodeResult};
const DEFAULT_BUF_SIZE: usize = 4 * 1024;
pub struct LzmaWriter<T> {
inner: T,
stream: LzmaStreamWrapper,
buffer: Vec<u8>,
direction: Direction,
}
impl<T: Write> LzmaWriter<T> {
pub fn new_compressor(inner: T, preset: u32) -> Result<LzmaWriter<T>, LzmaError> {
LzmaWriter::with_capacity(DEFAULT_BUF_SIZE, inner, Direction::Compress, preset)
}
pub fn new_decompressor(inner: T) -> Result<LzmaWriter<T>, LzmaError> {
LzmaWriter::with_capacity(DEFAULT_BUF_SIZE, inner, Direction::Decompress, 0)
}
pub fn with_capacity(capacity: usize, inner: T, direction: Direction, preset: u32) -> Result<LzmaWriter<T>, LzmaError> {
let mut writer = LzmaWriter {
inner,
stream: LzmaStreamWrapper::new(),
buffer: vec![0; capacity],
direction,
};
match writer.direction {
Direction::Compress => {
writer.stream.easy_encoder(preset, lzma_check::LzmaCheckCrc64)?
},
Direction::Decompress => {
writer.stream.stream_decoder(std::u64::MAX, 0)?
},
}
Ok(writer)
}
}
impl<W: Write> LzmaWriter<W> {
pub fn finish(mut self) -> Result<W, LzmaError> {
loop {
match self.lzma_code_and_write(&[], lzma_action::LzmaFinish) {
Ok(LzmaCodeResult {
ret: Ok(lzma_ret::LzmaStreamEnd),
bytes_read: _,
bytes_written: _,
}) => break,
Ok(_) => continue,
Err(err) => return Err(err),
}
}
Ok(self.inner)
}
#[allow(clippy::question_mark)]
fn lzma_code_and_write(&mut self, input: &[u8], action: lzma_action) -> Result<LzmaCodeResult, LzmaError> {
let result = self.stream.code(input, &mut self.buffer, action);
if let Err(err) = result.ret {
return Err(err);
}
if result.bytes_written > 0 {
Write::write_all(&mut self.inner, &self.buffer[..result.bytes_written])?;
}
Ok(result)
}
}
impl<W: Write> Write for LzmaWriter<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
loop {
match self.lzma_code_and_write(buf, lzma_action::LzmaRun) {
Ok(result) => if result.bytes_read == 0 && result.bytes_written > 0 {
continue
} else {
return Ok(result.bytes_read)
},
Err(LzmaError::Io(err)) => return Err(err),
Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err)),
}
}
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}