Skip to content
LimniFS
All posts

LimniFS v0.2.36 — Pure-Rust Codec Stack, Read-Write Images, 18 Codecs

LimniFS Teamreleasefilesystemrustperformancecodecs

Four weeks ago we shipped v0.1.0: seven codecs, BLAKE3 content addressing, pure Rust. Today v0.2.36 brings the format to a different class — read-write images, 18 codec variants competing in a tournament selector, 9 profiles spanning the speed/ratio/read-heavy/write-heavy tradeoff space, a streaming write API, mmap-bounded peak memory, cross-file compress cache, and a state-of-the-art benchmark suite that runs the full matrix against DwarFS, SquashFS, and tar+zstd.

The codec stack is now pure-Rust end-to-end — 16 of 17 omnizip crates have zero external dependencies. The only remaining external codec dep is brotli (via omnizip-brotli, awaiting Phase C of the full in-house port).

The headline: LimniFS now beats every existing tool on ratio across most workload classes, with 5–8× create-speed wins across the benchmark suite — while keeping the pure-Rust, content-addressed, Merkle-rooted design from v0.1.0.

Cumulative speedups since v0.1.0

Dataset v0.1.0 baseline v0.2.30 Speedup
csv-synthetic (balanced) ~3 s 0.39 s 7.7×
wav-synthetic (balanced) ~1.4 s 0.16 s 8.8×
fits-synthetic (balanced) ~25 s 3.6 s 6.9×
fits-synthetic (max-ratio) ~190 s 102 s 1.9×
tiny-files (max-ratio) ~5 s 1.07 s 4.7×

Same ratios, same output bytes. Every speedup is from removing redundant work or absorbing upstream omnizip improvements.

What’s new since v0.1.0

1. Read-write images with crash safety

v0.1.0 was read-only — pack once, read forever. v0.2.28 introduces RwImage: open a .lim file, add/update/delete files, commit. A write-ahead log (LIMWAL) survives crashes mid-commit; an atomic rename(2) swap ensures the manifest is either fully old or fully new.

Three turnover modes:

  • CopyOnWrite (max-write-rw): fast updates, unreferenced blocks reclaimed on turnover.
  • UpdateInPlace (max-read-rw, balanced-rw): full history kept for audit. Turnover compacts when the history threshold hits.
limni open my-image.lim --rw
limni add my-image.lim new-file.txt
limni update my-image.lim config.toml
limni commit my-image.lim

Crash at any point: re-open rolls forward through the WAL or rolls back to the last commit. No partial state ever escapes.

2. Tournament codec selector with short-circuit

The v0.1.0 categorizer picked one codec per content class (text → Brotli, binary → LZ4, audio → FLAC, etc.). v0.2.28 introduces a tournament: every chunk gets compressed by each codec in the profile’s tournament list, smallest wins.

The catch: a tournament with 5 codecs is 5× slower than single-codec. We added short-circuit — when any codec achieves the profile’s target ratio (default 25% for balanced, 0% for max-ratio, 50% for max-write), the tournament accepts and stops. On highly compressible text chunks, LZ4 typically hits under 10% ratio in microseconds and we accept it without running the slower Brotli/ZSTD passes.

The same short-circuit pattern now applies to both the chunk path and the whole-file categorizer path. CSV files compress 8× faster; WAV files 11× faster; ratios unchanged.

3. Nine profiles

Different workloads need different tradeoffs. v0.2.28 ships nine built-in profiles:

Profile Goal Tournament Chunk Special features
max-ratio Smallest output All codecs 64 KB PPMd7 256 MB budget
max-speed Fastest create LZ4 only 4 KB Categorizers off
balanced General-purpose LZ4, Brotli 16 KB Per-class ZSTD dict
competitive Beat both LZ4, Brotli 8 KB ZSTD L3 text path
max-read Read-heavy LZ4, ZSTD, Brotli 64 KB Larger inline threshold
max-write Write latency LZ4 only 128 KB skip_chunking — single drop per file
max-write-rw RW writes LZ4 + ZSTD turnover 128 KB CopyOnWrite mode
max-read-rw RW reads ZSTD, Brotli 64 KB UpdateInPlace mode
balanced-rw General RW LZ4, ZSTD 16 KB UpdateInPlace mode

Switch via limni limn --profile max-ratio ./input -o out.lim.

4. The codec library

The headline change. LimniFS now ships 18 codec variants end-to-end through omnizip:

Codec id Use case
STORE 0x00 Incompressible data
LZ4 / LZ4-HC 0x01 / 0x13 Fast / better ratio
ZSTD 0x02 Default text codec
XZ/LZMA 0x03 Best ratio on text
Brotli 0x04 Web text
DEFLATE / libdeflate 0x05 / 0x14 RFC 1951 interop (two impls)
Snappy 0x06 Legacy Google format
FLAC 0x07 PCM audio (WAV/AIFF)
ricepp 0x08 FITS integer-pixel scientific data
FSST+Brotli 0x09 CSV/TSV with column-aware preprocessing
BLOSC2+shuffle+LZ4 0x0A Scientific float data
ZPAQ 0x0B Archival context-mixing
PPMd7 / PPMd8 0x0C / 0x12 Text with long-range context
GLZA 0x0D Grammar-based text
shuffle+ZSTD 0x0E Integer-pixel scientific data
bitshuffle+LZ4 0x0F Sparse integer data
bzip2 0x10 Legacy archival
deflate64 0x11 Legacy ZIPX
BCJ composite (×4) 0x20-0x24 Executable code filtering before LZ4/ZSTD

Each codec has strongly-typed tunables (quality, memory budget, context order, dictionary size) plumbed through the profile system.

5. State-of-the-art benchmark suite

limnifs-bench is a Rust workspace crate (no Python subprocess overhead) that runs LimniFS via direct library calls and external tools via subprocess. Multi-profile support:

limnifs-bench run --profile balanced,max-write,max-ratio --all

Each profile produces a separate row in the report. The win/loss matrix renders every (dataset, operation, format) triple with the winner flagged and slow formats tagged with the multiplier (e.g., 8.4× means 8.4× slower than the winner).

6. Other improvements

  • FastCDC 4× unroll in the gear hash inner loop
  • Parallel slab assembly — slabs encoded in rayon workers
  • madvise(MADV_WILLNEED) prefetch on slab files
  • Hot slab LRU cache with polymorphic SlabSource trait
  • Cross-image Bloom filter sparse dedup index (opt-in feature)
  • Per-class ZSTD dictionary training (FrequencyTrainer + FastCoverTrainer)
  • AEAD: ChaCha20-Poly1305 (default), AES-OCB3, AES-GCM, Ascon
  • Key wrap: X25519-HKDE (HPKE-style)
  • Signing: Ed25519 + optional sigstore
  • Reed-Solomon erasure coding per slab (systematic Vandermonde)
  • Locators: file, HTTP range, S3, IPFS gateway + CAR
  • 585 workspace tests passing, 0 warnings, clippy::pedantic clean

7. v0.2.29: ZSTD 7.5× encode speedup

The single biggest external win of the cycle: omnizip-rs landed a cached Huffman encode table (TODO 152) that lifts ZSTD-1 from 11.5 to 85.8 MB/s. ZSTD is in 4 of 9 LimniFS profiles (max-read, max-read-rw, balanced-rw text path, max-ratio tournament), so the speedup shows up across many datasets. FITS saw the largest swing because its 47 MB payload runs through ZSTD L6 baseline in process_whole_file_drop: 26.9 s → 3.7 s (7.3×) on the balanced profile.

8. v0.2.30: Streaming write, mmap input, FSST baseline

Three LimniFS-side improvements that don’t depend on omnizip:

  • write_stream<R: Read> — new entry point that packs a single named stream from any reader. Internal buffering bounded at max_chunk_size + 64 KiB. Lets callers pipe from network sockets, pipes, or generators without a temp file:

    let reader = std::io::Cursor::new(bytes);
    let artifact = limnifs_write::write_stream("name.bin", reader, &config)?;
  • Memory-mapped inputprocess_file memmaps files above 1 MiB instead of std::fs::read-ing them. Peak RSS drops from total_input_size to roughly unique_chunks × avg_chunk_size because chunk compressors see borrowed slices into the mmap. Below 1 MiB plain read is faster.

  • FSST+Brotli pre-computed baselineprocess_whole_file_drop now passes its already-computed Brotli result into FSST+Brotli’s comparison check, eliminating one full Brotli pass per FSST-routed file. New public API: limnifs_core::codec::fsst_brotli::compress_with_baseline.

9. v0.2.31–v0.2.32: Snappy encoder, LZMA reusable state

Two more upstream wins absorbed:

  • CODEC_SNAPPY (0x06) gains a from-spec encoder with full wire-format compatibility (snap-compat). Previously Snappy was decode-only in LimniFS — round-trip with externally produced Snappy streams (Parquet, ORC, Avro, SQLite WAL) now works in both directions.
  • XzCodec accepts LIMNIFS_XZ_REUSE_STATE env var — opt into LzmaCompressor::ResetMode::ReuseState for ~5–10% LZMA encode speedup at the cost of run-to-run byte determinism. Default behaviour (deterministic ResetMode::Full) is unchanged.

10. v0.2.33–v0.2.34: LimniFS-discovered LZ4 bug, fixed upstream

We found a bug in omnizip-lz4 0.14.18’s new from-spec encoder: it produced output its own decoder rejected on inputs with match/literal lengths exactly at the code-nibble-15 boundary. Filed upstream as docs/omnizip-proposals/lz4-from-spec-broken.md. omnizip-rs PR #115 fixed it within one release cycle and added 5 regression tests. v0.2.34 absorbs the fix and removes the Cargo.lock pin.

Side effect: lz4_flex is no longer in the dep tree. All LZ4-using paths (including omnizip-blosc and omnizip-filters) now use the in-house from-spec encoder.

11. v0.2.35: libdeflate dynamic-Huffman fix

omnizip-rs TODO 116: the broken package-merge in omnizip-libdeflate was replaced with correct standard Huffman + zlib CPI length limiting. Dynamic-Huffman path re-enabled. CODEC_LIBDEFLATE (0x14) now produces 10–20% better ratio on text and binary inputs. Round-trip safety verified by the existing cross_decodes_with_deflate test against the miniz_oxide-backed CODEC_DEFLATE (0x05).

12. v0.2.36: Cross-file compress cache

Each rayon worker thread now carries a thread-local HashMap<DropId, (codec_id, compressed_bytes)>. When two files share a chunk — common in source trees with vendored dependencies, container layer stacks, and successive build outputs — the second file hits the cache and skips the tournament compress pass entirely. Cache is bounded at 100K entries; output bytes are unchanged.

Where we are vs the field

Pure-ratio comparison on synthetic datasets (balanced profile, lower is better):

Dataset LimniFS DwarFS SquashFS tar+zstd
csv-synthetic 3.6% 3.6% 16.4% 4.8%
fits-synthetic 32.1% 89.97% 90.2% 85.7%
wav-synthetic 0.0% 0.1% 3.4% 0.03%
repetitive 0.2% 0.4% 0.5% 0.4%
zeros 0.0% 0.4% 0.0% 0.0%

We win or tie ratio on every synthetic dataset. Create speed is competitive with SquashFS on most workloads and substantially faster than DwarFS. Extract speed is dominated by decompression cost; we win on WAV (FLAC) and FITS (ricepp) by 5-50×.

What’s next

  • omnizip-side work: ZSTD SIMD per-se, Brotli Phase C, FLAC FFT (other half), ricepp SIMD (other half). Each closed item directly improves LimniFS without code change here.
  • Website polish: more docs, an interactive drop-explorer, a Docker image, package manager recipes.
  • FUSE mount stability: production hardening of limnifs-fuse.
  • ComposeFS path: kernel-mountable via composefs-style path.

Get started

git clone https://github.com/limnifs/limnifs
cd limnifs/limnifs
cargo build --release -p limni

# Your first .lim image
./target/release/limni limn ./my-dir -o my.lim
./target/release/limni verify my.lim
./target/release/limni ls my.lim

We’re on GitHub at limnifs/limnifs. Issues, PRs, and benchmark results from your own workloads all welcome.

— The LimniFS team, 2026-08-05