https://github.com/brianpugh/hdiffpatch-python
hdiffpatch-python is a python wrapper around the HDiffPatch C++ library, providing binary diff and patch operations with compression support.
Science Score: 26.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
○CITATION.cff file
-
✓codemeta.json file
Found codemeta.json file -
✓.zenodo.json file
Found .zenodo.json file -
○DOI references
-
○Academic publication links
-
○Committers with academic emails
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (11.5%) to scientific vocabulary
Repository
hdiffpatch-python is a python wrapper around the HDiffPatch C++ library, providing binary diff and patch operations with compression support.
Basic Info
- Host: GitHub
- Owner: BrianPugh
- License: apache-2.0
- Language: Python
- Default Branch: main
- Size: 914 KB
Statistics
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
- Releases: 1
Metadata Files
README.md
hdiffpatch-python is a Python wrapper around the HDiffPatch C++ library, providing binary diff and patch operations with compression support.
Installation
hdiffpatch requires Python >=3.9 and can be installed via:
bash
pip install hdiffpatch
For development installation:
bash
git clone https://github.com/BrianPugh/hdiffpatch-python.git
cd hdiffpatch-python
uv sync
uv run python rebuild.py # Build Cython extensions
Quick Start
hdiffpatch primarily provides 2 simple functions:
difffor creating a patch.applyfor applying a patch.
Basic Usage
```python import hdiffpatch
Create binary data
olddata = b"Hello, world!" newdata = b"Hello, HDiffPatch!"
Create a diff
diff = hdiffpatch.diff(olddata, newdata)
Apply the diff
result = hdiffpatch.apply(olddata, diff) assert result == newdata ```
With Simple Compression
```python import hdiffpatch
olddata = b"Large binary data..." * 1000 newdata = b"Modified binary data..." * 1000
Create a compressed diff
diff = hdiffpatch.diff(olddata, newdata, compression="zlib")
Apply patch
result = hdiffpatch.apply(olddata, diff) assert result == newdata ```
With Advanced Compression Configuration
```python import hdiffpatch
olddata = b"Large binary data..." * 1000 newdata = b"Modified binary data..." * 1000
Use configuration classes for fine-grained control
config = hdiffpatch.ZlibConfig(level=9, window=12)
diff = hdiffpatch.diff(olddata, newdata, compression=config) result = hdiffpatch.apply(olddata, diff) assert result == newdata ```
Recompressing Diffs
```python import hdiffpatch
olddata = b"Large binary data..." * 1000 newdata = b"Modified binary data..." * 1000
Create a diff with zlib compression
diffzlib = hdiffpatch.diff(olddata, new_data, compression="zlib")
Recompress the same diff with zstd
diffzstd = hdiffpatch.recompress(diffzlib, compression="zstd")
Remove compression entirely
diffuncompressed = hdiffpatch.recompress(diffzlib, compression="none")
Both diffs produce the same result when applied
result1 = hdiffpatch.apply(olddata, diffzlib) result2 = hdiffpatch.apply(olddata, diffzstd) assert result1 == result2 == new_data ```
API Reference
Core Functions
python
def diff(old_data, new_data, compression="none", *, validate=True) -> bytes
Create a binary diff between two byte sequences.
Parameters:
old_data(bytes): Original data.new_data(bytes): Modified data.compression(str or config object): Compression type as string ("none","zlib","lzma","lzma2","zstd","bzip2","tamp") or a compression configuration object.validate(bool): Test that the patch successfully convertsold_datatonew_data. This is a computationally inexpensive operation. Defaults toTrue.
Returns: bytes - Binary diff data that can be used with apply() and old_data to generate new_data.
python
def apply(old_data, diff_data) -> bytes
Apply a binary patch to reconstruct new data.
Parameters:
old_data(bytes): Original data.diff_data(bytes): Patch data fromdiff().
Returns: bytes - Reconstructed data. The new_data that was passed to diff().
python
def recompress(diff_data, compression=None) -> bytes
Recompress a diff with a different compression algorithm.
Parameters:
diff_data(bytes): The diff data to recompress.compression(str or config object, optional): Target compression type as string ("none","zlib","lzma","lzma2","zstd","bzip2","tamp") or a compression configuration object. If None, removes compression.
Returns: bytes - The recompressed diff data
Compression Configuration
For advanced compression control, hdiffpatch provides configuration classes for each compression algorithm:
ZStdConfig
Fine-grained control over Zstandard compression:
```python
Basic configuration
config = hdiffpatch.ZStdConfig(level=15, window=20, workers=2)
Preset configurations
config = hdiffpatch.ZStdConfig.fast() # Optimized for speed config = hdiffpatch.ZStdConfig.balanced() # Balanced speed/compression config = hdiffpatch.ZStdConfig.bestcompression() # Maximum compression config = hdiffpatch.ZStdConfig.minimalmemory() # Minimal memory usage
Use with diff
diff = hdiffpatch.diff(olddata, newdata, compression=config) ```
Parameters:
level(1-22): Compression level, higher = better compressionwindow(10-27): Window size as log2, larger = better compressionworkers(0-200): Number of threads, 0 = single-threaded
ZlibConfig
Fine-grained control over zlib compression:
```python
Basic configuration
config = hdiffpatch.ZlibConfig( level=9, memory_level=8, window=15, strategy=hdiffpatch.ZlibStrategy.DEFAULT )
Preset configurations
config = hdiffpatch.ZlibConfig.fast() config = hdiffpatch.ZlibConfig.balanced() config = hdiffpatch.ZlibConfig.bestcompression() config = hdiffpatch.ZlibConfig.minimalmemory() config = hdiffpatch.ZlibConfig.png_optimized() # Optimized for PNG-like data ```
Parameters:
level(0-9): Compression levelmemory_level(1-9): Memory usage levelwindow(9-15): Window size as power of 2strategy: Compression strategy (DEFAULT,FILTERED,HUFFMAN_ONLY,RLE,FIXED)
LzmaConfig and Lzma2Config
Fine-grained control over LZMA compression:
```python
LZMA configuration
config = hdiffpatch.LzmaConfig(level=9, window=23, thread_num=1)
LZMA2 configuration (supports more threads)
config = hdiffpatch.Lzma2Config(level=9, window=23, thread_num=4)
Preset configurations available for both
config = hdiffpatch.LzmaConfig.fast() config = hdiffpatch.LzmaConfig.balanced() config = hdiffpatch.LzmaConfig.bestcompression() config = hdiffpatch.LzmaConfig.minimalmemory() ```
Parameters:
level(0-9): Compression levelwindow(12-30): Window size as log2thread_num: Number of threads (1-2 for LZMA, 1-64 for LZMA2)
BZip2Config
Fine-grained control over bzip2 compression:
```python config = hdiffpatch.BZip2Config(level=9, work_factor=30)
Preset configurations
config = hdiffpatch.BZip2Config.fast() config = hdiffpatch.BZip2Config.balanced() config = hdiffpatch.BZip2Config.bestcompression() config = hdiffpatch.BZip2Config.minimalmemory() ```
Parameters:
level(1-9): Compression levelwork_factor(0-250): Work factor for worst-case scenarios
TampConfig
Fine-grained control over Tamp compression (embedded-friendly):
```python config = hdiffpatch.TampConfig(window=10)
Preset configurations
config = hdiffpatch.TampConfig.fast() config = hdiffpatch.TampConfig.balanced() config = hdiffpatch.TampConfig.bestcompression() config = hdiffpatch.TampConfig.minimalmemory() ```
Parameters:
window(8-15): Window size as power of 2
Exceptions
python
hdiffpatch.HDiffPatchError
Compression Performance
Different compression algorithms offer trade-offs between compression ratio and speed:
zlib: Good balance of speed and compression. Very common.zstd: Fast compression with good ratios.lzma/lzma2: Very high compression ratios, slower.bzip2: Good compression, moderate speedtamp: Embedded-friendly compression, minimal memory usage.
Basic Compression Comparison
```python import hdiffpatch
Large repetitive data
olddata = b"A" * 10000 + b"B" * 10000 newdata = b"A" * 10000 + b"C" * 10000
Compare compression effectiveness
for compression in ["none", "zlib", "zstd", "lzma", "bzip2", "tamp"]: diff = hdiffpatch.diff(olddata, newdata, compression=compression) print(f"{compression}: {len(diff)} bytes") ```
Advanced Configuration Comparison
```python import hdiffpatch
Compare different configuration approaches
configs = { "zstdfast": hdiffpatch.ZStdConfig.fast(), "zstdbest": hdiffpatch.ZStdConfig.bestcompression(), "zlibbalanced": hdiffpatch.ZlibConfig.balanced(), "lzma2custom": hdiffpatch.Lzma2Config(level=6, window=20, threadnum=4), }
for name, config in configs.items(): diff = hdiffpatch.diff(olddata, newdata, compression=config) print(f"{name}: {len(diff)} bytes") ```
Real-World Example: MicroPython Firmware
Here's a comprehensive comparison using actual MicroPython firmware files with a 12-bit window size (4096 bytes). This window size was chosen because it is typically a good trade-off between memory-usage and compression-performance for embedded targets.
- RPI_PICO-20241129-v1.24.1.uf2: 651 KB
- RPI_PICO-20250415-v1.25.0.uf2: 652 KB
Since we're using compression for the diff, a natural question would be: "If I'm adding a decompression library to my target project, then how much smaller is the patch compared to just compressing the firmware?" To answer this question, we compare the size of the compressed patch to the compressed firmware.
| Algorithm | Size (HDiffPatch) | Size (firmware) | Improvement | |-----------|-------------------|-----------------|-------------| | none | 209.7 KB | 652.0 KB | 3.11x | | tamp | 143.1 KB | 322.8 KB | 2.26x | | zstd | 133.4 KB | 277.6 KB | 2.08x | | zlib | 125.5 KB | 251.8 KB | 2.01x | | bzip2 | 128.6 KB | 246.2 KB | 1.91x | | lzma | 116.9 KB | 222.7 KB | 1.91x |
In this example, using hdiffpatch resulted in a ~3x smaller update when compared to a naive uncompressed firmware update, and ~2x smaller when comparing against an equivalently-compressed firmware update.
To reproduce these results:
bash
uv run python tools/micropython-binary-demo.py
Owner
- Name: Brian Pugh
- Login: BrianPugh
- Kind: user
- Location: Washington D.C.
- Repositories: 123
- Profile: https://github.com/BrianPugh
Deep Learning Scientist and blockchain enthusiast
GitHub Events
Total
- Push event: 10
- Pull request event: 3
- Create event: 1
Last Year
- Push event: 10
- Pull request event: 3
- Create event: 1
Issues and Pull Requests
Last synced: 10 months ago
Packages
- Total packages: 1
-
Total downloads:
- pypi 848 last-month
- Total dependent packages: 0
- Total dependent repositories: 0
- Total versions: 2
- Total maintainers: 1
pypi.org: hdiffpatch
Python wrapper around HDiffPatch C++ library for efficient binary diff/patch operations.
- Homepage: https://github.com/BrianPugh/hdiffpatch-python
- Documentation: https://hdiffpatch.readthedocs.io/
- License: Apache-2.0
-
Latest release: 1.0.0
published 12 months ago
Rankings
Maintainers (1)
Dependencies
- actions/cache v4 composite
- actions/checkout v4 composite
- actions/download-artifact v4 composite
- actions/setup-python v5 composite
- actions/upload-artifact v4 composite
- docker/setup-qemu-action v3 composite
- pypa/cibuildwheel v2.23.3 composite
- pypa/gh-action-pypi-publish release/v1 composite
- snok/install-poetry v1 composite
- actions/cache v4 composite
- actions/checkout v4 composite
- actions/setup-python v5 composite
- andstor/file-existence-action v3 composite
- codecov/codecov-action v4 composite
- snok/install-poetry v1 composite
- asttokens 3.0.0
- attrs 25.3.0
- cfgv 3.4.0
- colorama 0.4.6
- coverage 7.9.2
- cython 3.1.2
- decorator 5.2.1
- distlib 0.3.9
- exceptiongroup 1.3.0
- executing 2.2.0
- filelock 3.18.0
- identify 2.6.12
- iniconfig 2.1.0
- ipdb 0.13.13
- ipython 8.18.1
- jedi 0.19.2
- line-profiler 4.2.0
- matplotlib-inline 0.1.7
- nodeenv 1.9.1
- packaging 25.0
- parso 0.8.4
- pexpect 4.9.0
- platformdirs 4.3.8
- pluggy 1.6.0
- pre-commit 4.2.0
- prompt-toolkit 3.0.51
- ptyprocess 0.7.0
- pure-eval 0.2.3
- pygments 2.19.2
- pytest 8.4.1
- pytest-cov 6.2.1
- pytest-mock 3.14.1
- pyyaml 6.0.2
- stack-data 0.6.3
- tomli 2.2.1
- traitlets 5.14.3
- typing-extensions 4.14.1
- virtualenv 20.31.2
- wcwidth 0.2.13
- ipdb >=0.13.9 debug
- line_profiler >=3.5.1 debug
- coverage >=5.1 develop
- cython >=1.0.0 develop
- pre_commit >=2.16.0 develop
- pytest >=7.1.2 develop
- pytest-cov >=3.0.0 develop
- pytest-mock >=3.7.0 develop
- attrs >=21.3.0
- python ^3.9