use rawzip::{ZipArchive, ZipArchiveWriter};
use rstest::rstest;
use std::io::{Cursor, Write};
fn find_zip_data_start_offset_slice<T: AsRef<[u8]>>(archive: &rawzip::ZipSliceArchive<T>) -> u64 {
archive
.entries()
.map(|x| x.unwrap().local_header_offset())
.min()
.unwrap_or(archive.directory_offset())
}
fn find_zip_data_start_offset_reader<R: rawzip::ReaderAt>(archive: &rawzip::ZipArchive<R>) -> u64 {
let mut min_offset = archive.directory_offset();
let mut buf = vec![0u8; rawzip::RECOMMENDED_BUFFER_SIZE];
let mut entries = archive.entries(&mut buf);
while let Some(entry) = entries.next_entry().unwrap() {
min_offset = min_offset.min(entry.local_header_offset());
}
min_offset
}
#[test]
fn test_concatenated_zip_files() {
let data = {
let mut data = Vec::new();
data.extend_from_slice(b"PREFIX_FOR_FIRST_ZIP\n");
{
let mut archive = ZipArchiveWriter::new(&mut data);
let (mut entry, config) = archive.new_file("first.txt").start().unwrap();
let mut writer = config.wrap(&mut entry);
writer.write_all(b"First ZIP content").unwrap();
let (_, descriptor) = writer.finish().unwrap();
entry.finish(descriptor).unwrap();
archive.finish().unwrap();
}
data.extend_from_slice(b"PREFIX_FOR_SECOND_ZIP\n");
{
let mut archive = ZipArchiveWriter::new(&mut data);
let (mut entry, config) = archive.new_file("second.txt").start().unwrap();
let mut writer = config.wrap(&mut entry);
writer.write_all(b"Second ZIP content").unwrap();
let (_, descriptor) = writer.finish().unwrap();
entry.finish(descriptor).unwrap();
archive.finish().unwrap();
}
data
};
let second_archive = ZipArchive::from_slice(&data).unwrap();
let entries: Vec<_> = second_archive.entries().collect();
assert_eq!(entries.len(), 1);
let entry = entries[0].as_ref().unwrap();
assert_eq!(entry.file_path().as_ref(), b"second.txt");
let second_zip_start = find_zip_data_start_offset_slice(&second_archive);
assert_ne!(second_zip_start, 0);
let locator = rawzip::ZipLocator::new();
let mut buffer = vec![0u8; rawzip::RECOMMENDED_BUFFER_SIZE];
let reader = std::io::Cursor::new(&data);
let first_archive = locator
.locate_in_reader(reader, &mut buffer, second_zip_start)
.unwrap();
let first_zip_start = find_zip_data_start_offset_reader(&first_archive);
let prefix = &data[..first_zip_start as usize];
assert_eq!(prefix, b"PREFIX_FOR_FIRST_ZIP\n");
let mut entries_iter = first_archive.entries(&mut buffer);
let entry = entries_iter.next_entry().unwrap().unwrap();
assert_eq!(entry.file_path().as_ref(), b"first.txt");
let first_archive_end = first_archive.end_offset();
let second_prefix = &data[first_archive_end as usize..second_zip_start as usize];
assert_eq!(second_prefix, b"PREFIX_FOR_SECOND_ZIP\n");
}
#[test]
fn test_zip_with_secret_prelude() {
let cases = [("assets/test.zip", 2)];
for (asset, entries_count) in cases {
let data = std::fs::read(asset).unwrap();
let data = [&[0u8; 1000], data.as_slice()].concat();
let archive = rawzip::ZipArchive::from_slice(&data).unwrap();
let zip_start_offset = find_zip_data_start_offset_slice(&archive);
let extracted_prefix = &data[..zip_start_offset as usize];
assert_eq!(extracted_prefix, &[0u8; 1000]);
let entries: Vec<_> = archive.entries().collect();
assert_eq!(entries.len(), entries_count);
}
let mut buf = vec![0u8; rawzip::RECOMMENDED_BUFFER_SIZE];
for (asset, entries_count) in cases {
let data = std::fs::read(asset).unwrap();
let data = [&[0u8; 1000], data.as_slice()].concat();
let locator = rawzip::ZipLocator::new();
let archive = locator
.locate_in_reader(&data, &mut buf, data.len() as u64)
.unwrap();
let zip_start_offset = find_zip_data_start_offset_reader(&archive);
let extracted_prefix = &data[..zip_start_offset as usize];
assert_eq!(extracted_prefix, &[0u8; 1000]);
let mut count = 0;
let mut entries = archive.entries(&mut buf);
while entries.next_entry().unwrap().is_some() {
count += 1;
}
assert_eq!(count, entries_count);
}
}
#[rstest]
#[case(0)]
#[case(100)]
#[case(65536)]
fn test_zip_declared_prelude(#[case] entry_count: usize) {
let mut output = Cursor::new(Vec::new());
output.write_all(&[0u8; 1000]).unwrap();
let mut archive = rawzip::ZipArchiveWriter::builder()
.with_offset(output.position())
.with_capacity(entry_count)
.build(output);
for i in 0..entry_count {
let filename = format!("file_{:05}.txt", i);
let (mut entry, config) = archive.new_file(&filename).start().unwrap();
let mut writer = config.wrap(&mut entry);
writer.write_all(b"x").unwrap();
let (_, descriptor_output) = writer.finish().unwrap();
entry.finish(descriptor_output).unwrap();
}
let writer = archive.finish().unwrap();
let data = writer.into_inner();
let archive = rawzip::ZipArchive::from_slice(&data).unwrap();
let zip_start_offset = find_zip_data_start_offset_slice(&archive);
assert_eq!(zip_start_offset, 1000);
assert_eq!(archive.entries().count(), entry_count);
}