Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,6 @@ benches/fixtures/*.tar
benches/fixtures/*.tar.*
benches/fixtures/*.zip
benches/fixtures/*.7z

# Profiling
dhat-heap.json
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@ dhat = "0.3"
flate2 = "1.1"
indicatif = "0.18"
libc = "0.2"
lru = "0.16"
napi = "3.8"
napi-build = "2.3"
napi-derive = "3.5"
predicates = "3.1"
proptest = "1.9"
proptest = "1.10"
pyo3 = "0.28"
rustc-hash = "2.1"
serde = "1.0"
Expand Down
60 changes: 60 additions & 0 deletions crates/exarch-core/examples/dhat_creation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//! Heap profiling for archive creation operations.
//!
//! Uses dhat to track all heap allocations during archive creation.
//! After running, open the generated `dhat-heap.json` in the dhat viewer:
//! <https://nnethercote.github.io/dh_view/dh_view.html>
//!
//! Usage:
//! ```sh
//! cargo run --example dhat_creation --release
//! cargo run --example dhat_creation --release -- tar
//! cargo run --example dhat_creation --release -- zip
//! ```

#![allow(unsafe_code, clippy::unwrap_used)]

#[global_allocator]
static ALLOC: dhat::Alloc = dhat::Alloc;

use exarch_core::create_archive;
use exarch_core::creation::CreationConfig;
use std::fs;

fn create_test_directory(root: &std::path::Path, file_count: usize, file_size: usize) {
fs::create_dir_all(root).unwrap();
let content = vec![0xAB_u8; file_size];

for i in 0..file_count {
fs::write(root.join(format!("file{i:04}.txt")), &content).unwrap();
}
}

fn main() {
let format = std::env::args().nth(1).unwrap_or_else(|| "zip".to_string());

let file_count = 500;
let file_size = 1024;

let temp = tempfile::tempdir().unwrap();
let source_dir = temp.path().join("source");
create_test_directory(&source_dir, file_count, file_size);

let extension = match format.as_str() {
"tar" => "tar",
"tar.gz" | "targz" => "tar.gz",
"zip" => "zip",
other => {
eprintln!("Unknown format: {other}. Use 'tar', 'tar.gz', or 'zip'.");
std::process::exit(1);
}
};

let output = temp.path().join(format!("output.{extension}"));
let config = CreationConfig::default();

eprintln!("Profiling {extension} creation: {file_count} files x {file_size} bytes each");

let _profiler = dhat::Profiler::new_heap();

create_archive(&output, &[&source_dir], &config).unwrap();
}
102 changes: 102 additions & 0 deletions crates/exarch-core/examples/dhat_extraction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//! Heap profiling for archive extraction operations.
//!
//! Uses dhat to track all heap allocations during extraction.
//! After running, open the generated `dhat-heap.json` in the dhat viewer:
//! <https://nnethercote.github.io/dh_view/dh_view.html>
//!
//! Usage:
//! ```sh
//! cargo run --example dhat_extraction --release
//! cargo run --example dhat_extraction --release -- zip
//! cargo run --example dhat_extraction --release -- tar
//! ```

#![allow(unsafe_code, clippy::unwrap_used)]

#[global_allocator]
static ALLOC: dhat::Alloc = dhat::Alloc;

use exarch_core::SecurityConfig;
use exarch_core::formats::TarArchive;
use exarch_core::formats::ZipArchive;
use exarch_core::formats::traits::ArchiveFormat;
use std::io::Cursor;
use std::io::Write;
use zip::write::SimpleFileOptions;
use zip::write::ZipWriter;

fn create_zip_in_memory(file_count: usize, file_size: usize) -> Vec<u8> {
let buffer = Vec::new();
let mut zip = ZipWriter::new(Cursor::new(buffer));
let options = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored);
let content = vec![0xAB_u8; file_size];

for i in 0..file_count {
let filename = format!("file{i:04}.txt");
zip.start_file(&filename, options).unwrap();
zip.write_all(&content).unwrap();
}

zip.finish().unwrap().into_inner()
}

fn create_tar_in_memory(file_count: usize, file_size: usize) -> Vec<u8> {
let buffer = Vec::new();
let mut builder = tar::Builder::new(buffer);
let content = vec![0xAB_u8; file_size];

for i in 0..file_count {
let filename = format!("file{i:04}.txt");
let mut header = tar::Header::new_gnu();
header.set_size(content.len() as u64);
header.set_mode(0o644);
header.set_cksum();
builder
.append_data(&mut header, &filename, &content[..])
.unwrap();
}

builder.into_inner().unwrap()
}

fn profile_zip_extraction(file_count: usize, file_size: usize) {
let zip_data = create_zip_in_memory(file_count, file_size);
let config = SecurityConfig::default();
let temp = tempfile::tempdir().unwrap();

let _profiler = dhat::Profiler::new_heap();

let cursor = Cursor::new(zip_data);
let mut archive = ZipArchive::new(cursor).unwrap();
archive.extract(temp.path(), &config).unwrap();
}

fn profile_tar_extraction(file_count: usize, file_size: usize) {
let tar_data = create_tar_in_memory(file_count, file_size);
let config = SecurityConfig::default();
let temp = tempfile::tempdir().unwrap();

let _profiler = dhat::Profiler::new_heap();

let cursor = Cursor::new(tar_data);
let mut archive = TarArchive::new(cursor);
archive.extract(temp.path(), &config).unwrap();
}

fn main() {
let format = std::env::args().nth(1).unwrap_or_else(|| "zip".to_string());

let file_count = 500;
let file_size = 1024;

eprintln!("Profiling {format} extraction: {file_count} files x {file_size} bytes each");

match format.as_str() {
"zip" => profile_zip_extraction(file_count, file_size),
"tar" => profile_tar_extraction(file_count, file_size),
other => {
eprintln!("Unknown format: {other}. Use 'zip' or 'tar'.");
std::process::exit(1);
}
}
}