use libc::c_void;
use parse_code;
use std::io::{self, Read};
use zstd_sys;
struct DecoderContext {
s: *mut zstd_sys::ZSTD_DStream,
}
impl Default for DecoderContext {
fn default() -> Self {
DecoderContext { s: unsafe { zstd_sys::ZSTD_createDStream() } }
}
}
impl Drop for DecoderContext {
fn drop(&mut self) {
let code = unsafe { zstd_sys::ZSTD_freeDStream(self.s) };
parse_code(code).unwrap();
}
}
pub struct Decoder<R: Read> {
reader: R,
buffer: Vec<u8>,
offset: usize,
context: DecoderContext,
single_frame: bool,
}
impl<R: Read> Decoder<R> {
pub fn new(reader: R) -> io::Result<Self> {
Self::with_dictionary(reader, &[])
}
pub fn single_frame(mut self) -> Self {
self.single_frame = true;
self
}
pub fn with_dictionary(reader: R, dictionary: &[u8]) -> io::Result<Self> {
let buffer_size = unsafe { zstd_sys::ZSTD_DStreamInSize() };
let context = DecoderContext::default();
parse_code(unsafe {
zstd_sys::ZSTD_initDStream_usingDict(context.s,
dictionary.as_ptr() as *const c_void,
dictionary.len())
})?;
let decoder = Decoder {
reader: reader,
buffer: Vec::with_capacity(buffer_size),
offset: 0,
context: context,
single_frame: false,
};
Ok(decoder)
}
fn reinit(&mut self) -> io::Result<()> {
parse_code(unsafe { zstd_sys::ZSTD_resetDStream(self.context.s) })?;
Ok(())
}
pub fn recommended_output_size() -> usize {
unsafe { zstd_sys::ZSTD_DStreamOutSize() }
}
pub fn get_ref(&self) -> &R {
&self.reader
}
pub fn get_mut(&mut self) -> &mut R {
&mut self.reader
}
pub fn finish(self) -> R {
self.reader
}
fn refill_buffer(&mut self, in_buffer: &mut zstd_sys::ZSTD_inBuffer)
-> io::Result<bool> {
let buffer_size = self.buffer.capacity();
unsafe {
self.buffer.set_len(buffer_size);
}
let read = self.reader.read(&mut self.buffer)?;
unsafe {
self.buffer.set_len(read);
}
in_buffer.pos = 0;
in_buffer.size = read;
Ok(read > 0)
}
}
impl<R: Read> Read for Decoder<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let mut in_buffer = zstd_sys::ZSTD_inBuffer {
src: self.buffer.as_ptr() as *const c_void,
size: self.buffer.len(),
pos: self.offset,
};
if self.offset > self.buffer.capacity() {
return Ok(0);
}
let mut out_buffer = zstd_sys::ZSTD_outBuffer {
dst: buf.as_mut_ptr() as *mut c_void,
size: buf.len(),
pos: 0,
};
while out_buffer.pos != buf.len() {
let mut input_exhausted = false;
if in_buffer.pos == in_buffer.size {
input_exhausted = !self.refill_buffer(&mut in_buffer)?;
}
let res = unsafe {
let code =
zstd_sys::ZSTD_decompressStream(self.context.s,
&mut out_buffer as *mut zstd_sys::ZSTD_outBuffer,
&mut in_buffer as *mut zstd_sys::ZSTD_inBuffer);
parse_code(code)?
};
if res > 1 && input_exhausted {
return Err(io::Error::new(io::ErrorKind::UnexpectedEof,
"incomplete frame"));
}
if res == 0 {
if self.single_frame {
in_buffer.pos = self.buffer.capacity() + 1;
break;
} else {
if in_buffer.pos == in_buffer.size &&
!self.refill_buffer(&mut in_buffer)? {
in_buffer.pos = self.buffer.capacity() + 1;
break;
} else {
self.reinit()?;
}
}
}
}
self.offset = in_buffer.pos;
Ok(out_buffer.pos)
}
}