#![cfg(feature = "lzma2")]
use std::io::Cursor;
use zesven::ArchivePath;
use zesven::codec::CodecMethod;
use zesven::codec::lzma::{decode_lzma2_dict_size, dict_size_covering, preset_dict_size};
use zesven::format::parser::read_archive_header;
use zesven::write::{WriteOptions, Writer};
const PAYLOAD: usize = 2 * 1024 * 1024;
fn declared_dictionary(level: u32, method: CodecMethod, data: &[u8]) -> u32 {
let options = WriteOptions::new().level(level).unwrap().method(method);
let mut writer = Writer::create(Cursor::new(Vec::new()))
.unwrap()
.options(options);
writer
.add_bytes(ArchivePath::new("entry.bin").unwrap(), data)
.unwrap();
let (_result, cursor) = writer.finish_into_inner().unwrap();
let mut archive = Cursor::new(cursor.into_inner());
let (_start, header) = read_archive_header(&mut archive, None).unwrap();
let folders = header.folders();
assert_eq!(folders.len(), 1, "expected exactly one folder");
let properties = folders[0].coders[0]
.properties
.as_ref()
.expect("the codec declares a dictionary");
match method {
CodecMethod::Lzma2 => decode_lzma2_dict_size(properties[0]).unwrap(),
CodecMethod::Lzma => u32::from_le_bytes(properties[1..5].try_into().unwrap()),
other => panic!("unexpected method {other:?}"),
}
}
fn payload(len: usize) -> Vec<u8> {
let mut data = Vec::with_capacity(len);
let mut state = 0x1234_5678_9abc_def0u64;
while data.len() < len {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
data.extend_from_slice(b"the quick brown fox jumps over the lazy dog ");
data.extend_from_slice(&state.to_le_bytes());
}
data.truncate(len);
data
}
#[test]
fn test_declared_dictionary_is_the_level_capped_by_the_data() {
let data = payload(PAYLOAD);
for level in 0..=9u32 {
let expected = preset_dict_size(level).min(dict_size_covering(data.len() as u64));
assert_eq!(
declared_dictionary(level, CodecMethod::Lzma2, &data),
expected,
"level {level} declares a dictionary it did not compress with",
);
}
}
#[cfg(feature = "lzma")]
#[test]
fn test_lzma1_declares_the_dictionary_it_used() {
let data = payload(PAYLOAD);
for level in 0..=9u32 {
let expected = preset_dict_size(level).min(dict_size_covering(data.len() as u64));
assert_eq!(
declared_dictionary(level, CodecMethod::Lzma, &data),
expected,
"level {level} declares a dictionary it did not compress with",
);
}
}
#[test]
fn test_level_changes_the_encoded_output() {
let data = payload(PAYLOAD);
let sizes: Vec<usize> = [1u32, 9]
.iter()
.map(|&level| {
let options = WriteOptions::new().level(level).unwrap();
let mut writer = Writer::create(Cursor::new(Vec::new()))
.unwrap()
.options(options);
writer
.add_bytes(ArchivePath::new("entry.bin").unwrap(), &data)
.unwrap();
let (_result, cursor) = writer.finish_into_inner().unwrap();
cursor.into_inner().len()
})
.collect();
assert!(
sizes[1] < sizes[0],
"level 9 produced {} bytes and level 1 produced {}; the level is not reaching the encoder",
sizes[1],
sizes[0],
);
}
#[cfg(feature = "parallel")]
#[test]
fn test_chunked_stream_round_trips() {
let data = payload(12 * 1024 * 1024);
let options = WriteOptions::new().level(1).unwrap();
let mut writer = Writer::create(Cursor::new(Vec::new()))
.unwrap()
.options(options);
writer
.add_bytes(ArchivePath::new("big.bin").unwrap(), &data)
.unwrap();
let (_result, cursor) = writer.finish_into_inner().unwrap();
let mut archive = zesven::read::Archive::open(Cursor::new(cursor.into_inner())).unwrap();
assert_eq!(archive.extract_to_vec("big.bin").unwrap(), data);
}
#[cfg(feature = "parallel")]
#[test]
fn test_chunked_output_is_deterministic() {
let data = payload(12 * 1024 * 1024);
let archive_bytes = || {
let options = WriteOptions::new().level(1).unwrap();
let mut writer = Writer::create(Cursor::new(Vec::new()))
.unwrap()
.options(options);
writer
.add_bytes(ArchivePath::new("big.bin").unwrap(), &data)
.unwrap();
writer.finish_into_inner().unwrap().1.into_inner()
};
assert_eq!(
archive_bytes(),
archive_bytes(),
"the same input produced two different archives",
);
}
#[test]
fn test_every_level_round_trips() {
let data = payload(256 * 1024);
for level in 0..=9u32 {
let options = WriteOptions::new().level(level).unwrap();
let mut writer = Writer::create(Cursor::new(Vec::new()))
.unwrap()
.options(options);
writer
.add_bytes(ArchivePath::new("entry.bin").unwrap(), &data)
.unwrap();
let (_result, cursor) = writer.finish_into_inner().unwrap();
let mut archive = zesven::read::Archive::open(Cursor::new(cursor.into_inner())).unwrap();
let extracted = archive.extract_to_vec("entry.bin").unwrap();
assert_eq!(extracted, data, "level {level} did not round-trip");
}
}