use lz4::block::types::{
clear_hash, count, get_index_on_hash, get_position_on_hash, hash4, hash5, hash_position,
memcpy_using_offset, nb_common_bytes, prepare_table, put_index_on_hash, put_position_on_hash,
read16, read32, read_arch, read_le16, read_le32, wild_copy32, wild_copy8, write16, write32,
write_le16, DictDirective, DictIssueDirective, LimitedOutputDirective, StreamStateInternal,
TableType, DEC64TABLE, FASTLOOP_SAFE_DISTANCE, GB, INC32TABLE, KB, LASTLITERALS, LZ4_64KLIMIT,
LZ4_DISTANCE_ABSOLUTE_MAX, LZ4_DISTANCE_MAX, LZ4_HASHLOG, LZ4_HASHTABLESIZE, LZ4_HASH_SIZE_U32,
LZ4_MEMORY_USAGE, LZ4_MIN_LENGTH, LZ4_SKIP_TRIGGER, MATCH_SAFEGUARD_DISTANCE, MB, MFLIMIT,
MINMATCH, ML_BITS, ML_MASK, RUN_BITS, RUN_MASK, WILDCOPYLENGTH,
};
#[test]
fn constants_minmatch() {
assert_eq!(MINMATCH, 4);
}
#[test]
fn constants_wildcopylength() {
assert_eq!(WILDCOPYLENGTH, 8);
}
#[test]
fn constants_lastliterals() {
assert_eq!(LASTLITERALS, 5);
}
#[test]
fn constants_mflimit() {
assert_eq!(MFLIMIT, 12);
}
#[test]
fn constants_match_safeguard_distance() {
assert_eq!(MATCH_SAFEGUARD_DISTANCE, 12);
}
#[test]
fn constants_fastloop_safe_distance() {
assert_eq!(FASTLOOP_SAFE_DISTANCE, 64);
}
#[test]
fn constants_lz4_min_length() {
assert_eq!(LZ4_MIN_LENGTH, 13);
}
#[test]
fn constants_kb_mb_gb() {
assert_eq!(KB, 1024);
assert_eq!(MB, 1 << 20);
assert_eq!(GB, 1 << 30);
}
#[test]
fn constants_distance_max() {
assert_eq!(LZ4_DISTANCE_ABSOLUTE_MAX, 65_535u32);
assert_eq!(LZ4_DISTANCE_MAX, LZ4_DISTANCE_ABSOLUTE_MAX);
}
#[test]
fn constants_ml_run_bits() {
assert_eq!(ML_BITS, 4u32);
assert_eq!(ML_MASK, 0x0Fu32);
assert_eq!(RUN_BITS, 4u32);
assert_eq!(RUN_MASK, 0x0Fu32);
}
#[test]
fn constants_hash_table_sizing() {
assert_eq!(LZ4_MEMORY_USAGE, 14u32);
assert_eq!(LZ4_HASHLOG, 12u32); assert_eq!(LZ4_HASHTABLESIZE, 1 << 14); assert_eq!(LZ4_HASH_SIZE_U32, 1 << 12); }
#[test]
fn constants_64klimit() {
assert_eq!(LZ4_64KLIMIT, 65547);
}
#[test]
fn constants_skip_trigger() {
assert_eq!(LZ4_SKIP_TRIGGER, 6u32);
}
#[test]
fn limited_output_directive_values() {
assert_eq!(LimitedOutputDirective::NotLimited as u32, 0);
assert_eq!(LimitedOutputDirective::LimitedOutput as u32, 1);
assert_eq!(LimitedOutputDirective::FillOutput as u32, 2);
}
#[test]
fn table_type_values() {
assert_eq!(TableType::ClearedTable as u32, 0);
assert_eq!(TableType::ByPtr as u32, 1);
assert_eq!(TableType::ByU32 as u32, 2);
assert_eq!(TableType::ByU16 as u32, 3);
}
#[test]
fn table_type_from_u32_known_values() {
assert_eq!(TableType::from(0u32), TableType::ClearedTable);
assert_eq!(TableType::from(1u32), TableType::ByPtr);
assert_eq!(TableType::from(2u32), TableType::ByU32);
assert_eq!(TableType::from(3u32), TableType::ByU16);
}
#[test]
fn table_type_from_u32_unknown_falls_back_to_cleared() {
assert_eq!(TableType::from(99u32), TableType::ClearedTable);
assert_eq!(TableType::from(u32::MAX), TableType::ClearedTable);
}
#[test]
fn dict_directive_values() {
assert_eq!(DictDirective::NoDict as u32, 0);
assert_eq!(DictDirective::WithPrefix64k as u32, 1);
assert_eq!(DictDirective::UsingExtDict as u32, 2);
assert_eq!(DictDirective::UsingDictCtx as u32, 3);
}
#[test]
fn dict_issue_directive_values() {
assert_eq!(DictIssueDirective::NoDictIssue as u32, 0);
assert_eq!(DictIssueDirective::DictSmall as u32, 1);
}
#[test]
fn stream_state_new_zeroed() {
let s = StreamStateInternal::new();
assert!(s.hash_table.iter().all(|&x| x == 0));
assert!(s.dictionary.is_null());
assert!(s.dict_ctx.is_null());
assert_eq!(s.current_offset, 0);
assert_eq!(s.table_type, TableType::ClearedTable as u32);
assert_eq!(s.dict_size, 0);
}
#[test]
fn stream_state_default_equals_new() {
let a = StreamStateInternal::new();
let b = StreamStateInternal::default();
assert_eq!(a.hash_table, b.hash_table);
assert_eq!(a.current_offset, b.current_offset);
assert_eq!(a.table_type, b.table_type);
assert_eq!(a.dict_size, b.dict_size);
assert_eq!(a.dictionary, b.dictionary);
}
#[test]
fn inc32table_values() {
assert_eq!(INC32TABLE, [0u32, 1, 2, 1, 0, 4, 4, 4]);
}
#[test]
fn dec64table_values() {
assert_eq!(DEC64TABLE, [0i32, 0, 0, -1, -4, 1, 2, 3]);
}
#[test]
fn read16_native_endian() {
let buf: [u8; 4] = [0x01, 0x02, 0x03, 0x04];
let val = unsafe { read16(buf.as_ptr()) };
let expected = u16::from_ne_bytes([0x01, 0x02]);
assert_eq!(val, expected);
}
#[test]
fn read32_native_endian() {
let buf: [u8; 4] = [0xDE, 0xAD, 0xBE, 0xEF];
let val = unsafe { read32(buf.as_ptr()) };
let expected = u32::from_ne_bytes([0xDE, 0xAD, 0xBE, 0xEF]);
assert_eq!(val, expected);
}
#[test]
fn read_arch_reads_pointer_width_bytes() {
let mut buf = [0u8; 16];
buf[0] = 0x42;
let val = unsafe { read_arch(buf.as_ptr()) };
let expected = unsafe { core::ptr::read_unaligned(buf.as_ptr() as *const usize) };
assert_eq!(val, expected);
}
#[test]
fn write16_and_read16_roundtrip() {
let mut buf = [0u8; 4];
unsafe { write16(buf.as_mut_ptr(), 0xABCD) };
let back = unsafe { read16(buf.as_ptr()) };
assert_eq!(back, 0xABCD);
}
#[test]
fn write32_and_read32_roundtrip() {
let mut buf = [0u8; 4];
unsafe { write32(buf.as_mut_ptr(), 0xDEAD_BEEF) };
let back = unsafe { read32(buf.as_ptr()) };
assert_eq!(back, 0xDEAD_BEEF);
}
#[test]
fn read_le16_little_endian_bytes() {
let buf: [u8; 2] = [0x01, 0x02];
let val = unsafe { read_le16(buf.as_ptr()) };
assert_eq!(val, 0x0201u16);
}
#[test]
fn read_le32_little_endian_bytes() {
let buf: [u8; 4] = [0x01, 0x02, 0x03, 0x04];
let val = unsafe { read_le32(buf.as_ptr()) };
assert_eq!(val, 0x04030201u32);
}
#[test]
fn write_le16_stores_little_endian() {
let mut buf = [0u8; 2];
unsafe { write_le16(buf.as_mut_ptr(), 0x0201) };
assert_eq!(buf[0], 0x01);
assert_eq!(buf[1], 0x02);
}
#[test]
fn wild_copy8_copies_exact_bytes() {
let src: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let mut dst = [0u8; 32];
unsafe {
let dst_end = dst.as_mut_ptr().add(8);
wild_copy8(dst.as_mut_ptr(), src.as_ptr(), dst_end);
}
assert_eq!(&dst[..8], &src[..8]);
}
#[test]
fn wild_copy8_copies_multiple_chunks() {
let src: Vec<u8> = (0u8..=255).collect();
let mut dst = vec![0u8; 256 + 16]; let len = 24usize;
unsafe {
let dst_end = dst.as_mut_ptr().add(len);
wild_copy8(dst.as_mut_ptr(), src.as_ptr(), dst_end);
}
assert_eq!(&dst[..len], &src[..len]);
}
#[test]
fn wild_copy32_copies_exact_bytes() {
let src: Vec<u8> = (0u8..128).collect();
let mut dst = vec![0u8; 128 + 32]; let len = 32usize;
unsafe {
let dst_end = dst.as_mut_ptr().add(len);
wild_copy32(dst.as_mut_ptr(), src.as_ptr(), dst_end);
}
assert_eq!(&dst[..len], &src[..len]);
}
#[test]
fn wild_copy32_large_copy() {
let src: Vec<u8> = (0u8..=127).cycle().take(200).collect();
let mut dst = vec![0u8; 200 + 32];
let len = 64usize;
unsafe {
let dst_end = dst.as_mut_ptr().add(len);
wild_copy32(dst.as_mut_ptr(), src.as_ptr(), dst_end);
}
assert_eq!(&dst[..len], &src[..len]);
}
#[test]
fn memcpy_using_offset_offset1_replicates_byte() {
let src_val = 0xABu8;
let mut src_buf = [0u8; 32];
src_buf[0] = src_val;
let mut dst_buf = [0u8; 32];
let copy_len = 16usize;
unsafe {
let src_ptr = src_buf.as_ptr();
let dst_ptr = dst_buf.as_mut_ptr();
let dst_end = dst_ptr.add(copy_len);
memcpy_using_offset(dst_ptr, src_ptr, dst_end, 1);
}
for &b in &dst_buf[..copy_len] {
assert_eq!(b, src_val, "offset=1 should replicate the byte");
}
}
#[test]
fn memcpy_using_offset_offset2_replicates_pattern() {
let mut src_buf = [0u8; 32];
src_buf[0] = 0x11;
src_buf[1] = 0x22;
let mut dst_buf = [0u8; 32];
let copy_len = 16usize;
unsafe {
let src_ptr = src_buf.as_ptr();
let dst_ptr = dst_buf.as_mut_ptr();
let dst_end = dst_ptr.add(copy_len);
memcpy_using_offset(dst_ptr, src_ptr, dst_end, 2);
}
for i in 0..copy_len {
let expected = if i % 2 == 0 { 0x11u8 } else { 0x22u8 };
assert_eq!(dst_buf[i], expected, "offset=2 at index {i}");
}
}
#[test]
fn memcpy_using_offset_offset4_replicates_pattern() {
let mut src_buf = [0u8; 32];
src_buf[0] = 0xAA;
src_buf[1] = 0xBB;
src_buf[2] = 0xCC;
src_buf[3] = 0xDD;
let mut dst_buf = [0u8; 32];
let copy_len = 16usize;
unsafe {
let src_ptr = src_buf.as_ptr();
let dst_ptr = dst_buf.as_mut_ptr();
let dst_end = dst_ptr.add(copy_len);
memcpy_using_offset(dst_ptr, src_ptr, dst_end, 4);
}
let pattern = [0xAAu8, 0xBB, 0xCC, 0xDD];
for i in 0..copy_len {
assert_eq!(dst_buf[i], pattern[i % 4], "offset=4 at index {i}");
}
}
#[test]
fn nb_common_bytes_single_differing_bit() {
let diff: usize = 1; let common = nb_common_bytes(diff);
assert_eq!(common, 0);
}
#[test]
fn nb_common_bytes_first_byte_equal() {
let diff: usize = 0x100; let common = nb_common_bytes(diff);
#[cfg(target_endian = "little")]
assert_eq!(common, 1);
#[cfg(not(target_endian = "little"))]
let _ = common; }
#[test]
fn nb_common_bytes_maximum_pointer_width() {
let diff: usize = 1usize << (usize::BITS - 8);
let common = nb_common_bytes(diff);
#[cfg(target_endian = "little")]
assert_eq!(common, (core::mem::size_of::<usize>() - 1) as u32);
#[cfg(not(target_endian = "little"))]
assert_eq!(common, 0u32);
}
#[test]
fn count_zero_matching_bytes() {
let p_in: [u8; 16] = [0xAA; 16];
let p_match: [u8; 16] = [0xBB; 16]; let result = unsafe {
let limit = p_in.as_ptr().add(p_in.len());
count(p_in.as_ptr(), p_match.as_ptr(), limit)
};
assert_eq!(result, 0);
}
#[test]
fn count_all_matching() {
let data: [u8; 16] = [0x55u8; 16];
let data2: [u8; 16] = [0x55u8; 16];
let result = unsafe {
let limit = data.as_ptr().add(data.len());
count(data.as_ptr(), data2.as_ptr(), limit)
};
assert_eq!(result, 16);
}
#[test]
fn count_partial_match() {
let p_in: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let p_match: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 99, 99, 99, 99, 99, 99, 99, 99];
let result = unsafe {
let limit = p_in.as_ptr().add(p_in.len());
count(p_in.as_ptr(), p_match.as_ptr(), limit)
};
assert_eq!(result, 8);
}
#[test]
fn count_single_match() {
let p_in: [u8; 8] = [0xAA, 0xBB, 0, 0, 0, 0, 0, 0];
let p_match: [u8; 8] = [0xAA, 0xCC, 0, 0, 0, 0, 0, 0];
let result = unsafe {
let limit = p_in.as_ptr().add(p_in.len());
count(p_in.as_ptr(), p_match.as_ptr(), limit)
};
assert_eq!(result, 1);
}
#[test]
fn hash4_zero_sequence_gives_zero() {
assert_eq!(hash4(0u32, TableType::ByU32), 0u32);
}
#[test]
fn hash4_byu16_produces_wider_index() {
let seq = 0xDEAD_BEEFu32;
let h32 = hash4(seq, TableType::ByU32);
let h16 = hash4(seq, TableType::ByU16);
assert!(
h32 < (1u32 << LZ4_HASHLOG),
"ByU32 hash must fit in LZ4_HASHLOG bits"
);
assert!(
h16 < (1u32 << (LZ4_HASHLOG + 1)),
"ByU16 hash must fit in LZ4_HASHLOG+1 bits"
);
}
#[test]
fn hash4_deterministic() {
let seq = 0x1234_5678u32;
assert_eq!(hash4(seq, TableType::ByU32), hash4(seq, TableType::ByU32));
}
#[test]
fn hash5_deterministic() {
let seq = 0x0102_0304_0506_0708u64;
assert_eq!(hash5(seq, TableType::ByU32), hash5(seq, TableType::ByU32));
}
#[test]
fn hash5_byu16_vs_byu32_range() {
let seq = 0xFEDC_BA98_7654_3210u64;
let h32 = hash5(seq, TableType::ByU32);
let h16 = hash5(seq, TableType::ByU16);
assert!(h32 < (1u32 << LZ4_HASHLOG));
assert!(h16 < (1u32 << (LZ4_HASHLOG + 1)));
}
#[test]
fn hash_position_fits_in_table_range() {
let buf = [0x11u8, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88];
let h = unsafe { hash_position(buf.as_ptr(), TableType::ByU32) };
assert!(h < (1u32 << LZ4_HASHLOG));
}
#[test]
fn put_and_get_index_byu32() {
let mut table = [0u32; LZ4_HASH_SIZE_U32];
let h = 42u32;
let idx = 0xDEAD_BEEFu32;
unsafe {
put_index_on_hash(idx, h, table.as_mut_ptr(), TableType::ByU32);
let got = get_index_on_hash(h, table.as_ptr(), TableType::ByU32);
assert_eq!(got, idx);
}
}
#[test]
fn put_and_get_index_byu16() {
let mut table = [0u32; LZ4_HASH_SIZE_U32 * 2]; let h = 10u32;
let idx = 0xBEEFu32; unsafe {
put_index_on_hash(idx, h, table.as_mut_ptr(), TableType::ByU16);
let got = get_index_on_hash(h, table.as_ptr(), TableType::ByU16);
assert_eq!(got, idx);
}
}
#[test]
fn clear_hash_byu32() {
let mut table = [0xFFFF_FFFFu32; LZ4_HASH_SIZE_U32];
let h = 7u32;
unsafe {
clear_hash(h, table.as_mut_ptr(), TableType::ByU32);
}
assert_eq!(table[h as usize], 0u32);
assert_eq!(table[0], 0xFFFF_FFFFu32);
}
#[test]
fn clear_hash_byu16() {
let mut table = [0xFFFF_FFFFu32; LZ4_HASH_SIZE_U32 * 2];
let h = 3u32;
unsafe {
clear_hash(h, table.as_mut_ptr(), TableType::ByU16);
let tbl = table.as_ptr() as *const u16;
let val = *tbl.add(h as usize);
assert_eq!(val, 0u16);
}
}
#[test]
fn put_and_get_position_byptr() {
let data = [0xAAu8; 8];
let ptr: *const u8 = data.as_ptr();
let mut table = [core::ptr::null::<u8>(); LZ4_HASH_SIZE_U32];
let h = 5u32;
unsafe {
put_position_on_hash(
ptr,
h,
table.as_mut_ptr() as *mut *const u8,
TableType::ByPtr,
);
let got = get_position_on_hash(h, table.as_ptr() as *const *const u8, TableType::ByPtr);
assert_eq!(got, ptr);
}
}
#[test]
fn clear_hash_byptr_sets_null() {
let data = [0u8; 8];
let ptr: *const u8 = data.as_ptr();
let mut table = [ptr; LZ4_HASH_SIZE_U32];
let h = 2u32;
unsafe {
clear_hash(h, table.as_mut_ptr() as *mut u32, TableType::ByPtr);
assert!(table[h as usize].is_null());
}
}
#[test]
fn prepare_table_cleared_table_sets_dict_fields_null() {
let mut ctx = StreamStateInternal::new();
ctx.current_offset = 0;
ctx.dict_size = 99;
unsafe {
prepare_table(&mut ctx as *mut _, 100, TableType::ByU32);
}
assert!(ctx.dictionary.is_null());
assert!(ctx.dict_ctx.is_null());
assert_eq!(ctx.dict_size, 0);
}
#[test]
fn prepare_table_type_change_resets_hash_table() {
let mut ctx = StreamStateInternal::new();
ctx.table_type = TableType::ByU32 as u32;
ctx.hash_table[0] = 0xDEAD;
ctx.hash_table[100] = 0xBEEF;
unsafe {
prepare_table(&mut ctx as *mut _, 100, TableType::ByU16);
}
assert!(ctx.hash_table.iter().all(|&x| x == 0));
}
#[test]
fn prepare_table_byu32_adds_64k_gap_when_offset_nonzero() {
let mut ctx = StreamStateInternal::new();
ctx.table_type = TableType::ByU32 as u32;
ctx.current_offset = 1000;
unsafe {
prepare_table(&mut ctx as *mut _, 100, TableType::ByU32);
}
assert_ne!(ctx.current_offset, 1000u32);
}
#[test]
fn prepare_table_large_input_forces_reset() {
let mut ctx = StreamStateInternal::new();
ctx.table_type = TableType::ByU32 as u32;
ctx.hash_table[0] = 0xDEAD;
unsafe {
prepare_table(&mut ctx as *mut _, 4 * KB as i32, TableType::ByU32);
}
assert!(ctx.hash_table.iter().all(|&x| x == 0));
assert_eq!(ctx.table_type, TableType::ClearedTable as u32);
}