use super::{api, Result};
use crate::{common::DEFAULT_BUF_SIZE, lz4f::Preferences, Error, ErrorKind};
use std::{cell::RefCell, ops::Deref};
#[must_use]
pub fn max_compressed_size(original_size: usize, prefs: &Preferences) -> usize {
api::compress_frame_bound(original_size, prefs)
}
pub fn compress(src: &[u8], dst: &mut [u8], prefs: &Preferences) -> Result<usize> {
compress_to_ptr(src, dst.as_mut_ptr(), dst.len(), prefs)
}
fn compress_to_ptr(src: &[u8], dst: *mut u8, dst_len: usize, prefs: &Preferences) -> Result<usize> {
let mut prefs = *prefs;
if prefs.frame_info().content_size() > 0 {
prefs.set_content_size(src.len());
}
api::compress(src, dst, dst_len, &prefs)
}
pub fn compress_to_vec(src: &[u8], dst: &mut Vec<u8>, prefs: &Preferences) -> Result<usize> {
let orig_len = dst.len();
dst.reserve(max_compressed_size(src.len(), prefs));
#[allow(unsafe_code)]
unsafe {
let result = compress_to_ptr(
src,
dst.as_mut_ptr().add(orig_len),
dst.capacity() - orig_len,
prefs,
);
dst.set_len(orig_len + result.as_ref().unwrap_or(&0));
result
}
}
pub fn decompress_to_vec(src: &[u8], dst: &mut Vec<u8>) -> Result<usize> {
let header_len = dst.len();
let mut src_offset = 0;
let mut dst_offset = header_len;
DecompressionCtx::with(|ctx| {
let mut ctx = ctx.borrow_mut();
ctx.reset();
loop {
dst.resize_with(dst.len() + DEFAULT_BUF_SIZE, Default::default);
match ctx.decompress_dict(&src[src_offset..], &mut dst[dst_offset..], &[], false) {
Ok((src_len, dst_len, expected)) => {
src_offset += src_len;
dst_offset += dst_len;
if expected == 0 {
dst.resize_with(dst_offset, Default::default);
return Ok(dst_offset - header_len);
} else if src_offset >= src.len() {
dst.resize_with(header_len, Default::default);
return Err(Error::new(ErrorKind::CompressedDataIncomplete).into());
}
}
Err(err) => {
dst.resize_with(header_len, Default::default);
return Err(err);
}
}
}
})
}
struct DecompressionCtx(RefCell<api::DecompressionContext>);
impl DecompressionCtx {
fn new() -> Self {
Self(RefCell::new(api::DecompressionContext::new().unwrap()))
}
fn with<F, R>(f: F) -> R
where
F: FnOnce(&RefCell<api::DecompressionContext>) -> R,
{
DECOMPRESSION_CTX.with(|state| (f)(state))
}
}
impl Deref for DecompressionCtx {
type Target = RefCell<api::DecompressionContext>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
thread_local!(static DECOMPRESSION_CTX: DecompressionCtx = DecompressionCtx::new());