mod fixture;
use std::alloc::{GlobalAlloc, Layout, System};
use std::io::{Cursor, Read};
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
static ALLOC_COUNT: AtomicUsize = AtomicUsize::new(0);
static ALLOC_BYTES: AtomicUsize = AtomicUsize::new(0);
static CURRENT_BYTES: AtomicUsize = AtomicUsize::new(0);
static PEAK_BYTES: AtomicUsize = AtomicUsize::new(0);
struct CountingAlloc;
unsafe impl GlobalAlloc for CountingAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = unsafe { System.alloc(layout) };
if !ptr.is_null() {
ALLOC_COUNT.fetch_add(1, Relaxed);
ALLOC_BYTES.fetch_add(layout.size(), Relaxed);
let cur = CURRENT_BYTES.fetch_add(layout.size(), Relaxed) + layout.size();
PEAK_BYTES.fetch_max(cur, Relaxed);
}
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
CURRENT_BYTES.fetch_sub(layout.size(), Relaxed);
unsafe { System.dealloc(ptr, layout) }
}
}
#[global_allocator]
static A: CountingAlloc = CountingAlloc;
fn reset() {
ALLOC_COUNT.store(0, Relaxed);
ALLOC_BYTES.store(0, Relaxed);
CURRENT_BYTES.store(0, Relaxed);
PEAK_BYTES.store(0, Relaxed);
}
struct Stats {
alloc_count: usize,
alloc_bytes: usize,
peak_bytes: usize,
}
fn snapshot() -> Stats {
Stats {
alloc_count: ALLOC_COUNT.load(Relaxed),
alloc_bytes: ALLOC_BYTES.load(Relaxed),
peak_bytes: PEAK_BYTES.load(Relaxed),
}
}
fn fmt_bytes(n: usize) -> String {
if n >= 1024 * 1024 {
format!("{:.1} MB", n as f64 / (1024.0 * 1024.0))
} else if n >= 1024 {
format!("{:.1} KB", n as f64 / 1024.0)
} else {
format!("{n} B")
}
}
fn main() {
let zip_bytes = fixture::generate();
let expected_size = {
let mut archive = zip::ZipArchive::new(Cursor::new(&zip_bytes)).unwrap();
archive
.by_name(fixture::TARGET_FILE)
.unwrap()
.size() as usize
};
let file_count = {
let archive = zip::ZipArchive::new(Cursor::new(&zip_bytes)).unwrap();
archive.len()
};
println!(
"Archive: {}, {file_count} files",
fmt_bytes(zip_bytes.len())
);
println!(
"Target: {} ({} uncompressed)\n",
fixture::TARGET_FILE,
fmt_bytes(expected_size)
);
println!(
"{:<30} {:>12} {:>14} {:>14}",
"Operation", "Alloc count", "Alloc bytes", "Peak heap"
);
println!("{:-<72}", "");
reset();
{
let archive = tinyzip::Archive::open(zip_bytes.as_slice()).unwrap();
archive
.find_file(fixture::TARGET_FILE.as_bytes())
.unwrap();
}
let s = snapshot();
println!(
"{:<30} {:>12} {:>14} {:>14}",
"tinyzip find_file",
s.alloc_count,
fmt_bytes(s.alloc_bytes),
fmt_bytes(s.peak_bytes)
);
reset();
{
let archive = zip::ZipArchive::new(Cursor::new(&zip_bytes)).unwrap();
archive.index_for_name(fixture::TARGET_FILE).unwrap();
}
let s = snapshot();
println!(
"{:<30} {:>12} {:>14} {:>14}",
"zip find_file",
s.alloc_count,
fmt_bytes(s.alloc_bytes),
fmt_bytes(s.peak_bytes)
);
println!();
reset();
{
let archive = tinyzip::Archive::open(zip_bytes.as_slice()).unwrap();
let entry = archive
.find_file(fixture::TARGET_FILE.as_bytes())
.unwrap();
let mut decompressed = [0u8; 128 * 1024];
let mut chunks = entry.read_chunks::<4096>().unwrap();
let mut state =
miniz_oxide::inflate::stream::InflateState::new(miniz_oxide::DataFormat::Raw);
let mut out_pos = 0;
while let Some(chunk) = chunks.next() {
let result = miniz_oxide::inflate::stream::inflate(
&mut state,
chunk.unwrap(),
&mut decompressed[out_pos..],
miniz_oxide::MZFlush::None,
);
out_pos += result.bytes_written;
}
}
let s = snapshot();
println!(
"{:<30} {:>12} {:>14} {:>14}",
"tinyzip extract",
s.alloc_count,
fmt_bytes(s.alloc_bytes),
fmt_bytes(s.peak_bytes)
);
reset();
{
let mut archive = zip::ZipArchive::new(Cursor::new(&zip_bytes)).unwrap();
let mut entry = archive.by_name(fixture::TARGET_FILE).unwrap();
let mut buf = Vec::with_capacity(expected_size);
entry.read_to_end(&mut buf).unwrap();
}
let s = snapshot();
println!(
"{:<30} {:>12} {:>14} {:>14}",
"zip extract",
s.alloc_count,
fmt_bytes(s.alloc_bytes),
fmt_bytes(s.peak_bytes)
);
}