use crate::map_error_code;
use std::convert::TryInto;
use std::io;
use zstd_safe;
#[derive(Default)]
pub struct Decompressor<'a> {
context: zstd_safe::DCtx<'a>,
}
impl Decompressor<'static> {
pub fn new() -> io::Result<Self> {
Self::with_dictionary(&[])
}
pub fn with_dictionary(dictionary: &[u8]) -> io::Result<Self> {
let mut decompressor = Self::default();
decompressor.set_dictionary(dictionary)?;
Ok(decompressor)
}
}
impl<'a> Decompressor<'a> {
pub fn with_prepared_dictionary<'b>(
dictionary: &'a crate::dict::DecoderDictionary<'b>,
) -> io::Result<Self>
where
'b: 'a,
{
let mut decompressor = Self::default();
decompressor.set_prepared_dictionary(dictionary)?;
Ok(decompressor)
}
pub fn set_dictionary(&mut self, dictionary: &[u8]) -> io::Result<()> {
self.context
.load_dictionary(dictionary)
.map_err(map_error_code)?;
Ok(())
}
pub fn set_prepared_dictionary<'b>(
&mut self,
dictionary: &'a crate::dict::DecoderDictionary<'b>,
) -> io::Result<()>
where
'b: 'a,
{
self.context
.ref_ddict(dictionary.as_ddict())
.map_err(map_error_code)?;
Ok(())
}
pub fn decompress_to_buffer<C: zstd_safe::WriteBuf + ?Sized>(
&mut self,
source: &[u8],
destination: &mut C,
) -> io::Result<usize> {
self.context
.decompress(destination, source)
.map_err(map_error_code)
}
pub fn decompress(
&mut self,
data: &[u8],
capacity: usize,
) -> io::Result<Vec<u8>> {
let exact = total_decompressed_size(data);
let capacity = match exact {
Some(needed) if needed > capacity as u64 => {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Destination buffer is too small: {} bytes needed, \
{} given",
needed, capacity
),
))
}
Some(needed) => needed as usize,
None => bound_from_zstd(data).unwrap_or(capacity).min(capacity),
};
let mut buffer = Vec::with_capacity(capacity);
self.decompress_to_buffer(data, &mut buffer)?;
Ok(buffer)
}
pub fn set_parameter(
&mut self,
parameter: zstd_safe::DParameter,
) -> io::Result<()> {
self.context
.set_parameter(parameter)
.map_err(map_error_code)?;
Ok(())
}
crate::decoder_parameters!();
pub fn upper_bound(data: &[u8]) -> Option<usize> {
total_decompressed_size(data)
.and_then(|size| size.try_into().ok())
.or_else(|| bound_from_zstd(data))
}
}
fn bound_from_zstd(_data: &[u8]) -> Option<usize> {
#[cfg(feature = "experimental")]
if let Ok(bound) = zstd_safe::decompress_bound(_data) {
return bound.try_into().ok();
}
None
}
fn total_decompressed_size(mut data: &[u8]) -> Option<u64> {
let mut total = 0u64;
while !data.is_empty() {
let frame_len = zstd_safe::find_frame_compressed_size(data).ok()?;
let content = zstd_safe::get_frame_content_size(data).ok()??;
total = total.checked_add(content)?;
data = data.get(frame_len..)?;
}
Some(total)
}
fn _assert_traits() {
fn _assert_send<T: Send>(_: T) {}
_assert_send(Decompressor::new());
}