hdiffpatch 1.1.0__cp314-cp314-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,177 @@
1
+ """ZlibConfig class for configuring zlib compression parameters."""
2
+
3
+ from enum import Enum
4
+ from typing import Union
5
+
6
+ import attrs
7
+
8
+ from ._base_config import BaseConfig
9
+
10
+
11
+ class ZlibStrategy(Enum):
12
+ """Zlib compression strategies."""
13
+
14
+ DEFAULT = "default"
15
+ FILTERED = "filtered"
16
+ HUFFMAN_ONLY = "huffman_only"
17
+ RLE = "rle"
18
+ FIXED = "fixed"
19
+
20
+
21
+ def _convert_strategy(value: Union[ZlibStrategy, str]) -> ZlibStrategy:
22
+ """Convert and validate strategy parameter.
23
+
24
+ Parameters
25
+ ----------
26
+ value : ZlibStrategy or str
27
+ The strategy value to convert
28
+
29
+ Returns
30
+ -------
31
+ ZlibStrategy
32
+ The validated strategy enum
33
+
34
+ Raises
35
+ ------
36
+ TypeError
37
+ If value is not a ZlibStrategy enum or string
38
+ ValueError
39
+ If string value is not a valid strategy
40
+ """
41
+ if isinstance(value, str):
42
+ try:
43
+ return ZlibStrategy(value.lower())
44
+ except ValueError:
45
+ valid_strategies = [s.value for s in ZlibStrategy]
46
+ raise ValueError(f"Invalid strategy '{value}'. Valid options: {valid_strategies}") from None
47
+ elif isinstance(value, ZlibStrategy):
48
+ return value
49
+ else:
50
+ raise TypeError("strategy must be a ZlibStrategy enum or string")
51
+
52
+
53
+ @attrs.frozen
54
+ class ZlibConfig(BaseConfig):
55
+ """Configuration for zlib compression parameters.
56
+
57
+ This class allows fine-grained control over zlib compression behavior,
58
+ including compression level, memory usage, window size, and strategy.
59
+
60
+ Parameters
61
+ ----------
62
+ level : int, default=9
63
+ Compression level (0-9). Higher values give better
64
+ compression but are slower. 0 = no compression, 9 = best compression.
65
+ memory_level : int, default=8
66
+ Memory level (1-9). Higher values use more memory but
67
+ may improve compression speed.
68
+ window : int, default=15
69
+ Window size as power of 2 (9-15). Larger windows give
70
+ better compression but use more memory. (32KB window)
71
+ strategy : ZlibStrategy, default=ZlibStrategy.DEFAULT
72
+ Compression strategy optimized for different data types.
73
+ save_window_bits : bool, default=True
74
+ Whether to save window bits in compressed data header.
75
+
76
+ Examples
77
+ --------
78
+ Fast compression with minimal memory usage
79
+
80
+ >>> config = ZlibConfig(level=1, memory_level=1, window=9)
81
+
82
+ Best compression for text data
83
+
84
+ >>> config = ZlibConfig(level=9, strategy=ZlibStrategy.DEFAULT)
85
+
86
+ Optimized for PNG-like data
87
+
88
+ >>> config = ZlibConfig(strategy=ZlibStrategy.RLE)
89
+ """
90
+
91
+ level: int = attrs.field(
92
+ default=9,
93
+ validator=attrs.validators.and_(
94
+ attrs.validators.instance_of(int),
95
+ attrs.validators.ge(0),
96
+ attrs.validators.le(9),
97
+ ),
98
+ )
99
+ memory_level: int = attrs.field(
100
+ default=8,
101
+ validator=attrs.validators.and_(
102
+ attrs.validators.instance_of(int),
103
+ attrs.validators.ge(1),
104
+ attrs.validators.le(9),
105
+ ),
106
+ )
107
+ window: int = attrs.field(
108
+ default=15,
109
+ validator=attrs.validators.and_(
110
+ attrs.validators.instance_of(int),
111
+ attrs.validators.ge(9),
112
+ attrs.validators.le(15),
113
+ ),
114
+ )
115
+ strategy: ZlibStrategy = attrs.field(
116
+ default=ZlibStrategy.DEFAULT,
117
+ converter=_convert_strategy,
118
+ )
119
+ save_window_bits: bool = attrs.field(
120
+ default=True,
121
+ converter=bool,
122
+ )
123
+
124
+ @classmethod
125
+ def fast(cls) -> "ZlibConfig":
126
+ """Create a ZlibConfig optimized for speed.
127
+
128
+ Returns
129
+ -------
130
+ ZlibConfig
131
+ Configuration optimized for speed
132
+ """
133
+ return cls(level=1, memory_level=1, window=9)
134
+
135
+ @classmethod
136
+ def balanced(cls) -> "ZlibConfig":
137
+ """Create a ZlibConfig with balanced speed/compression.
138
+
139
+ Returns
140
+ -------
141
+ ZlibConfig
142
+ Configuration with balanced speed/compression tradeoff
143
+ """
144
+ return cls(level=6, memory_level=8, window=15)
145
+
146
+ @classmethod
147
+ def best_compression(cls) -> "ZlibConfig":
148
+ """Create a ZlibConfig optimized for best compression.
149
+
150
+ Returns
151
+ -------
152
+ ZlibConfig
153
+ Configuration optimized for best compression
154
+ """
155
+ return cls(level=9, memory_level=9, window=15)
156
+
157
+ @classmethod
158
+ def minimal_memory(cls) -> "ZlibConfig":
159
+ """Create a ZlibConfig with minimal memory usage.
160
+
161
+ Returns
162
+ -------
163
+ ZlibConfig
164
+ Configuration with minimal memory usage
165
+ """
166
+ return cls(level=6, memory_level=1, window=9)
167
+
168
+ @classmethod
169
+ def png_optimized(cls) -> "ZlibConfig":
170
+ """Create a ZlibConfig optimized for PNG-like data.
171
+
172
+ Returns
173
+ -------
174
+ ZlibConfig
175
+ Configuration optimized for PNG-like data
176
+ """
177
+ return cls(level=9, strategy=ZlibStrategy.RLE)
@@ -0,0 +1,113 @@
1
+ """ZStdConfig class for configuring zstd compression parameters."""
2
+
3
+ from typing import Optional
4
+
5
+ import attrs
6
+
7
+ from ._base_config import BaseConfig
8
+
9
+
10
+ @attrs.frozen
11
+ class ZStdConfig(BaseConfig):
12
+ """Configuration for zstd compression parameters.
13
+
14
+ This class allows control over zstd compression behavior,
15
+ including compression level, window size, and threading.
16
+
17
+ Parameters
18
+ ----------
19
+ level : int, default=3
20
+ Compression level (1-22). Higher values give better
21
+ compression but are slower. 1 = fastest, 22 = best compression.
22
+ window : int or None, default=None
23
+ Window size as log2. Must be between 10 and 27.
24
+ Larger windows give better compression but use more memory.
25
+ None uses zstd default based on compression level.
26
+ workers : int, default=0
27
+ Number of worker threads (0-200). 0 = single-threaded.
28
+
29
+ Examples
30
+ --------
31
+ Fast compression with minimal memory usage
32
+
33
+ >>> config = ZStdConfig(level=1, workers=0)
34
+
35
+ Best compression for large files with multiple threads
36
+
37
+ >>> config = ZStdConfig(level=22, workers=4)
38
+
39
+ Balanced compression with custom window size
40
+
41
+ >>> config = ZStdConfig(level=6, window=20, workers=2)
42
+ """
43
+
44
+ level: int = attrs.field(
45
+ default=3,
46
+ validator=attrs.validators.and_(
47
+ attrs.validators.instance_of(int),
48
+ attrs.validators.ge(1),
49
+ attrs.validators.le(22),
50
+ ),
51
+ )
52
+ window: Optional[int] = attrs.field(
53
+ default=None,
54
+ validator=attrs.validators.optional(
55
+ attrs.validators.and_(
56
+ attrs.validators.instance_of(int),
57
+ attrs.validators.ge(10),
58
+ attrs.validators.le(27),
59
+ )
60
+ ),
61
+ )
62
+ workers: int = attrs.field(
63
+ default=0,
64
+ validator=attrs.validators.and_(
65
+ attrs.validators.instance_of(int),
66
+ attrs.validators.ge(0),
67
+ attrs.validators.le(200),
68
+ ),
69
+ )
70
+
71
+ @classmethod
72
+ def fast(cls) -> "ZStdConfig":
73
+ """Create a ZStdConfig optimized for speed.
74
+
75
+ Returns
76
+ -------
77
+ ZStdConfig
78
+ Configuration optimized for speed
79
+ """
80
+ return cls(level=1, workers=0)
81
+
82
+ @classmethod
83
+ def balanced(cls) -> "ZStdConfig":
84
+ """Create a ZStdConfig with balanced speed/compression.
85
+
86
+ Returns
87
+ -------
88
+ ZStdConfig
89
+ Configuration with balanced speed/compression tradeoff
90
+ """
91
+ return cls(level=6, workers=2)
92
+
93
+ @classmethod
94
+ def best_compression(cls) -> "ZStdConfig":
95
+ """Create a ZStdConfig optimized for best compression.
96
+
97
+ Returns
98
+ -------
99
+ ZStdConfig
100
+ Configuration optimized for best compression
101
+ """
102
+ return cls(level=22, window=27, workers=4)
103
+
104
+ @classmethod
105
+ def minimal_memory(cls) -> "ZStdConfig":
106
+ """Create a ZStdConfig with minimal memory usage.
107
+
108
+ Returns
109
+ -------
110
+ ZStdConfig
111
+ Configuration with minimal memory usage
112
+ """
113
+ return cls(level=3, window=10, workers=0)
hdiffpatch/py.typed ADDED
File without changes
@@ -0,0 +1,2 @@
1
+ Version: 1.13.0
2
+ Arguments: ['C:\\Users\\runneradmin\\AppData\\Local\\Temp\\cibw-run-6z3wtab3\\cp314-win_amd64\\build\\venv\\Scripts\\delvewheel', 'repair', '-w', 'C:\\Users\\runneradmin\\AppData\\Local\\Temp\\cibw-run-6z3wtab3\\cp314-win_amd64\\repaired_wheel', '-v', 'C:\\Users\\runneradmin\\AppData\\Local\\Temp\\cibw-run-6z3wtab3\\cp314-win_amd64\\built_wheel\\hdiffpatch-1.1.0-cp314-cp314-win_amd64.whl']
@@ -0,0 +1,377 @@
1
+ Metadata-Version: 2.4
2
+ Name: hdiffpatch
3
+ Version: 1.1.0
4
+ Summary: Python wrapper around HDiffPatch C++ library for efficient binary diff/patch operations.
5
+ Author: Brian Pugh
6
+ License-Expression: Apache-2.0
7
+ Project-URL: homepage, https://github.com/BrianPugh/hdiffpatch-python
8
+ Project-URL: repository, https://github.com/BrianPugh/hdiffpatch-python
9
+ Keywords: binary,diff,patch,compression,hdiffpatch,delta,update
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Programming Language :: C++
21
+ Classifier: Programming Language :: Cython
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Topic :: System :: Archiving :: Compression
24
+ Classifier: Topic :: Utilities
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: attrs>=21.3.0
29
+ Dynamic: license-file
30
+
31
+ **hdiffpatch-python** is a Python wrapper around the [HDiffPatch](https://github.com/sisong/HDiffPatch) C++ library, providing binary diff and patch operations with compression support.
32
+
33
+ <div align="center">
34
+
35
+ ![Python compat](https://img.shields.io/badge/%3E=python-3.9-blue.svg)
36
+ [![PyPi](https://img.shields.io/pypi/v/hdiffpatch.svg)](https://pypi.python.org/pypi/hdiffpatch)
37
+ [![GHA Status](https://github.com/BrianPugh/hdiffpatch-python/actions/workflows/tests.yaml/badge.svg?branch=main)](https://github.com/BrianPugh/hdiffpatch-python/actions?query=workflow%3Atests)
38
+ [![Coverage](https://codecov.io/github/BrianPugh/hdiffpatch-python/coverage.svg?branch=main)](https://codecov.io/github/BrianPugh/hdiffpatch-python?branch=main)
39
+
40
+ </div>
41
+
42
+ ## Installation
43
+
44
+ **hdiffpatch** requires Python `>=3.9` and can be installed via:
45
+
46
+ ```bash
47
+ pip install hdiffpatch
48
+ ```
49
+
50
+ For development installation:
51
+
52
+ ```bash
53
+ git clone https://github.com/BrianPugh/hdiffpatch-python.git
54
+ cd hdiffpatch-python
55
+ uv sync
56
+ uv run python rebuild.py # Build Cython extensions
57
+ ```
58
+
59
+ ## Quick Start
60
+
61
+ **hdiffpatch** primarily provides 2 simple functions:
62
+
63
+ * `diff` for creating a patch.
64
+ * `apply` for applying a patch.
65
+
66
+ ### Basic Usage
67
+
68
+ ```python
69
+ import hdiffpatch
70
+
71
+ # Create binary data
72
+ old_data = b"Hello, world!"
73
+ new_data = b"Hello, HDiffPatch!"
74
+
75
+ # Create a diff
76
+ diff = hdiffpatch.diff(old_data, new_data)
77
+
78
+ # Apply the diff
79
+ result = hdiffpatch.apply(old_data, diff)
80
+ assert result == new_data
81
+ ```
82
+
83
+ ### With Simple Compression
84
+
85
+ ```python
86
+ import hdiffpatch
87
+
88
+ old_data = b"Large binary data..." * 1000
89
+ new_data = b"Modified binary data..." * 1000
90
+
91
+ # Create a compressed diff
92
+ diff = hdiffpatch.diff(old_data, new_data, compression="zlib")
93
+
94
+ # Apply patch
95
+ result = hdiffpatch.apply(old_data, diff)
96
+ assert result == new_data
97
+ ```
98
+
99
+ ### With Advanced Compression Configuration
100
+
101
+ ```python
102
+ import hdiffpatch
103
+
104
+ old_data = b"Large binary data..." * 1000
105
+ new_data = b"Modified binary data..." * 1000
106
+
107
+ # Use configuration classes for fine-grained control
108
+ config = hdiffpatch.ZlibConfig(level=9, window=12)
109
+
110
+ diff = hdiffpatch.diff(old_data, new_data, compression=config)
111
+ result = hdiffpatch.apply(old_data, diff)
112
+ assert result == new_data
113
+ ```
114
+
115
+ ### Recompressing Diffs
116
+
117
+ ```python
118
+ import hdiffpatch
119
+
120
+ old_data = b"Large binary data..." * 1000
121
+ new_data = b"Modified binary data..." * 1000
122
+
123
+ # Create a diff with zlib compression
124
+ diff_zlib = hdiffpatch.diff(old_data, new_data, compression="zlib")
125
+
126
+ # Recompress the same diff with zstd
127
+ diff_zstd = hdiffpatch.recompress(diff_zlib, compression="zstd")
128
+
129
+ # Remove compression entirely
130
+ diff_uncompressed = hdiffpatch.recompress(diff_zlib, compression="none")
131
+
132
+ # Both diffs produce the same result when applied
133
+ result1 = hdiffpatch.apply(old_data, diff_zlib)
134
+ result2 = hdiffpatch.apply(old_data, diff_zstd)
135
+ assert result1 == result2 == new_data
136
+ ```
137
+
138
+ ## API Reference
139
+
140
+ ### Core Functions
141
+
142
+ ```python
143
+ def diff(old_data, new_data, compression="none", *, validate=True) -> bytes
144
+ ```
145
+
146
+ Create a binary diff between two byte sequences.
147
+
148
+ **Parameters:**
149
+
150
+ * `old_data` (bytes): Original data.
151
+ * `new_data` (bytes): Modified data.
152
+ * `compression` (str or config object): Compression type as string (`"none"`, `"zlib"`, `"lzma"`, `"lzma2"`, `"zstd"`, `"bzip2"`, `"tamp"`) or a compression configuration object.
153
+ * `validate` (bool): Test that the patch successfully converts `old_data` to `new_data`. This is a computationally inexpensive operation. Defaults to `True`.
154
+
155
+ **Returns:** `bytes` - Binary diff data that can be used with `apply()` and `old_data` to generate `new_data`.
156
+
157
+ ---
158
+
159
+ ```python
160
+ def apply(old_data, diff_data) -> bytes
161
+ ```
162
+
163
+ Apply a binary patch to reconstruct new data.
164
+
165
+ **Parameters:**
166
+
167
+ * `old_data` (bytes): Original data.
168
+ * `diff_data` (bytes): Patch data from `diff()`.
169
+
170
+ **Returns:** `bytes` - Reconstructed data. The `new_data` that was passed to `diff()`.
171
+
172
+ ---
173
+
174
+ ```python
175
+ def recompress(diff_data, compression=None) -> bytes
176
+ ```
177
+
178
+ Recompress a diff with a different compression algorithm.
179
+
180
+ **Parameters:**
181
+
182
+ * `diff_data` (bytes): The diff data to recompress.
183
+ * `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.
184
+
185
+ **Returns:** `bytes` - The recompressed diff data
186
+
187
+ ### Compression Configuration
188
+
189
+ For advanced compression control, **hdiffpatch** provides configuration classes for each compression algorithm:
190
+
191
+ #### ZStdConfig
192
+
193
+ Fine-grained control over Zstandard compression:
194
+
195
+ ```python
196
+ # Basic configuration
197
+ config = hdiffpatch.ZStdConfig(level=15, window=20, workers=2)
198
+
199
+ # Preset configurations
200
+ config = hdiffpatch.ZStdConfig.fast() # Optimized for speed
201
+ config = hdiffpatch.ZStdConfig.balanced() # Balanced speed/compression
202
+ config = hdiffpatch.ZStdConfig.best_compression() # Maximum compression
203
+ config = hdiffpatch.ZStdConfig.minimal_memory() # Minimal memory usage
204
+
205
+ # Use with diff
206
+ diff = hdiffpatch.diff(old_data, new_data, compression=config)
207
+ ```
208
+
209
+ **Parameters:**
210
+
211
+ * `level` (1-22): Compression level, higher = better compression
212
+ * `window` (10-27): Window size as log2, larger = better compression
213
+ * `workers` (0-200): Number of threads, 0 = single-threaded
214
+
215
+ #### ZlibConfig
216
+
217
+ Fine-grained control over zlib compression:
218
+
219
+ ```python
220
+ # Basic configuration
221
+ config = hdiffpatch.ZlibConfig(
222
+ level=9,
223
+ memory_level=8,
224
+ window=15,
225
+ strategy=hdiffpatch.ZlibStrategy.DEFAULT
226
+ )
227
+
228
+ # Preset configurations
229
+ config = hdiffpatch.ZlibConfig.fast()
230
+ config = hdiffpatch.ZlibConfig.balanced()
231
+ config = hdiffpatch.ZlibConfig.best_compression()
232
+ config = hdiffpatch.ZlibConfig.minimal_memory()
233
+ config = hdiffpatch.ZlibConfig.png_optimized() # Optimized for PNG-like data
234
+ ```
235
+
236
+ **Parameters:**
237
+
238
+ * `level` (0-9): Compression level
239
+ * `memory_level` (1-9): Memory usage level
240
+ * `window` (9-15): Window size as power of 2
241
+ * `strategy`: Compression strategy (`DEFAULT`, `FILTERED`, `HUFFMAN_ONLY`, `RLE`, `FIXED`)
242
+
243
+ #### LzmaConfig and Lzma2Config
244
+
245
+ Fine-grained control over LZMA compression:
246
+
247
+ ```python
248
+ # LZMA configuration
249
+ config = hdiffpatch.LzmaConfig(level=9, window=23, thread_num=1)
250
+
251
+ # LZMA2 configuration (supports more threads)
252
+ config = hdiffpatch.Lzma2Config(level=9, window=23, thread_num=4)
253
+
254
+ # Preset configurations available for both
255
+ config = hdiffpatch.LzmaConfig.fast()
256
+ config = hdiffpatch.LzmaConfig.balanced()
257
+ config = hdiffpatch.LzmaConfig.best_compression()
258
+ config = hdiffpatch.LzmaConfig.minimal_memory()
259
+ ```
260
+
261
+ **Parameters:**
262
+
263
+ * `level` (0-9): Compression level
264
+ * `window` (12-30): Window size as log2
265
+ * `thread_num`: Number of threads (1-2 for LZMA, 1-64 for LZMA2)
266
+
267
+ #### BZip2Config
268
+
269
+ Fine-grained control over bzip2 compression:
270
+
271
+ ```python
272
+ config = hdiffpatch.BZip2Config(level=9)
273
+
274
+ # Preset configurations
275
+ config = hdiffpatch.BZip2Config.fast()
276
+ config = hdiffpatch.BZip2Config.balanced()
277
+ config = hdiffpatch.BZip2Config.best_compression()
278
+ config = hdiffpatch.BZip2Config.minimal_memory()
279
+ ```
280
+
281
+ **Parameters:**
282
+
283
+ * `level` (1-9): Compression level
284
+
285
+ #### TampConfig
286
+
287
+ Fine-grained control over [Tamp](https://github.com/BrianPugh/tamp) compression (embedded-friendly):
288
+
289
+ ```python
290
+ config = hdiffpatch.TampConfig(window=10)
291
+
292
+ # Preset configurations
293
+ config = hdiffpatch.TampConfig.fast()
294
+ config = hdiffpatch.TampConfig.balanced()
295
+ config = hdiffpatch.TampConfig.best_compression()
296
+ config = hdiffpatch.TampConfig.minimal_memory()
297
+ ```
298
+
299
+ **Parameters:**
300
+
301
+ * `window` (8-15): Window size as power of 2
302
+
303
+ ### Exceptions
304
+
305
+ ```python
306
+ hdiffpatch.HDiffPatchError
307
+ ```
308
+
309
+ ## Compression Performance
310
+
311
+ Different compression algorithms offer trade-offs between compression ratio and speed:
312
+
313
+ * **`zlib`**: Good balance of speed and compression. Very common.
314
+ * **`zstd`**: Fast compression with good ratios.
315
+ * **`lzma`/`lzma2`**: Very high compression ratios, slower.
316
+ * **`bzip2`**: Good compression, moderate speed
317
+ * **`tamp`**: Embedded-friendly compression, minimal memory usage.
318
+
319
+ ### Basic Compression Comparison
320
+
321
+ ```python
322
+ import hdiffpatch
323
+
324
+ # Large repetitive data
325
+ old_data = b"A" * 10000 + b"B" * 10000
326
+ new_data = b"A" * 10000 + b"C" * 10000
327
+
328
+ # Compare compression effectiveness
329
+ for compression in ["none", "zlib", "zstd", "lzma", "bzip2", "tamp"]:
330
+ diff = hdiffpatch.diff(old_data, new_data, compression=compression)
331
+ print(f"{compression}: {len(diff)} bytes")
332
+ ```
333
+
334
+ ### Advanced Configuration Comparison
335
+
336
+ ```python
337
+ import hdiffpatch
338
+
339
+ # Compare different configuration approaches
340
+ configs = {
341
+ "zstd_fast": hdiffpatch.ZStdConfig.fast(),
342
+ "zstd_best": hdiffpatch.ZStdConfig.best_compression(),
343
+ "zlib_balanced": hdiffpatch.ZlibConfig.balanced(),
344
+ "lzma2_custom": hdiffpatch.Lzma2Config(level=6, window=20, thread_num=4),
345
+ }
346
+
347
+ for name, config in configs.items():
348
+ diff = hdiffpatch.diff(old_data, new_data, compression=config)
349
+ print(f"{name}: {len(diff)} bytes")
350
+ ```
351
+
352
+ ### Real-World Example: MicroPython Firmware
353
+
354
+ 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.
355
+
356
+ * [RPI_PICO-20241129-v1.24.1.uf2](https://micropython.org/resources/firmware/RPI_PICO-20241129-v1.24.1.uf2): 651 KB
357
+ * [RPI_PICO-20250415-v1.25.0.uf2](https://micropython.org/resources/firmware/RPI_PICO-20250415-v1.25.0.uf2): 652 KB
358
+
359
+ 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?"
360
+ To answer this question, we compare the size of the compressed patch to the compressed firmware.
361
+
362
+ | Algorithm | Size (HDiffPatch) | Size (firmware) | Improvement |
363
+ |-----------|-------------------|-----------------|-------------|
364
+ | none | 209.7 KB | 652.0 KB | 3.11x |
365
+ | tamp | 143.1 KB | 322.8 KB | 2.26x |
366
+ | zstd | 133.4 KB | 277.6 KB | 2.08x |
367
+ | zlib | 125.5 KB | 251.8 KB | 2.01x |
368
+ | bzip2 | 128.6 KB | 246.2 KB | 1.91x |
369
+ | lzma | 116.9 KB | 222.7 KB | 1.91x |
370
+
371
+ 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.
372
+
373
+ To reproduce these results:
374
+
375
+ ```bash
376
+ uv run python tools/micropython-binary-demo.py
377
+ ```
@@ -0,0 +1,19 @@
1
+ hdiffpatch/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ hdiffpatch/_base_config.py,sha256=WDY0Pl8bNRxFlA0AJtShmSkBr9nlUsQ-zKEAJjWBnrw,1657
3
+ hdiffpatch/_bzip2_config.py,sha256=6fMohYu5L3_AaMlxjZFhByxL8-JNvoPDcKNj-vZN0L8,2113
4
+ hdiffpatch/_c_extension.cp314-win_amd64.pyd,sha256=4aMSDAbITI84-bqdGrYmijj81zW2gp0rM2_VOB3LBYE,823296
5
+ hdiffpatch/_c_extension.pyi,sha256=cM56jgsEgPJHP_uEiwT74e6Cy6CjJeDbB_YFr5MIDuc,3345
6
+ hdiffpatch/_lzma_config.py,sha256=279rYp6DX_ZYq0TxZ3EsEbL2S2PwRuAJxFo9DFGT0oQ,6339
7
+ hdiffpatch/_tamp_config.py,sha256=MvZXbL6L9qIqTblxLVC3yw5hmK6X0bHhl40xlFe_MPw,2063
8
+ hdiffpatch/_zlib_config.py,sha256=-yBhc0mEj-j1_yAmDTWJ9mwa3fW1pimyFGyCcdRUyYY,5102
9
+ hdiffpatch/_zstd_config.py,sha256=4GWN-8mAOct1JDB6QLe1fX-QTfIrSRuuRxxQgnLTeMs,3238
10
+ hdiffpatch/__init__.py,sha256=PBIQLHNz30-Uae6XAJwW4wNkbTPq6isZDGioJWQH8qI,1630
11
+ hdiffpatch-1.1.0.dist-info/DELVEWHEEL,sha256=-jbqjRgQ-KwUcgGkYKs8BA0UEzvyNeKhvm4hbULgVW0,409
12
+ hdiffpatch-1.1.0.dist-info/METADATA,sha256=dmWChLJ8Y7R3r7xJVOxppUd1fb0d0iWIJF9F-dY5Wj8,12167
13
+ hdiffpatch-1.1.0.dist-info/RECORD,,
14
+ hdiffpatch-1.1.0.dist-info/scm_file_list.json,sha256=FJKC8I5Yw52AvZFZOtiJ3CR6Rzb5btBDNZyva_2Pzt4,153244
15
+ hdiffpatch-1.1.0.dist-info/scm_version.json,sha256=zDDmWuNX4epXvAyPPaE5WyRBD7sNnt0Fq7Uq5xj-VhM,168
16
+ hdiffpatch-1.1.0.dist-info/top_level.txt,sha256=IHwSCeRigLNb80MkH0f87HsMsvxktM_sC_H2Mg246tQ,11
17
+ hdiffpatch-1.1.0.dist-info/WHEEL,sha256=pIUzES5vCYHKBR6AfxXxDFyqjZ9ckCheusePfq1FYSo,101
18
+ hdiffpatch-1.1.0.dist-info/licenses/LICENSE,sha256=NlaN0UJZDwQsLaQ4kMTmn_Q4BogQGPxRbjlWM0D1SSI,11558
19
+ hdiffpatch.libs/msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll,sha256=pMIim9wqKmMKzcCVtNhgCOXD47x3cxdDVPPaT1vrnN4,575056
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: false
4
+ Tag: cp314-cp314-win_amd64
5
+