divvun/bidiff

A Rust take on bsdiff

Rust

174

95 commits

updated Feb 19, 2026

See the code

README

bidiff

A binary diff and patch library for Rust, distantly derived from the bsdiff algorithm.

Instead of suffix arrays, bidiff uses a hash-table index with:

  • File-backed mmap by default -- the kernel can page the hash table to disk under memory pressure, keeping anonymous memory usage low
  • Optional --ram mode for faster diffs when memory is plentiful
  • Parallel construction via lock-free CAS insertion
  • Parallel scanning via rayon with ring-buffer channels for streaming matches
  • Chunked patch format with independent zstd-compressed chunks for parallel patch application

Usage

# Create a patch
bidiff diff old_file new_file patch_file

# Apply a patch
bidiff patch old_file patch_file output_file

# Round-trip verification (diff then patch, verifies hash match)
bidiff cycle old_file new_file

Options

--scan-chunk-mb <N>   Scan chunk size in MB (default: 1)
--block-size <N>      Hash index block size, minimum 4 (default: 32)
--threads <N>         Max threads for parallel scanning (default: all cores)
--ram                 Keep hash table in RAM (faster, uses more memory)
--max                 Maximize compression (zstd level 22)

Benchmarks

System: AMD Ryzen Threadripper 2950X (32 cores), 60 GiB RAM, Linux 6.12.

Default settings (1 MiB chunks, file-backed hash table). Memory column shows peak anonymous RSS during diffing.

Test caseNew sizePatch sizeRatioPatch timeDiff timeMemoryDiff time (RAM)Memory (RAM)
Wine 4.18 → 4.19201 MiB249 KiB0.12%0.13s0.42s21.5 MiB0.38s149 MiB
Linux 5.3 → 5.4895 MiB6.8 MiB0.76%0.50s2.13s59.2 MiB1.81s563 MiB
Firefox 71.0b11 → b12198 MiB10.9 MiB5.49%0.14s0.76s18.6 MiB0.73s147 MiB
Chrome 78.0.3904.97 → 108145 MiB8.3 MiB5.71%0.11s0.79s16.9 MiB0.75s147 MiB

With --max (zstd level 22)

Smaller patches at the cost of much slower diff times. Patch application speed is similar.

Test caseNew sizePatch sizeRatioPatch timeDiff timeMemoryDiff time (RAM)Memory (RAM)
Wine 4.18 → 4.19201 MiB203 KiB0.10%0.12s3.7s60.9 MiB4.2s189 MiB
Linux 5.3 → 5.4895 MiB6.1 MiB0.68%0.52s1m 4s60.6 MiB1m 6s573 MiB
Firefox 71.0b11 → b12198 MiB8.3 MiB4.20%0.11s1m 2s62.5 MiB58.5s189 MiB
Chrome 78.0.3904.97 → 108145 MiB5.6 MiB3.84%0.09s1m 18s57.6 MiB1m 21s186 MiB

System: Apple M2 Max (12 cores), 32 GiB RAM, macOS 26.1.

Default settings (1 MiB chunks, file-backed hash table). Memory column shows peak anonymous RSS during diffing.

Test caseNew sizePatch sizeRatioPatch timeDiff timeMemoryDiff time (RAM)Memory (RAM)
Wine 4.18 → 4.19201.4 MiB249 KiB0.120%0.194s0.258s25.7 MiB0.198s149.7 MiB
Linux 5.3 → 5.4894.7 MiB6.8 MiB0.762%0.918s1.400s51.0 MiB1.004s563.4 MiB
Firefox 71.0b11 → b12197.8 MiB10.9 MiB5.486%0.118s0.614s19.0 MiB0.578s147.9 MiB
Chrome 78.0.3904.97 → 108145.4 MiB57.0 MiB39.213%0.064s0.642s12.9 MiB0.640s13.0 MiB

Comparison with bidiff 1.1, bsdiff, and xdelta3

bidiff 1.1 (suffix arrays, single-threaded scan), bsdiff 4.3, xdelta3 3.0.11. Same test system.

Patch size

Test caseNew sizebidiff 2.0bidiff 1.1bsdiffxdelta3
Wine 4.18 → 4.19201 MiB249 KiB (0.12%)180 KiB (0.09%)110 KiB (0.05%)256 KiB (0.12%)
Linux 5.3 → 5.4895 MiB6.8 MiB (0.76%)6.2 MiB (0.69%)5.0 MiB (0.56%)5.4 MiB (0.60%)
Firefox 71.0b11 → b12198 MiB10.9 MiB (5.49%)7.2 MiB (3.66%)7.8 MiB (3.95%)21.7 MiB (10.95%)
Chrome 78.0.3904.97 → 108145 MiB8.3 MiB (5.71%)5.2 MiB (3.59%)5.0 MiB (3.46%)18.7 MiB (12.87%)

Diff time

Test casebidiff 2.0bidiff 1.1bsdiffxdelta3
Wine 4.18 → 4.190.42s29.8s3m 8s1.0s
Linux 5.3 → 5.42.1s4m 17s15m 6s8.5s
Firefox 71.0b11 → b120.76s1m 37s4m 47s18.8s
Chrome 78.0.3904.97 → 1080.79s1m 23s3m 7s18.6s

Patch time

Test casebidiff 2.0bidiff 1.1bsdiffxdelta3
Wine 4.18 → 4.190.13s0.69s1.29s0.42s
Linux 5.3 → 5.40.50s3.97s8.8s2.1s
Firefox 71.0b11 → b120.14s0.84s2.4s1.5s
Chrome 78.0.3904.97 → 1080.11s0.53s1.5s1.1s

bsdiff and bidiff 1.1 produce the smallest patches (suffix array matching) but are orders of magnitude slower to diff — minutes vs bidiff's sub-second times. xdelta3 is faster than bsdiff but still 2–25x slower than bidiff and produces the largest patches. bidiff 2.0 trades slightly larger patches for dramatically faster diffing and patching thanks to parallel hash-table scanning and parallel zstd decompression.

Workspace structure

  • bidiff (root) -- Library crate. Feature flags:
    • diff -- diffing engine (rayon, ringbuf, memmap2, tempfile)
    • patch -- patch encoding and application (integer-encoding, zstd)
    • Both enabled by default.
  • bidiff-cli (cli/) -- CLI binary with diff, patch, and cycle subcommands.

License

Licensed under either of

at your option.

Contributors

fasterthanlime

51 commits

bbqsrc

30 commits

nvzqz

9 commits

killercup

4 commits

divvun/bidiff

A Rust take on bsdiff

Rust

174

95 commits

updated Feb 19, 2026

See the code

README

bidiff

A binary diff and patch library for Rust, distantly derived from the bsdiff algorithm.

Instead of suffix arrays, bidiff uses a hash-table index with:

  • File-backed mmap by default -- the kernel can page the hash table to disk under memory pressure, keeping anonymous memory usage low
  • Optional --ram mode for faster diffs when memory is plentiful
  • Parallel construction via lock-free CAS insertion
  • Parallel scanning via rayon with ring-buffer channels for streaming matches
  • Chunked patch format with independent zstd-compressed chunks for parallel patch application

Usage

# Create a patch
bidiff diff old_file new_file patch_file

# Apply a patch
bidiff patch old_file patch_file output_file

# Round-trip verification (diff then patch, verifies hash match)
bidiff cycle old_file new_file

Options

--scan-chunk-mb <N>   Scan chunk size in MB (default: 1)
--block-size <N>      Hash index block size, minimum 4 (default: 32)
--threads <N>         Max threads for parallel scanning (default: all cores)
--ram                 Keep hash table in RAM (faster, uses more memory)
--max                 Maximize compression (zstd level 22)

Benchmarks

System: AMD Ryzen Threadripper 2950X (32 cores), 60 GiB RAM, Linux 6.12.

Default settings (1 MiB chunks, file-backed hash table). Memory column shows peak anonymous RSS during diffing.

Test caseNew sizePatch sizeRatioPatch timeDiff timeMemoryDiff time (RAM)Memory (RAM)
Wine 4.18 → 4.19201 MiB249 KiB0.12%0.13s0.42s21.5 MiB0.38s149 MiB
Linux 5.3 → 5.4895 MiB6.8 MiB0.76%0.50s2.13s59.2 MiB1.81s563 MiB
Firefox 71.0b11 → b12198 MiB10.9 MiB5.49%0.14s0.76s18.6 MiB0.73s147 MiB
Chrome 78.0.3904.97 → 108145 MiB8.3 MiB5.71%0.11s0.79s16.9 MiB0.75s147 MiB

With --max (zstd level 22)

Smaller patches at the cost of much slower diff times. Patch application speed is similar.

Test caseNew sizePatch sizeRatioPatch timeDiff timeMemoryDiff time (RAM)Memory (RAM)
Wine 4.18 → 4.19201 MiB203 KiB0.10%0.12s3.7s60.9 MiB4.2s189 MiB
Linux 5.3 → 5.4895 MiB6.1 MiB0.68%0.52s1m 4s60.6 MiB1m 6s573 MiB
Firefox 71.0b11 → b12198 MiB8.3 MiB4.20%0.11s1m 2s62.5 MiB58.5s189 MiB
Chrome 78.0.3904.97 → 108145 MiB5.6 MiB3.84%0.09s1m 18s57.6 MiB1m 21s186 MiB

System: Apple M2 Max (12 cores), 32 GiB RAM, macOS 26.1.

Default settings (1 MiB chunks, file-backed hash table). Memory column shows peak anonymous RSS during diffing.

Test caseNew sizePatch sizeRatioPatch timeDiff timeMemoryDiff time (RAM)Memory (RAM)
Wine 4.18 → 4.19201.4 MiB249 KiB0.120%0.194s0.258s25.7 MiB0.198s149.7 MiB
Linux 5.3 → 5.4894.7 MiB6.8 MiB0.762%0.918s1.400s51.0 MiB1.004s563.4 MiB
Firefox 71.0b11 → b12197.8 MiB10.9 MiB5.486%0.118s0.614s19.0 MiB0.578s147.9 MiB
Chrome 78.0.3904.97 → 108145.4 MiB57.0 MiB39.213%0.064s0.642s12.9 MiB0.640s13.0 MiB

Comparison with bidiff 1.1, bsdiff, and xdelta3

bidiff 1.1 (suffix arrays, single-threaded scan), bsdiff 4.3, xdelta3 3.0.11. Same test system.

Patch size

Test caseNew sizebidiff 2.0bidiff 1.1bsdiffxdelta3
Wine 4.18 → 4.19201 MiB249 KiB (0.12%)180 KiB (0.09%)110 KiB (0.05%)256 KiB (0.12%)
Linux 5.3 → 5.4895 MiB6.8 MiB (0.76%)6.2 MiB (0.69%)5.0 MiB (0.56%)5.4 MiB (0.60%)
Firefox 71.0b11 → b12198 MiB10.9 MiB (5.49%)7.2 MiB (3.66%)7.8 MiB (3.95%)21.7 MiB (10.95%)
Chrome 78.0.3904.97 → 108145 MiB8.3 MiB (5.71%)5.2 MiB (3.59%)5.0 MiB (3.46%)18.7 MiB (12.87%)

Diff time

Test casebidiff 2.0bidiff 1.1bsdiffxdelta3
Wine 4.18 → 4.190.42s29.8s3m 8s1.0s
Linux 5.3 → 5.42.1s4m 17s15m 6s8.5s
Firefox 71.0b11 → b120.76s1m 37s4m 47s18.8s
Chrome 78.0.3904.97 → 1080.79s1m 23s3m 7s18.6s

Patch time

Test casebidiff 2.0bidiff 1.1bsdiffxdelta3
Wine 4.18 → 4.190.13s0.69s1.29s0.42s
Linux 5.3 → 5.40.50s3.97s8.8s2.1s
Firefox 71.0b11 → b120.14s0.84s2.4s1.5s
Chrome 78.0.3904.97 → 1080.11s0.53s1.5s1.1s

bsdiff and bidiff 1.1 produce the smallest patches (suffix array matching) but are orders of magnitude slower to diff — minutes vs bidiff's sub-second times. xdelta3 is faster than bsdiff but still 2–25x slower than bidiff and produces the largest patches. bidiff 2.0 trades slightly larger patches for dramatically faster diffing and patching thanks to parallel hash-table scanning and parallel zstd decompression.

Workspace structure

  • bidiff (root) -- Library crate. Feature flags:
    • diff -- diffing engine (rayon, ringbuf, memmap2, tempfile)
    • patch -- patch encoding and application (integer-encoding, zstd)
    • Both enabled by default.
  • bidiff-cli (cli/) -- CLI binary with diff, patch, and cycle subcommands.

License

Licensed under either of

at your option.

Contributors

fasterthanlime

51 commits

bbqsrc

30 commits

nvzqz

9 commits

killercup

4 commits

Languages

Rust

85.4%

Shell

14.6%