#archive #zip #mmap #fuse #pack

bin+lib bale

A mmap-first, fixed-stride zip-like pack format

1 unstable release

Uses new Rust 2024

0.1.0 Jan 15, 2026

#59 in #pack

Apache-2.0

380KB
7K SLoC

Contains (Zip file, 40KB) tests/fixtures/valid/multi_file.bale, (Zip file, 17KB) tests/fixtures/valid/align_16k.bale, (Zip file, 5KB) tests/fixtures/invalid/bad_crc.bale, (Zip file, 14KB) duplicate_paths.bale, (Zip file, 14KB) tests/fixtures/invalid/unsorted_cd.bale, (Zip file, 9KB) tests/fixtures/valid/orphaned_data.bale and 2 more.

bale

Warning: This project is a work-in-progress. It has known bugs, incomplete features, and the API will change. Use at your own risk.

A Rust library and CLI for working with bale archives - a mmap-first, zero-copy zip-compatible archive format with fixed-stride entries for efficient random access.

Features

  • Zip-compatible: Archives can be read by standard zip tools (unzip, zipinfo, etc.)
  • Memory-mapped: Designed for efficient mmap-based access
  • Zero-copy: Fixed-stride entries enable direct access without parsing
  • 4K aligned: File data aligned to 4096 bytes for optimal I/O

Installation

Requires Rust 1.89.0 or later.

cargo install bale

Or add to your Cargo.toml:

[dependencies]
bale = "0.1"

Usage

CLI

Create an empty bale archive:

bale touch archive.bale

Add files to an archive:

bale add archive.bale file1.txt file2.txt

# Add with a path prefix
bale add archive.bale --prefix src/ *.rs

List entries in an archive:

bale list archive.bale

Extract entries from an archive:

# Extract all entries to current directory
bale extract archive.bale

# Extract to a specific directory
bale extract archive.bale -o output/

# Extract specific entries
bale extract archive.bale file1.txt file2.txt

Delete entries from an archive:

bale delete archive.bale file1.txt file2.txt

Compact an archive to reclaim space from deleted entries:

bale compact archive.bale

Mount an archive as a FUSE filesystem:

# Mount at a specific directory
bale mount archive.bale /mnt/archive

# Interactive shell at mount point
bale mount archive.bale --shell

# Run a command against mounted archive
bale mount archive.bale --shell 'ls -la'

# Use python3 via shell
bale mount archive.bale --shell "python3 -c 'import os; print(os.listdir(\".\"))'"

Library

use bale::{ArchiveWriter, ArchiveReader};

// Create a new archive and add an entry
let mut writer = ArchiveWriter::create("archive.bale")?;
writer.add_entry("hello.txt", b"Hello, World!", 0o644)?;
writer.sync()?;

// Read from an archive
let reader = ArchiveReader::open("archive.bale")?;
if let Some(entry) = reader.find_entry("hello.txt") {
    let data = reader.read_data(entry)?;
    println!("{}", String::from_utf8_lossy(data));
}

Format

Bale extends the zip format with constraints that enable efficient random access:

Property Value
Alignment 4096 bytes
Max path length 256 bytes
Byte order Little-endian
EOCD Standard 22-byte zip format

Development

# Build
cargo build

# Test
cargo test

# Lint
cargo clippy

# Format
cargo fmt

Status

Version 0.1.0. The format is stable but the API may change in future releases.

Licensed under the Apache License, Version 2.0. See LICENSE.txt for details.

Copyright 2025 Adam Mill

Dependencies

~5–20MB
~258K SLoC