#![cfg(feature = "lzma2")]
use std::io::Cursor;
use zesven::codec::CodecMethod;
use zesven::read::Archive;
use zesven::write::{WriteOptions, Writer};
use zesven::{ArchivePath, WriteFilter};
const LARGE: usize = 68 * 1024 * 1024;
fn fast() -> WriteOptions {
WriteOptions::new().level(1).unwrap()
}
fn payload(len: usize) -> Vec<u8> {
let mut data = Vec::with_capacity(len);
let mut state = 0x243F_6A88_85A3_08D3u64;
while data.len() < len {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
data.extend_from_slice(b"a large entry should not have to fit in memory to be archived ");
data.extend_from_slice(&state.to_le_bytes());
}
data.truncate(len);
data
}
fn archive_one(options: WriteOptions, data: &[u8]) -> Vec<u8> {
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()
}
#[test]
fn test_large_entry_round_trips() {
let data = payload(LARGE);
let bytes = archive_one(fast(), &data);
let mut opened = Archive::open(Cursor::new(bytes)).unwrap();
assert_eq!(opened.extract_to_vec("big.bin").unwrap(), data);
}
#[test]
fn test_a_streamed_entry_reports_its_sizes() {
let data = payload(LARGE);
let streamed = archive_one(fast(), &data);
let mut opened = Archive::open(Cursor::new(streamed)).unwrap();
let entries = opened.entries().to_vec();
assert_eq!(entries.len(), 1);
assert_eq!(entries[0].size, data.len() as u64);
assert_eq!(opened.extract_to_vec("big.bin").unwrap(), data);
}
#[test]
fn test_large_stored_entry_round_trips() {
let data = payload(LARGE);
let bytes = archive_one(fast().method(CodecMethod::Copy), &data);
let mut opened = Archive::open(Cursor::new(bytes)).unwrap();
assert_eq!(opened.extract_to_vec("big.bin").unwrap(), data);
}
#[cfg(feature = "lzma")]
#[test]
fn test_large_lzma1_entry_round_trips() {
let data = payload(LARGE);
let bytes = archive_one(fast().method(CodecMethod::Lzma), &data);
let mut opened = Archive::open(Cursor::new(bytes)).unwrap();
assert_eq!(opened.extract_to_vec("big.bin").unwrap(), data);
}
#[test]
fn test_large_entries_with_filters_fall_back_and_stay_correct() {
let data = payload(LARGE);
let filtered = archive_one(fast().filter(WriteFilter::delta(4)), &data);
let mut opened = Archive::open(Cursor::new(filtered)).unwrap();
assert_eq!(opened.extract_to_vec("big.bin").unwrap(), data);
}
#[cfg(feature = "aes")]
#[test]
fn test_large_encrypted_entry_round_trips() {
use zesven::crypto::{NoncePolicy, Password};
let data = payload(LARGE);
let options = fast()
.password("correct horse battery staple")
.nonce_policy(NoncePolicy::random_with_params(4, 8))
.encrypt_data(true);
let bytes = archive_one(options, &data);
let mut opened = Archive::open_with_password(
Cursor::new(bytes),
Password::new("correct horse battery staple"),
)
.unwrap();
assert_eq!(opened.extract_to_vec("big.bin").unwrap(), data);
}
#[test]
fn test_a_large_entry_keeps_the_order_of_small_ones() {
let small = payload(64 * 1024);
let large = payload(LARGE);
let mut writer = Writer::create(Cursor::new(Vec::new()))
.unwrap()
.options(fast());
writer
.add_bytes(ArchivePath::new("01-small.bin").unwrap(), &small)
.unwrap();
writer
.add_bytes(ArchivePath::new("02-large.bin").unwrap(), &large)
.unwrap();
writer
.add_bytes(ArchivePath::new("03-small.bin").unwrap(), &small)
.unwrap();
let bytes = writer.finish_into_inner().unwrap().1.into_inner();
let mut opened = Archive::open(Cursor::new(bytes)).unwrap();
let paths: Vec<String> = opened
.entries()
.iter()
.map(|e| e.path.as_str().to_string())
.collect();
assert_eq!(paths, vec!["01-small.bin", "02-large.bin", "03-small.bin"]);
assert_eq!(opened.extract_to_vec("01-small.bin").unwrap(), small);
assert_eq!(opened.extract_to_vec("02-large.bin").unwrap(), large);
assert_eq!(opened.extract_to_vec("03-small.bin").unwrap(), small);
}
#[test]
fn test_an_empty_source_claiming_to_be_large_is_harmless() {
use zesven::write::EntryMeta;
let data = payload(64 * 1024);
let mut writer = Writer::create(Cursor::new(Vec::new()))
.unwrap()
.options(fast());
writer
.add_stream(
ArchivePath::new("empty.bin").unwrap(),
&mut Cursor::new(Vec::new()),
EntryMeta::file(80 * 1024 * 1024),
)
.unwrap();
writer
.add_bytes(ArchivePath::new("after.bin").unwrap(), &data)
.unwrap();
let bytes = writer.finish_into_inner().unwrap().1.into_inner();
let mut opened = Archive::open(Cursor::new(bytes)).unwrap();
let paths: Vec<String> = opened
.entries()
.iter()
.map(|e| e.path.as_str().to_string())
.collect();
assert_eq!(paths, vec!["empty.bin", "after.bin"]);
assert!(opened.extract_to_vec("empty.bin").unwrap().is_empty());
assert_eq!(opened.extract_to_vec("after.bin").unwrap(), data);
}
fn incompressible(len: usize) -> Vec<u8> {
let mut data = Vec::with_capacity(len);
let mut state = 0x2545_F491_4F6C_DD1Du64;
while data.len() < len {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
data.extend_from_slice(&state.to_le_bytes());
}
data.truncate(len);
data
}
#[test]
fn test_large_entry_across_volumes() {
use zesven::VolumeConfig;
let dir = tempfile::TempDir::new().unwrap();
let data = incompressible(LARGE);
let config = VolumeConfig::new(dir.path().join("big.7z"), 17 * 1024 * 1024);
let mut writer = Writer::create_multivolume(config).unwrap().options(fast());
writer
.add_bytes(ArchivePath::new("big.bin").unwrap(), &data)
.unwrap();
let result = writer.finish().unwrap();
assert!(
result.volume_count > 1,
"the entry fitted in one volume, so this proves nothing: {} volume(s)",
result.volume_count,
);
assert!(
result
.volume_sizes
.iter()
.take(result.volume_count as usize - 1)
.all(|&size| size == 17 * 1024 * 1024),
"every volume but the last must be full: {:?}",
result.volume_sizes,
);
let mut opened = Archive::open_path(dir.path().join("big.7z.001")).unwrap();
assert_eq!(opened.extract_to_vec("big.bin").unwrap(), data);
}