use std::io::prelude::*;
use std::io;
use super::CompressParams;
use raw::{self, Decompress, DeStatus, Compress, CompressOp, CoStatus};
#[derive(Clone, Copy, Eq, PartialEq)]
enum DoneStatus {
Processing,
Finishing,
Done,
}
pub struct BrotliEncoder<R: BufRead> {
obj: R,
data: Compress,
done: DoneStatus,
err: Option<raw::Error>,
}
pub struct BrotliDecoder<R: BufRead> {
obj: R,
data: Decompress,
err: Option<raw::Error>,
}
impl<R: BufRead> BrotliEncoder<R> {
pub fn new(r: R, level: u32) -> BrotliEncoder<R> {
let mut data = Compress::new();
data.set_params(CompressParams::new().quality(level));
BrotliEncoder {
obj: r,
data: data,
done: DoneStatus::Processing,
err: None,
}
}
pub fn from_params(r: R, params: &CompressParams) -> BrotliEncoder<R> {
let mut data = Compress::new();
data.set_params(params);
BrotliEncoder {
obj: r,
data: data,
done: DoneStatus::Processing,
err: None,
}
}
pub fn get_ref(&self) -> &R {
&self.obj
}
pub fn get_mut(&mut self) -> &mut R {
&mut self.obj
}
pub fn into_inner(self) -> R {
self.obj
}
}
impl<R: BufRead> Read for BrotliEncoder<R> {
fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
if buf.is_empty() { return Ok(0) }
if let Some(ref err) = self.err {
return Err(err.clone().into())
}
if let Some(data) = self.data.take_output(Some(buf.len())) {
buf[..data.len()].copy_from_slice(data);
return Ok(data.len())
}
match self.done {
DoneStatus::Done => return Ok(0),
DoneStatus::Finishing => return tryfinish(self, buf),
DoneStatus::Processing => (),
}
loop {
let amt_in;
let amt_out;
{
let input = &mut try!(self.obj.fill_buf());
let avail_in = input.len();
if avail_in == 0 {
break
}
let mut output = &mut buf;
let avail_out = output.len();
if let Err(err) = self.data.compress(CompressOp::Process, input, output) {
self.err = Some(err.clone().into());
return Err(err.into())
}
amt_in = avail_in - input.len();
amt_out = avail_out - output.len();
}
self.obj.consume(amt_in);
if amt_out == 0 {
assert!(amt_in != 0);
continue
}
return Ok(amt_out)
}
self.done = DoneStatus::Finishing;
return tryfinish(self, buf);
fn tryfinish<R: BufRead>(enc: &mut BrotliEncoder<R>, mut buf: &mut [u8])
-> io::Result<usize> {
let output = &mut buf;
let avail_out = output.len();
let iscomplete = match enc.data.compress(CompressOp::Finish, &mut &[][..], output) {
Ok(c) => c,
Err(err) => {
enc.err = err.clone().into();
return Err(err.into())
},
};
let written = avail_out - output.len();
assert!(written != 0 || iscomplete == CoStatus::Finished);
if iscomplete == CoStatus::Finished {
enc.done = DoneStatus::Done
}
Ok(written)
}
}
}
impl<R: BufRead> BrotliDecoder<R> {
pub fn new(r: R) -> BrotliDecoder<R> {
BrotliDecoder {
data: Decompress::new(),
obj: r,
err: None,
}
}
pub fn get_ref(&self) -> &R {
&self.obj
}
pub fn get_mut(&mut self) -> &mut R {
&mut self.obj
}
pub fn into_inner(self) -> R {
self.obj
}
}
impl<R: BufRead> Read for BrotliDecoder<R> {
fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
if buf.is_empty() { return Ok(0) }
if let Some(ref err) = self.err {
return Err(err.clone().into())
}
loop {
let amt_in;
let amt_out;
let status;
{
let mut input = try!(self.obj.fill_buf());
let avail_in = input.len();
let avail_out = buf.len();
status = match self.data.decompress(&mut input, &mut buf) {
Ok(s) => s,
Err(err) => {
self.err = Some(err.clone().into());
return Err(err.into())
},
};
amt_in = avail_in - input.len();
amt_out = avail_out - buf.len()
}
self.obj.consume(amt_in);
if amt_in == 0 && status == DeStatus::NeedInput {
return Err(io::Error::new(io::ErrorKind::Other,
"corrupted brotli stream"))
}
if amt_out == 0 && status != DeStatus::Finished {
assert!(amt_in != 0);
continue
}
return Ok(amt_out)
}
}
}