use core::ptr;
pub const MINMATCH: usize = 4;
pub const WILDCOPYLENGTH: usize = 8;
pub const LASTLITERALS: usize = 5;
pub const MFLIMIT: usize = 12;
pub const MATCH_SAFEGUARD_DISTANCE: usize = 2 * WILDCOPYLENGTH - MINMATCH;
pub const FASTLOOP_SAFE_DISTANCE: usize = 64;
pub const LZ4_MIN_LENGTH: usize = MFLIMIT + 1;
pub const KB: usize = 1 << 10;
pub const MB: usize = 1 << 20;
pub const GB: usize = 1 << 30;
pub const LZ4_DISTANCE_ABSOLUTE_MAX: u32 = 65_535;
pub const LZ4_DISTANCE_MAX: u32 = LZ4_DISTANCE_ABSOLUTE_MAX;
pub const ML_BITS: u32 = 4;
pub const ML_MASK: u32 = (1u32 << ML_BITS) - 1;
pub const RUN_BITS: u32 = 8 - ML_BITS;
pub const RUN_MASK: u32 = (1u32 << RUN_BITS) - 1;
pub const LZ4_MEMORY_USAGE: u32 = 14;
pub const LZ4_HASHLOG: u32 = LZ4_MEMORY_USAGE - 2; pub const LZ4_HASHTABLESIZE: usize = 1 << 14; pub const LZ4_HASH_SIZE_U32: usize = 1 << 12;
pub const LZ4_64KLIMIT: usize = (64 * KB) + (MFLIMIT - 1);
pub const LZ4_SKIP_TRIGGER: u32 = 6;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u32)]
pub enum LimitedOutputDirective {
NotLimited = 0,
LimitedOutput = 1,
FillOutput = 2,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u32)]
pub enum TableType {
ClearedTable = 0,
ByPtr = 1,
ByU32 = 2,
ByU16 = 3,
}
impl From<u32> for TableType {
fn from(v: u32) -> Self {
match v {
1 => TableType::ByPtr,
2 => TableType::ByU32,
3 => TableType::ByU16,
_ => TableType::ClearedTable,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u32)]
pub enum DictDirective {
NoDict = 0,
WithPrefix64k = 1,
UsingExtDict = 2,
UsingDictCtx = 3,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u32)]
pub enum DictIssueDirective {
NoDictIssue = 0,
DictSmall = 1,
}
#[repr(C)]
pub struct StreamStateInternal {
pub hash_table: [u32; LZ4_HASH_SIZE_U32],
pub dictionary: *const u8,
pub dict_ctx: *const StreamStateInternal,
pub current_offset: u32,
pub table_type: u32,
pub dict_size: u32,
}
unsafe impl Send for StreamStateInternal {}
impl StreamStateInternal {
pub const fn new() -> Self {
Self {
hash_table: [0u32; LZ4_HASH_SIZE_U32],
dictionary: ptr::null(),
dict_ctx: ptr::null(),
current_offset: 0,
table_type: TableType::ClearedTable as u32,
dict_size: 0,
}
}
}
impl Default for StreamStateInternal {
fn default() -> Self {
Self::new()
}
}
#[inline(always)]
pub unsafe fn read16(ptr: *const u8) -> u16 {
core::ptr::read_unaligned(ptr as *const u16)
}
#[inline(always)]
pub unsafe fn read32(ptr: *const u8) -> u32 {
core::ptr::read_unaligned(ptr as *const u32)
}
#[inline(always)]
pub unsafe fn read_arch(ptr: *const u8) -> usize {
core::ptr::read_unaligned(ptr as *const usize)
}
#[inline(always)]
pub unsafe fn write16(ptr: *mut u8, value: u16) {
core::ptr::write_unaligned(ptr as *mut u16, value);
}
#[inline(always)]
pub unsafe fn write32(ptr: *mut u8, value: u32) {
core::ptr::write_unaligned(ptr as *mut u32, value);
}
#[inline(always)]
pub unsafe fn read_le16(ptr: *const u8) -> u16 {
#[cfg(target_endian = "little")]
{
read16(ptr)
}
#[cfg(not(target_endian = "little"))]
{
(*ptr) as u16 | ((*ptr.add(1)) as u16) << 8
}
}
#[inline(always)]
pub unsafe fn read_le32(ptr: *const u8) -> u32 {
#[cfg(target_endian = "little")]
{
read32(ptr)
}
#[cfg(not(target_endian = "little"))]
{
(*ptr) as u32
| ((*ptr.add(1)) as u32) << 8
| ((*ptr.add(2)) as u32) << 16
| ((*ptr.add(3)) as u32) << 24
}
}
#[inline(always)]
pub unsafe fn write_le16(ptr: *mut u8, value: u16) {
#[cfg(target_endian = "little")]
{
write16(ptr, value);
}
#[cfg(not(target_endian = "little"))]
{
*ptr = value as u8;
*ptr.add(1) = (value >> 8) as u8;
}
}
pub static INC32TABLE: [u32; 8] = [0, 1, 2, 1, 0, 4, 4, 4];
pub static DEC64TABLE: [i32; 8] = [0, 0, 0, -1, -4, 1, 2, 3];
#[inline(always)]
pub unsafe fn wild_copy8(mut dst: *mut u8, mut src: *const u8, dst_end: *mut u8) {
loop {
core::ptr::copy_nonoverlapping(src, dst, 8);
dst = dst.add(8);
src = src.add(8);
if dst >= dst_end {
break;
}
}
}
#[inline(always)]
pub unsafe fn wild_copy32(mut dst: *mut u8, mut src: *const u8, dst_end: *mut u8) {
loop {
core::ptr::copy_nonoverlapping(src, dst, 16);
core::ptr::copy_nonoverlapping(src.add(16), dst.add(16), 16);
dst = dst.add(32);
src = src.add(32);
if dst >= dst_end {
break;
}
}
}
#[inline]
pub unsafe fn memcpy_using_offset_base(
mut dst: *mut u8,
mut src: *const u8,
dst_end: *mut u8,
offset: usize,
) {
debug_assert!(src.add(offset) == dst);
if offset < 8 {
write32(dst, 0);
*dst = *src;
*dst.add(1) = *src.add(1);
*dst.add(2) = *src.add(2);
*dst.add(3) = *src.add(3);
src = src.add(INC32TABLE[offset] as usize);
core::ptr::copy_nonoverlapping(src, dst.add(4), 4);
src = src.offset(-(DEC64TABLE[offset] as isize));
dst = dst.add(8);
} else {
core::ptr::copy_nonoverlapping(src, dst, 8);
dst = dst.add(8);
src = src.add(8);
}
wild_copy8(dst, src, dst_end);
}
#[inline]
pub unsafe fn memcpy_using_offset(
mut dst: *mut u8,
src: *const u8,
dst_end: *mut u8,
offset: usize,
) {
let mut v = [0u8; 8];
match offset {
1 => {
let b = *src;
v = [b, b, b, b, b, b, b, b];
}
2 => {
core::ptr::copy_nonoverlapping(src, v.as_mut_ptr(), 2);
core::ptr::copy_nonoverlapping(src, v.as_mut_ptr().add(2), 2);
core::ptr::copy_nonoverlapping(v.as_ptr(), v.as_mut_ptr().add(4), 4);
}
4 => {
core::ptr::copy_nonoverlapping(src, v.as_mut_ptr(), 4);
core::ptr::copy_nonoverlapping(src, v.as_mut_ptr().add(4), 4);
}
_ => {
memcpy_using_offset_base(dst, src, dst_end, offset);
return;
}
}
core::ptr::copy_nonoverlapping(v.as_ptr(), dst, 8);
dst = dst.add(8);
while dst < dst_end {
core::ptr::copy_nonoverlapping(v.as_ptr(), dst, 8);
dst = dst.add(8);
}
}
#[cfg(target_pointer_width = "64")]
#[inline(always)]
pub fn nb_common_bytes(val: usize) -> u32 {
debug_assert!(val != 0);
#[cfg(target_endian = "little")]
{
(val as u64).trailing_zeros() >> 3
}
#[cfg(not(target_endian = "little"))]
{
(val as u64).leading_zeros() >> 3
}
}
#[cfg(target_pointer_width = "32")]
#[inline(always)]
pub fn nb_common_bytes(val: usize) -> u32 {
debug_assert!(val != 0);
#[cfg(target_endian = "little")]
{
(val as u32).trailing_zeros() >> 3
}
#[cfg(not(target_endian = "little"))]
{
(val as u32).leading_zeros() >> 3
}
}
#[inline(always)]
pub unsafe fn count(mut p_in: *const u8, mut p_match: *const u8, p_in_limit: *const u8) -> u32 {
let p_start = p_in;
let step = core::mem::size_of::<usize>();
if p_in < p_in_limit.sub(step - 1) {
let diff = read_arch(p_match) ^ read_arch(p_in);
if diff == 0 {
p_in = p_in.add(step);
p_match = p_match.add(step);
} else {
return nb_common_bytes(diff);
}
}
while p_in < p_in_limit.sub(step - 1) {
let diff = read_arch(p_match) ^ read_arch(p_in);
if diff == 0 {
p_in = p_in.add(step);
p_match = p_match.add(step);
continue;
}
p_in = p_in.add(nb_common_bytes(diff) as usize);
return p_in.offset_from(p_start) as u32;
}
if step == 8 && p_in < p_in_limit.sub(3) && read32(p_match) == read32(p_in) {
p_in = p_in.add(4);
p_match = p_match.add(4);
}
if p_in < p_in_limit.sub(1) && read16(p_match) == read16(p_in) {
p_in = p_in.add(2);
p_match = p_match.add(2);
}
if p_in < p_in_limit && *p_match == *p_in {
p_in = p_in.add(1);
}
p_in.offset_from(p_start) as u32
}
#[inline(always)]
pub fn hash4(sequence: u32, table_type: TableType) -> u32 {
let hash_log = if table_type == TableType::ByU16 {
LZ4_HASHLOG + 1
} else {
LZ4_HASHLOG
};
sequence.wrapping_mul(2_654_435_761u32) >> (32 - hash_log)
}
#[inline(always)]
pub fn hash5(sequence: u64, table_type: TableType) -> u32 {
let hash_log = if table_type == TableType::ByU16 {
LZ4_HASHLOG + 1
} else {
LZ4_HASHLOG
};
#[cfg(target_endian = "little")]
{
const PRIME5: u64 = 889_523_592_379;
(((sequence << 24).wrapping_mul(PRIME5)) >> (64 - hash_log)) as u32
}
#[cfg(not(target_endian = "little"))]
{
const PRIME8: u64 = 11_400_714_785_074_694_791;
(((sequence >> 24).wrapping_mul(PRIME8)) >> (64 - hash_log)) as u32
}
}
#[inline(always)]
pub unsafe fn hash_position(p: *const u8, table_type: TableType) -> u32 {
#[cfg(target_pointer_width = "64")]
if table_type != TableType::ByU16 {
return hash5(read_arch(p) as u64, table_type);
}
hash4(read32(p), table_type)
}
#[inline(always)]
pub unsafe fn clear_hash(h: u32, table_base: *mut u32, table_type: TableType) {
match table_type {
TableType::ByPtr => {
let tbl = table_base as *mut *const u8;
*tbl.add(h as usize) = ptr::null();
}
TableType::ByU32 => {
*table_base.add(h as usize) = 0;
}
TableType::ByU16 => {
let tbl = table_base as *mut u16;
*tbl.add(h as usize) = 0;
}
TableType::ClearedTable => {
debug_assert!(false, "clear_hash called on ClearedTable");
}
}
}
#[inline(always)]
pub unsafe fn put_index_on_hash(idx: u32, h: u32, table_base: *mut u32, table_type: TableType) {
match table_type {
TableType::ByU32 => {
*table_base.add(h as usize) = idx;
}
TableType::ByU16 => {
debug_assert!(idx < 65536, "put_index_on_hash: idx overflows u16");
let tbl = table_base as *mut u16;
*tbl.add(h as usize) = idx as u16;
}
_ => {
debug_assert!(false, "put_index_on_hash: invalid table type");
}
}
}
#[inline(always)]
pub unsafe fn put_position_on_hash(
p: *const u8,
h: u32,
table_base: *mut *const u8,
_table_type: TableType,
) {
*table_base.add(h as usize) = p;
}
#[inline(always)]
pub unsafe fn put_position(p: *const u8, table_base: *mut *const u8, table_type: TableType) {
let h = hash_position(p, table_type);
put_position_on_hash(p, h, table_base, table_type);
}
#[inline(always)]
pub unsafe fn get_index_on_hash(h: u32, table_base: *const u32, table_type: TableType) -> u32 {
match table_type {
TableType::ByU32 => {
debug_assert!((h as usize) < (1 << (LZ4_MEMORY_USAGE - 2)));
*table_base.add(h as usize)
}
TableType::ByU16 => {
debug_assert!((h as usize) < (1 << (LZ4_MEMORY_USAGE - 1)));
let tbl = table_base as *const u16;
*tbl.add(h as usize) as u32
}
_ => {
debug_assert!(false, "get_index_on_hash: invalid table type");
0
}
}
}
#[inline(always)]
pub unsafe fn get_position_on_hash(
h: u32,
table_base: *const *const u8,
_table_type: TableType,
) -> *const u8 {
*table_base.add(h as usize)
}
#[inline(always)]
pub unsafe fn get_position(
p: *const u8,
table_base: *const *const u8,
table_type: TableType,
) -> *const u8 {
let h = hash_position(p, table_type);
get_position_on_hash(h, table_base, table_type)
}
pub unsafe fn prepare_table(
cctx: *mut StreamStateInternal,
input_size: i32,
table_type: TableType,
) {
let ctx = &mut *cctx;
let current_type = TableType::from(ctx.table_type);
if current_type != TableType::ClearedTable {
debug_assert!(input_size >= 0);
let need_reset = current_type != table_type
|| (table_type == TableType::ByU16
&& ctx.current_offset.wrapping_add(input_size as u32) >= 0xFFFF)
|| (table_type == TableType::ByU32 && ctx.current_offset > GB as u32)
|| table_type == TableType::ByPtr
|| input_size >= (4 * KB as i32);
if need_reset {
ptr::write_bytes(ctx.hash_table.as_mut_ptr(), 0, LZ4_HASH_SIZE_U32);
ctx.current_offset = 0;
ctx.table_type = TableType::ClearedTable as u32;
}
}
if ctx.current_offset != 0 && table_type == TableType::ByU32 {
ctx.current_offset = ctx.current_offset.wrapping_add(64 * KB as u32);
}
ctx.dict_ctx = ptr::null();
ctx.dictionary = ptr::null();
ctx.dict_size = 0;
}