use super::compress_hc::{compress_hash_chain, compress_optimal};
use super::lz4mid::lz4mid_compress;
use super::search::{insert, HcFavor};
use super::types::{
get_clevel_params, DictCtxDirective, HcCCtxInternal, HcStrategy, LZ4HC_CLEVEL_MAX,
};
use crate::block::compress::LZ4_MAX_INPUT_SIZE;
use crate::block::types::LimitedOutputDirective;
const KB_64: usize = 64 * 1024;
const KB_4: usize = 4 * 1024;
impl HcCCtxInternal {
#[inline]
pub fn is_compatible(&self, other: &Self) -> bool {
let is_mid_self =
get_clevel_params(self.compression_level as i32).strat == HcStrategy::Lz4Mid;
let is_mid_other =
get_clevel_params(other.compression_level as i32).strat == HcStrategy::Lz4Mid;
is_mid_self == is_mid_other
}
}
pub unsafe fn set_external_dict(ctx: &mut HcCCtxInternal, new_block: *const u8) {
if ctx.end >= ctx.prefix_start.add(4)
&& get_clevel_params(ctx.compression_level as i32).strat != HcStrategy::Lz4Mid
{
insert(ctx, ctx.end.sub(3));
}
ctx.low_limit = ctx.dict_limit;
ctx.dict_start = ctx.prefix_start;
ctx.dict_limit = ctx
.dict_limit
.wrapping_add((ctx.end as usize - ctx.prefix_start as usize) as u32);
ctx.prefix_start = new_block;
ctx.end = new_block;
ctx.next_to_update = ctx.dict_limit;
ctx.dict_ctx = core::ptr::null();
}
#[inline(always)]
pub unsafe fn compress_generic_internal(
ctx: &mut HcCCtxInternal,
src: *const u8,
dst: *mut u8,
src_size_ptr: &mut i32,
dst_capacity: i32,
c_level: i32,
limit: LimitedOutputDirective,
dict: DictCtxDirective,
) -> i32 {
if limit == LimitedOutputDirective::FillOutput && dst_capacity < 1 {
return 0;
}
if limit != LimitedOutputDirective::NotLimited && dst_capacity < 5 {
*src_size_ptr = 0;
ctx.dirty = 1;
return 0;
}
if *src_size_ptr as u32 > LZ4_MAX_INPUT_SIZE {
return 0;
}
ctx.end = ctx.end.add(*src_size_ptr as usize);
let c_param = get_clevel_params(c_level);
let favor = if ctx.favor_dec_speed != 0 {
HcFavor::DecompressionSpeed
} else {
HcFavor::CompressionRatio
};
let result = match c_param.strat {
HcStrategy::Lz4Mid => {
lz4mid_compress(ctx, src, dst, src_size_ptr, dst_capacity, limit, dict)
}
HcStrategy::Lz4Hc => compress_hash_chain(
ctx,
src,
dst,
src_size_ptr,
dst_capacity,
c_param.nb_searches as i32,
limit,
dict,
),
HcStrategy::Lz4Opt => compress_optimal(
ctx,
src,
dst,
src_size_ptr,
dst_capacity,
c_param.nb_searches as i32,
c_param.target_length as usize,
limit,
c_level >= LZ4HC_CLEVEL_MAX, dict,
favor,
),
};
if result <= 0 {
ctx.dirty = 1;
}
result
}
pub unsafe fn compress_generic_no_dict_ctx(
ctx: &mut HcCCtxInternal,
src: *const u8,
dst: *mut u8,
src_size_ptr: &mut i32,
dst_capacity: i32,
c_level: i32,
limit: LimitedOutputDirective,
) -> i32 {
debug_assert!(
ctx.dict_ctx.is_null(),
"compress_generic_no_dict_ctx: dict_ctx must be null"
);
compress_generic_internal(
ctx,
src,
dst,
src_size_ptr,
dst_capacity,
c_level,
limit,
DictCtxDirective::NoDictCtx,
)
}
pub unsafe fn compress_generic_dict_ctx(
ctx: &mut HcCCtxInternal,
src: *const u8,
dst: *mut u8,
src_size_ptr: &mut i32,
dst_capacity: i32,
c_level: i32,
limit: LimitedOutputDirective,
) -> i32 {
debug_assert!(
!ctx.dict_ctx.is_null(),
"compress_generic_dict_ctx: dict_ctx must be non-null"
);
let position = (ctx.end as usize).wrapping_sub(ctx.prefix_start as usize)
+ (ctx.dict_limit - ctx.low_limit) as usize;
if position >= KB_64 {
ctx.dict_ctx = core::ptr::null();
compress_generic_no_dict_ctx(ctx, src, dst, src_size_ptr, dst_capacity, c_level, limit)
} else if position == 0 && *src_size_ptr > KB_4 as i32 && ctx.is_compatible(&*ctx.dict_ctx) {
let dict_ctx_ptr = ctx.dict_ctx;
core::ptr::copy_nonoverlapping(dict_ctx_ptr, ctx as *mut HcCCtxInternal, 1);
set_external_dict(ctx, src);
ctx.compression_level = c_level as i16;
compress_generic_no_dict_ctx(ctx, src, dst, src_size_ptr, dst_capacity, c_level, limit)
} else {
compress_generic_internal(
ctx,
src,
dst,
src_size_ptr,
dst_capacity,
c_level,
limit,
DictCtxDirective::UsingDictCtxHc,
)
}
}
pub unsafe fn compress_generic(
ctx: &mut HcCCtxInternal,
src: *const u8,
dst: *mut u8,
src_size_ptr: &mut i32,
dst_capacity: i32,
c_level: i32,
limit: LimitedOutputDirective,
) -> i32 {
if ctx.dict_ctx.is_null() {
compress_generic_no_dict_ctx(ctx, src, dst, src_size_ptr, dst_capacity, c_level, limit)
} else {
compress_generic_dict_ctx(ctx, src, dst, src_size_ptr, dst_capacity, c_level, limit)
}
}