use std::io::{Read, Result as IoResult};
use ll;
struct DecoderContext {
c: ll::ZBUFFDecompressionContext,
}
impl DecoderContext {
fn new() -> Self {
DecoderContext { c: unsafe { ll::ZBUFF_createDCtx() } }
}
}
impl Drop for DecoderContext {
fn drop(&mut self) {
let code = unsafe { ll::ZBUFF_freeDCtx(self.c) };
ll::parse_code(code).unwrap();
}
}
pub struct Decoder<R: Read> {
reader: R,
buffer: Vec<u8>,
offset: usize,
context: DecoderContext,
}
impl<R: Read> Decoder<R> {
pub fn new(reader: R) -> IoResult<Self> {
let context = DecoderContext::new();
try!(ll::parse_code(unsafe { ll::ZBUFF_decompressInit(context.c) }));
let buffer_size = unsafe { ll::ZBUFF_recommendedDInSize() };
Ok(Decoder {
reader: reader,
buffer: Vec::with_capacity(buffer_size),
offset: 0,
context: context,
})
}
}
impl<R: Read> Read for Decoder<R> {
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
let mut written = 0;
while written != buf.len() {
if self.offset == self.buffer.len() {
let buffer_size = self.buffer.capacity();
unsafe {
self.buffer.set_len(buffer_size);
}
self.offset = 0;
let read = try!(self.reader.read(&mut self.buffer));
unsafe {
self.buffer.set_len(read);
}
if read == 0 {
break;
}
}
let mut out_size = buf.len() - written;
let mut in_size = self.buffer.len() - self.offset;
unsafe {
let code = ll::ZBUFF_decompressContinue(self.context.c,
buf[written..].as_mut_ptr(),
&mut out_size,
self.buffer[self.offset..].as_ptr(),
&mut in_size);
let hint = try!(ll::parse_code(code));
}
written += out_size;
self.offset += in_size;
}
Ok(written)
}
}