extern crate std;
use alloc::vec::Vec;
use crate::decoding::StreamingDecoder;
use crate::encoding::{
CompressionLevel, CompressionParameters, FrameCompressor, Strategy, compress_slice_to_vec,
compress_with_parameters,
};
use crate::io::Read;
fn generate_data(seed: u64, len: usize) -> Vec<u8> {
let mut state = seed;
let mut data = Vec::with_capacity(len);
for _ in 0..len {
state = state
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
data.push((state >> 33) as u8);
}
data
}
fn long_range_repetitive(total_len: usize) -> Vec<u8> {
let motif = generate_data(0xABCD_1234, 16);
let mut base = Vec::with_capacity(4096);
while base.len() < 4096 {
base.extend_from_slice(&motif);
}
base.truncate(4096);
let mut data = Vec::with_capacity(total_len);
let mut counter = 0u64;
while data.len() < total_len {
data.extend_from_slice(&base);
counter = counter.wrapping_add(1);
data.extend_from_slice(&counter.to_le_bytes());
}
data.truncate(total_len);
data
}
fn decode(compressed: &[u8]) -> Vec<u8> {
let mut decoder = StreamingDecoder::new(compressed).unwrap();
let mut out = Vec::new();
decoder.read_to_end(&mut out).unwrap();
out
}
#[test]
fn empty_override_is_byte_identical_to_level() {
let data = generate_data(7, 64 * 1024);
for level in [
CompressionLevel::Fastest,
CompressionLevel::Default,
CompressionLevel::Better,
CompressionLevel::Best,
CompressionLevel::Level(5),
CompressionLevel::Level(19),
] {
let params = CompressionParameters::builder(level).build().unwrap();
let via_params = compress_with_parameters(&data, ¶ms);
let via_level = compress_slice_to_vec(&data, level);
assert_eq!(
via_params, via_level,
"empty override diverged from level {level:?}",
);
}
}
#[test]
fn custom_parameters_round_trip() {
let data = generate_data(99, 96 * 1024);
let cases = [
CompressionParameters::builder(CompressionLevel::Level(3))
.window_log(18)
.strategy(Strategy::Dfast)
.build()
.unwrap(),
CompressionParameters::builder(CompressionLevel::Level(9))
.strategy(Strategy::Lazy2)
.search_log(6)
.target_length(64)
.build()
.unwrap(),
CompressionParameters::builder(CompressionLevel::Level(19))
.window_log(22)
.hash_log(23)
.chain_log(24)
.strategy(Strategy::Btultra2)
.build()
.unwrap(),
];
for params in cases {
let compressed = compress_with_parameters(&data, ¶ms);
assert_eq!(
decode(&compressed),
data,
"round-trip failed for {params:?}"
);
}
}
#[test]
fn greedy_strategy_override_round_trips() {
let data = generate_data(0x5151, 80 * 1024);
let params = CompressionParameters::builder(CompressionLevel::Level(7))
.strategy(Strategy::Greedy)
.build()
.unwrap();
let compressed = compress_with_parameters(&data, ¶ms);
assert_eq!(decode(&compressed), data);
let compressible = long_range_repetitive(80 * 1024);
let greedy = compress_with_parameters(
&compressible,
&CompressionParameters::builder(CompressionLevel::Level(5))
.strategy(Strategy::Greedy)
.build()
.unwrap(),
);
assert!(
greedy.len() < compressible.len() / 2,
"greedy did not compress repetitive fixture: {} vs {}",
greedy.len(),
compressible.len(),
);
assert_eq!(decode(&greedy), compressible);
}
#[cfg(feature = "hash")]
#[test]
fn ldm_on_round_trips() {
let data = long_range_repetitive(512 * 1024);
let params = CompressionParameters::builder(CompressionLevel::Level(19))
.enable_long_distance_matching(true)
.build()
.unwrap();
let compressed = compress_with_parameters(&data, ¶ms);
assert_eq!(decode(&compressed), data);
}
#[cfg(feature = "hash")]
#[test]
fn ldm_on_is_not_larger_than_ldm_off() {
let data = long_range_repetitive(1024 * 1024);
let off = CompressionParameters::builder(CompressionLevel::Level(19))
.build()
.unwrap();
let on = CompressionParameters::builder(CompressionLevel::Level(19))
.enable_long_distance_matching(true)
.build()
.unwrap();
let off_sz = compress_with_parameters(&data, &off).len();
let on_compressed = compress_with_parameters(&data, &on);
let on_sz = on_compressed.len();
assert_eq!(decode(&on_compressed), data);
assert!(
on_sz <= off_sz,
"LDM-on ({on_sz}) larger than LDM-off ({off_sz})",
);
}
#[cfg(feature = "hash")]
#[test]
fn ldm_custom_knobs_round_trip() {
let data = long_range_repetitive(512 * 1024);
let params = CompressionParameters::builder(CompressionLevel::Level(19))
.ldm_hash_log(20)
.ldm_min_match(48)
.ldm_bucket_size_log(4)
.ldm_hash_rate_log(6)
.build()
.unwrap();
assert_eq!(decode(&compress_with_parameters(&data, ¶ms)), data);
}
#[test]
fn set_parameters_streaming_round_trip() {
let data = generate_data(0xC0FFEE, 70 * 1024);
let params = CompressionParameters::builder(CompressionLevel::Level(11))
.strategy(Strategy::Lazy2)
.window_log(20)
.build()
.unwrap();
let mut compressed = Vec::new();
let mut compressor = FrameCompressor::new(CompressionLevel::Default);
compressor.set_parameters(¶ms);
compressor.set_source(data.as_slice());
compressor.set_drain(&mut compressed);
compressor.compress();
assert_eq!(decode(&compressed), data);
}
#[test]
fn window_log_override_round_trips() {
let data = long_range_repetitive(256 * 1024);
let params = CompressionParameters::builder(CompressionLevel::Level(9))
.window_log(17)
.build()
.unwrap();
let compressed = compress_with_parameters(&data, ¶ms);
assert_eq!(decode(&compressed), data);
}
#[cfg(feature = "hash")]
#[test]
fn ldm_large_window_multi_segment_round_trips() {
let data = long_range_repetitive(512 * 1024);
for window_log in [24, 25, 26] {
let params = CompressionParameters::builder(CompressionLevel::Level(19))
.window_log(window_log)
.enable_long_distance_matching(true)
.build()
.unwrap();
let compressed = compress_with_parameters(&data, ¶ms);
assert_eq!(
decode(&compressed),
data,
"LDM round-trip failed at window_log {window_log}",
);
}
}
#[test]
fn streaming_decode_cycles_small_window_round_trips() {
let data = long_range_repetitive(1024 * 1024);
let params = CompressionParameters::builder(CompressionLevel::Level(1))
.window_log(17)
.build()
.unwrap();
let compressed = compress_with_parameters(&data, ¶ms);
assert_eq!(
decode(&compressed),
data,
"streaming decode over a cycling window must round-trip exactly",
);
}
#[test]
fn set_compression_level_clears_parameter_overrides() {
let data = long_range_repetitive(64 * 1024);
let mut compressor: FrameCompressor = FrameCompressor::new(CompressionLevel::Default);
let params = CompressionParameters::builder(CompressionLevel::Level(19))
.strategy(Strategy::Greedy)
.build()
.unwrap();
compressor.set_parameters(¶ms);
let _first = compressor.compress_independent_frame(&data);
compressor.set_compression_level(CompressionLevel::Level(19));
let reverted = compressor.compress_independent_frame(&data);
let expected = compress_slice_to_vec(&data, CompressionLevel::Level(19));
assert_eq!(
reverted, expected,
"set_compression_level did not clear sticky parameter overrides",
);
assert_eq!(decode(&reverted), data);
}