use std::cell::{RefCell, RefMut};
use thread_local::ThreadLocal;
use zstd_safe::{CCtx, CDict, DCtx, DDict};
#[derive(Default)]
pub struct Compression(CompressionInner);
impl Compression {
pub fn new() -> Self {
Compression(CompressionInner::new())
}
pub fn compress_to_buffer<C: zstd_safe::WriteBuf + ?Sized>(
&self,
source: &[u8],
destination: &mut C,
level: i32,
) -> Result<usize, &'static str> {
self.0.compress_to_buffer(source, destination, level)
}
pub fn compress(&self, data: &[u8], level: i32) -> Result<Vec<u8>, &'static str> {
let mut buffer = make_compressed_data_buffer(data.len());
self.compress_to_buffer(data, &mut buffer, level)?;
Ok(buffer)
}
pub fn decompress_to_buffer<C: zstd_safe::WriteBuf + ?Sized>(
&self,
source: &[u8],
destination: &mut C,
) -> Result<usize, &'static str> {
self.0.decompress_to_buffer(source, destination)
}
}
pub struct CompressionWithDict {
inner: CompressionInner,
com_dict: CDict<'static>,
de_dict: DDict<'static>,
}
impl CompressionWithDict {
pub fn new(dict: &[u8], compression_level: i32) -> Self {
CompressionWithDict {
inner: CompressionInner::new(),
com_dict: CDict::create(dict, compression_level),
de_dict: DDict::create(dict),
}
}
pub fn compress_to_buffer<C: zstd_safe::WriteBuf + ?Sized>(
&self,
source: &[u8],
destination: &mut C,
) -> Result<usize, &'static str> {
self.inner
.compress_to_buffer_using_dict(source, destination, &self.com_dict)
}
pub fn compress(&self, data: &[u8]) -> Result<Vec<u8>, &'static str> {
let mut buffer = make_compressed_data_buffer(data.len());
self.compress_to_buffer(data, &mut buffer)?;
Ok(buffer)
}
pub fn decompress_to_buffer<C: zstd_safe::WriteBuf + ?Sized>(
&self,
source: &[u8],
destination: &mut C,
) -> Result<usize, &'static str> {
self.inner
.decompress_to_buffer_using_dict(source, destination, &self.de_dict)
}
}
#[derive(Default)]
struct CompressionInner {
com_context: ThreadLocal<RefCell<zstd_safe::CCtx<'static>>>,
de_context: ThreadLocal<RefCell<zstd_safe::DCtx<'static>>>,
}
impl CompressionInner {
fn new() -> Self {
CompressionInner {
com_context: ThreadLocal::new(),
de_context: ThreadLocal::new(),
}
}
#[inline]
fn get_com_context(&self) -> RefMut<'_, CCtx<'static>> {
self.com_context
.get_or(|| RefCell::new(CCtx::create()))
.borrow_mut()
}
#[inline]
fn get_de_context(&self) -> RefMut<'_, DCtx<'static>> {
self.de_context
.get_or(|| RefCell::new(DCtx::create()))
.borrow_mut()
}
fn compress_to_buffer<C: zstd_safe::WriteBuf + ?Sized>(
&self,
source: &[u8],
destination: &mut C,
level: i32,
) -> Result<usize, &'static str> {
self.get_com_context()
.compress(destination, source, level)
.map_err(zstd_safe::get_error_name)
}
fn decompress_to_buffer<C: zstd_safe::WriteBuf + ?Sized>(
&self,
source: &[u8],
destination: &mut C,
) -> Result<usize, &'static str> {
self.get_de_context()
.decompress(destination, source)
.map_err(zstd_safe::get_error_name)
}
fn compress_to_buffer_using_dict<C: zstd_safe::WriteBuf + ?Sized>(
&self,
source: &[u8],
destination: &mut C,
dict: &CDict,
) -> Result<usize, &'static str> {
self.get_com_context()
.compress_using_cdict(destination, source, dict)
.map_err(zstd_safe::get_error_name)
}
pub fn decompress_to_buffer_using_dict<C: zstd_safe::WriteBuf + ?Sized>(
&self,
source: &[u8],
destination: &mut C,
dict: &DDict,
) -> Result<usize, &'static str> {
self.get_de_context()
.decompress_using_ddict(destination, source, dict)
.map_err(zstd_safe::get_error_name)
}
}
#[inline]
fn make_compressed_data_buffer(uncompressed_len: usize) -> Vec<u8> {
let buffer_len = zstd_safe::compress_bound(uncompressed_len);
Vec::with_capacity(buffer_len)
}