# HDiffPatch
**Repository Path**: zhihaomeng/HDiffPatch
## Basic Information
- **Project Name**: HDiffPatch
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-01-17
- **Last Updated**: 2024-06-01
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# [HDiffPatch](https://github.com/sisong/HDiffPatch)
[](https://github.com/sisong/HDiffPatch/releases)
[](https://github.com/sisong/HDiffPatch/blob/master/LICENSE)
[](https://github.com/sisong/HDiffPatch/pulls)
[](https://github.com/sisong/HDiffPatch/issues)
[](https://github.com/sisong/HDiffPatch/actions?query=workflow%3Aci+branch%3Amaster)
[](https://ci.appveyor.com/project/sisong/hdiffpatch/branch/master)
a C\C++ library and command-line tools for Diff & Patch between binary files or directories(folder); cross-platform; run fast; create small delta/differential; support large files and limit memory requires when diff & patch.
( NOTE: This library does not deal with file metadata, such as file last wirte time, permissions, link file, etc... To this library, a file is just as a stream of bytes; You can extend this library or use other tools. )
( update Android Apk? Jar or Zip file diff & patch? try [ApkDiffPatch](https://github.com/sisong/ApkDiffPatch)!
but ApkDiffPath can't be used in the Android app store, because it requires re-signing apk;
[sfpatcher](https://github.com/sisong/sfpatcher) (like [archive-patcher](https://github.com/google/archive-patcher)), is designed for Android app store, but patch is much faster than archive-patcher. )
---
## Releases/Binaries
[Download from latest release](https://github.com/sisong/HDiffPatch/releases) : Command line app for Windows, Linux, MacOS; and .so patch lib for Android.
( release files build by projects in path `HDiffPatch/builds` )
## Builds
`$ cd
/HDiffPatch`
if on linux or macos, try :
`$ make LZMA=0 ZSTD=0 MD5=0`
or
```
$ git clone https://github.com/sisong/lzma.git ../lzma
$ git clone https://github.com/facebook/zstd.git ../zstd
$ git clone https://github.com/sisong/libmd5.git ../libmd5
$ make
```
if on windows, befor compile `builds/vc/HDiffPatch.sln` by `Visual Studio`
```
$ git clone https://github.com/sisong/lzma.git ../lzma
$ git clone https://github.com/facebook/zstd.git ../zstd
$ git clone https://github.com/sisong/libmd5.git ../libmd5
$ git clone https://github.com/sisong/zlib.git ../zlib
$ git clone https://github.com/sisong/bzip2.git ../bzip2
```
build libhpatchz.so for Android:
* install Android NDK
* `$ cd /HDiffPatch/builds/android_ndk_jni_mk`
* `$ build_libs.sh` (or `$ build_libs.bat` on windows, then got \*.so files)
* import file `com/github/sisong/HPatch.java` (from `HDiffPatch/builds/android_ndk_jni_mk/java/`) & .so files, java code can call the patch function in libhpatchz.so
---
## command line usage Chinese version: [命令行使用说明中文版](README_cmdline_cn.md)
## **diff** command line usage:
diff usage: **hdiffz** [options] **oldPath newPath outDiffFile**
compress usage: **hdiffz** [-c-...] **"" newPath outDiffFile**
test usage: **hdiffz** -t **oldPath newPath testDiffFile**
resave usage: **hdiffz** [-c-...] **diffFile outDiffFile**
get manifest: **hdiffz** [-g#...] [-C-checksumType] **inputPath -M#outManifestTxtFile**
manifest diff: **hdiffz** [options] **-M-old#oldManifestFile -M-new#newManifestFile oldPath newPath outDiffFile**
```
oldPath newPath inputPath can be file or directory(folder),
oldPath can empty, and input parameter ""
memory options:
-m[-matchScore]
DEFAULT; all file load into Memory; best diffFileSize;
requires (newFileSize+ oldFileSize*5(or *9 when oldFileSize>=2GB))+O(1)
bytes of memory;
matchScore>=0, DEFAULT -m-6, recommended bin: 0--4 text: 4--9 etc...
-s[-matchBlockSize]
all file load as Stream; fast;
requires O(oldFileSize*16/matchBlockSize+matchBlockSize*5)bytes of memory;
matchBlockSize>=4, DEFAULT -s-64, recommended 16,32,48,1k,64k,1m etc...
special options:
-block[-fastMatchBlockSize]
must run with -m;
set is use fast block match befor slow match, DEFAULT false;
fastMatchBlockSize>=4, DEFAULT 4k, recommended 256,1k,64k,1m etc...;
if newData similar to oldData then diff speed++ & diff memory--,
but small possibility outDiffFile's size+
-cache
must run with -m;
set is use a big cache for slow match, DEFAULT false;
if newData not similar to oldData then diff speed++,
big cache max used O(oldFileSize) memory, and build slow(diff speed--)
-SD[-stepSize]
create single compressed diffData, only need one decompress buffer
when patch, and support step by step patching when step by step downloading!
stepSize>=(1024*4), DEFAULT -SD-256k, recommended 64k,2m etc...
-BSD
create diffFile compatible with bsdiff, unsupport input directory(folder).
-p-parallelThreadNumber
if parallelThreadNumber>1 then open multi-thread Parallel mode;
DEFAULT -p-4; requires more memory!
-c-compressType[-compressLevel]
set outDiffFile Compress type & level, DEFAULT uncompress;
for resave diffFile,recompress diffFile to outDiffFile by new set;
support compress type & level:
(re. https://github.com/sisong/lzbench/blob/master/lzbench171_sorted.md )
-c-zlib[-{1..9}] DEFAULT level 9
-c-pzlib[-{1..9}] DEFAULT level 6
support run by multi-thread parallel, fast!
WARNING: code not compatible with it compressed by -c-zlib!
and code size may be larger than if it compressed by -c-zlib.
-c-bzip2[-{1..9}] (or -bz2) DEFAULT level 9
-c-pbzip2[-{1..9}] (or -pbz2) DEFAULT level 8
support run by multi-thread parallel, fast!
WARNING: code not compatible with it compressed by -c-bzip2!
and code size may be larger than if it compressed by -c-bzip2.
-c-lzma[-{0..9}[-dictSize]] DEFAULT level 7
dictSize can like 4096 or 4k or 4m or 128m etc..., DEFAULT 8m
support run by 2-thread parallel.
-c-lzma2[-{0..9}[-dictSize]] DEFAULT level 7
dictSize can like 4096 or 4k or 4m or 128m etc..., DEFAULT 8m
support run by multi-thread parallel, fast!
WARNING: code not compatible with it compressed by -c-lzma!
-c-zstd[-{0..22}[-dictBits]] DEFAULT level 20
dictBits can 10--31, DEFAULT 24.
support run by multi-thread parallel, fast!
-C-checksumType
set outDiffFile Checksum type for directory diff, DEFAULT -C-fadler64;
support checksum type:
-C-no no checksum
-C-crc32
-C-fadler64 DEFAULT
-C-md5
-n-maxOpenFileNumber
limit Number of open files at same time when stream directory diff;
maxOpenFileNumber>=8, DEFAULT -n-48, the best limit value by different
operating system.
-g#ignorePath[#ignorePath#...]
set iGnore path list when Directory Diff; ignore path list such as:
#.DS_Store#desktop.ini#*thumbs*.db#.git*#.svn/#cache_*/00*11/*.tmp
# means separator between names; (if char # in name, need write #: )
* means can match any chars in name; (if char * in name, need write *: );
/ at the end of name means must match directory;
-g-old#ignorePath[#ignorePath#...]
set iGnore path list in oldPath when Directory Diff;
if oldFile can be changed, need add it in old ignore list;
-g-new#ignorePath[#ignorePath#...]
set iGnore path list in newPath when Directory Diff;
in general, new ignore list should is empty;
-M#outManifestTxtFile
create a Manifest file for inputPath; it is a text file, saved infos of
all files and directoriy list in inputPath; this file while be used in
manifest diff, support re-checksum data by manifest diff;
can be used to protect historical versions be modified!
-M-old#oldManifestFile
oldManifestFile is created from oldPath; if no oldPath not need -M-old;
-M-new#newManifestFile
newManifestFile is created from newPath;
-D force run Directory diff between two files; DEFAULT (no -D) run
directory diff need oldPath or newPath is directory.
-d Diff only, do't run patch check, DEFAULT run patch check.
-t Test only, run patch check, patch(oldPath,testDiffFile)==newPath ?
-f Force overwrite, ignore write path already exists;
DEFAULT (no -f) not overwrite and then return error;
if used -f and write path is exist directory, will always return error.
--patch
swap to hpatchz mode.
-h or -?
output Help info (this usage).
-v output Version info.
```
## **patch** command line usage:
patch usage: **hpatchz** [options] **oldPath diffFile outNewPath**
uncompress usage: **hpatchz** [options] **"" diffFile outNewPath**
create SFX: **hpatchz** [-X-exe#selfExecuteFile] **diffFile -X#outSelfExtractArchive**
run SFX: **selfExtractArchive** [options] **oldPath -X outNewPath**
extract SFX: **selfExtractArchive** (same as: selfExtractArchive -f "" -X "./")
```
memory options:
-s[-cacheSize]
DEFAULT -s-64m; oldPath loaded as Stream;
cacheSize can like 262144 or 256k or 512m or 2g etc....
requires (cacheSize + 4*decompress buffer size)+O(1) bytes of memory.
if diffFile is single compressed diffData, then requires
(cacheSize+ stepSize + 1*decompress buffer size)+O(1) bytes of memory;
see: hdiffz -SD-stepSize option.
if diffFile is bsdiff diffData, then requires
(cacheSize + 3*decompress buffer size)+O(1) bytes of memory;
see: hdiffz -BSD option.
-m oldPath all loaded into Memory;
requires (oldFileSize + 4*decompress buffer size)+O(1) bytes of memory.
if diffFile is single compressed diffData, then requires
(oldFileSize+ stepSize + 1*decompress buffer size)+O(1) bytes of memory.
if diffFile is bsdiff diffData, then requires
(oldFileSize + 3*decompress buffer size)+O(1) bytes of memory.
special options:
-C-checksumSets
set Checksum data for directory patch, DEFAULT -C-new-copy;
checksumSets support (can choose multiple):
-C-diff checksum diffFile;
-C-old checksum old reference files;
-C-new checksum new files edited from old reference files;
-C-copy checksum new files copy from old same files;
-C-no no checksum;
-C-all same as: -C-diff-old-new-copy;
-n-maxOpenFileNumber
limit Number of open files at same time when stream directory patch;
maxOpenFileNumber>=8, DEFAULT -n-24, the best limit value by different
operating system.
-f Force overwrite, ignore write path already exists;
DEFAULT (no -f) not overwrite and then return error;
support oldPath outNewPath same path!(patch to tempPath and overwrite old)
if used -f and outNewPath is exist file:
if patch output file, will overwrite;
if patch output directory, will always return error;
if used -f and outNewPath is exist directory:
if patch output file, will always return error;
if patch output directory, will overwrite, but not delete
needless existing files in directory.
-h or -?
output Help info (this usage).
-v output Version info.
```
---
## library API usage:
all **diff**&**patch** function in file: `libHDiffPatch/HDiff/diff.h` & `libHDiffPatch/HPatch/patch.h`
**dir_diff()** & **dir patch** in: `dirDiffPatch/dir_diff/dir_diff.h` & `dirDiffPatch/dir_patch/dir_patch.h`
### manual:
* **create diff**(in newData,in oldData,out diffData);
release the diffData for update oldData.
* **patch**(out newData,in oldData,in diffData);
ok , get the newData.
### v1 API, uncompressed diffData:
* **create_diff()**
* **patch()**
* **patch_stream()**
* **patch_stream_with_cache()**
### v2 API, compressed diffData:
* **create_compressed_diff()**
* **create_compressed_diff_stream()**
* **resave_compressed_diff()**
* **patch_decompress()**
* **patch_decompress_with_cache()**
* **patch_decompress_mem()**
### v3 API, **diff**&**patch** between directories(folder):
* **dir_diff()**
* **TDirPatcher_\*()** functions with **struct TDirPatcher**
### v4 API, single compressed diffData:
* **create_single_compressed_diff()**
* **create_single_compressed_diff_stream()**
* **resave_single_compressed_diff()**
* **patch_single_stream()**
* **patch_single_stream_mem()**
* **patch_single_compressed_diff()**
* **patch_single_stream_diff()**
v4.1 API, bsdiff wrapper:
* **create_bsdiff()**
* **bspatch_with_cache()**
---
## HDiffPatch vs BsDiff & xdelta:
case list:
| |newFile <-- oldFile|newSize|oldSize|
|----:|:----|----:|----:|
|1|apache-maven-2.2.1-src.tar <-- apache-maven-2.0.11-src.tar|5150720|4689920|
|2|httpd_2.4.4-netware-bin.tar <-- httpd_2.2.24-netware-bin.tar|22612480|17059328|
|3|httpd-2.4.4-src.tar <-- httpd-2.2.24-src.tar|31809536|37365760|
|4|Firefox-21.0-mac-en-US.app.tar <-- Firefox-20.0-mac-en-US.app.tar|98740736|96340480|
|5|emacs-24.3.tar <-- emacs-23.4.tar|185528320|166420480|
|6|eclipse-java-juno-SR2-macosx-cocoa-x86_64.tar <-- eclipse-java-juno-SR2-macosx-cocoa.tar|178595840|178800640|
|7|gcc-4.8.0.tar <-- gcc-4.7.0.tar|552775680|526745600|
**test PC**: Windows11, CPU Ryzen 5800H, SSD Disk, Memroy 8G*2 DDR4 3200MHz
**Program version**: HDiffPatch4.1, BsDiff4.3, xdelta3.1
**test Program**:
**xdelta** diff with `-e -n -f -s {old} {new} {pat}`
**xdelta** patch with `-d -f -s {old} {pat} {new}`
**xdelta -B** diff with `-B {oldSize} -e -n -f -s {old} {new} {pat}`
**xdelta -B** patch with `-B {oldSize} -d -f -s {old} {pat} {new}`
**bsdiff** diff with `{old} {new} {pat}`
**bspatch** patch with `{old} {new} {pat}`
**hdiffz -BSD** diff with `-m-6 -BSD -block -d -f -p-1 {old} {new} {pat}`
**hdiffz -bzip2** diff with `-m-6 -SD -block -d -f -p-1 -c-bzip2-9 {old} {new} {pat}`
**hdiffz -zlib** diff with `-m-6 -SD -block -d -f -p-1 -c-zlib-9 {old} {new} {pat}`
**hdiffz -lzma2** diff with `-m-6 -SD -block -d -f -p-1 -c-lzma-9-16m {old} {new} {pat}`
**hdiffz -zstd** diff with `-m-6 -SD -block -d -f -p-1 -c-zstd-20-24 {old} {new} {pat}`
**hdiffz -s -zlib** diff with `-s-64 -SD -d -f -p-1 -c-zlib-9 {old} {new} {pat}`
**hdiffz -s -lzma2** diff with `-s-64 -SD -d -f -p-1 -c-lzma-9-16m {old} {new} {pat}`
**hdiffz -s -zstd** diff with `-s-64 -SD -d -f -p-1 -c-zstd-17-24 {old} {new} {pat}`
**hpatchz** patch with `-s-256k -f {old} {pat} {new}`
**test result average**:
|Program|compress|diff mem(MB)|speed(MB/S)|patch mem(MB)|max mem(MB)|speed(MB/S)|
|:----|----:|----:|----:|----:|----:|----:|
|bzip2|31.76%|
|lzma2|28.47%|
|xdelta3 |12.18%|212|6.9|84|98|53.8|
|xdelta3 -B|7.35%|442|19.5|197|534|174.1|
|bsdiff |6.63%|1263|2.3|298|1043|119.7|
|hdiffz -BSD |5.67%|596|15.8|12|14|130.8|
|hdiffz -bzip2|5.77%|596|17.4|7|7|208.0|
|hdiffz -zlib|5.93%|596|17.4|4|4|374.2|
|hdiffz -lzma2|5.02%|597|13.2|12|20|321.4|
|hdiffz -zstd|5.22%|660|12.3|13|20|382.6|
|hdiffz -s -zlib|8.13%|44|40.7|4|4|436.3|
|hdiffz -s -lzma2 |6.39%|119|17.4|13|20|337.3|
|hdiffz -s -zstd|6.82%|73|24.1|13|20|464.0|
## input Apk Files for test:
case list:
| |newFile <-- oldFile|newSize|oldSize|
|----:|:----|----:|----:|
|1|12306_5.2.11.apk <-- 12306_5.1.2.apk| 61120025|66209244|
|2|alipay10.1.99.apk <-- alipay10.1.95.apk|94178674|90951351|
|3|alipay10.2.0.apk <-- alipay10.1.99.apk|95803005|94178674|
|4|baidumaps10.25.0.apk <-- baidumaps10.24.12.apk|95539893|104527191|
|5|baidumaps10.25.5.apk <-- baidumaps10.25.0.apk|95526276|95539893|
|6|bilibili6.15.0.apk <-- bilibili6.14.0.apk|74783182|72067209|
|7|chrome-64-0-3282-137.apk <-- chrome-64-0-3282-123.apk|43879588|43879588|
|8|chrome-65-0-3325-109.apk <-- chrome-64-0-3282-137.apk|43592997|43879588|
|9|didi6.0.2.apk <-- didi6.0.0.apk|100866981|91462767|
|10|firefox68.10.0.apk <-- firefox68.9.0.apk|43543846|43531470|
|11|firefox68.10.1.apk <-- firefox68.10.0.apk|43542786|43543846|
|12|google-maps-9-71-0.apk <-- google-maps-9-70-0.apk|50568872|51304768|
|13|google-maps-9-72-0.apk <-- google-maps-9-71-0.apk|54342938|50568872|
|14|jd9.0.0.apk <-- jd8.5.12.apk|96891703|94233891|
|15|jd9.0.8.apk <-- jd9.0.0.apk|97329322|96891703|
|16|jinianbeigu2_1.12.4.apk <-- jinianbeigu2_1.12.3.apk|171611658|159691189|
|17|lushichuanshuo19.4.71003.apk <-- lushichuanshuo19.2.69054.apk|93799693|93442621|
|18|meituan10.9.401.apk <-- meituan10.9.203.apk|88956726|89384406|
|19|minecraft1.17.30.apk <-- minecraft1.17.20.apk|373025314|370324338|
|20|minecraft1.18.10.apk <-- minecraft1.17.30.apk|401075178|373025314|
|21|popcap.pvz2_2.4.84.1010.apk <-- popcap.pvz2_2.4.84.1009.apk|387572492|386842079|
|22|supercell.clashofclans13.369.3.apk <-- supercell.clashofclans13.180.18.apk|152896934|149011539|
|23|tangmumaopaoku4.8.0.971.apk <-- tangmumaopaoku4.6.0.913.apk|105486308|104732413|
|24|taobao9.8.0.apk <-- taobao9.7.2.apk|178734456|176964070|
|25|taobao9.9.1.apk <-- taobao9.8.0.apk|184437315|178734456|
|26|tiktok11.5.0.apk <-- tiktok11.3.0.apk|88544106|87075000|
|27|translate6.9.0.apk <-- translate6.8.0.apk|28171978|28795243|
|28|translate6.9.1.apk <-- translate6.9.0.apk|31290990|28171978|
|29|weixin7.0.15.apk <-- weixin7.0.14.apk|148405483|147695111|
|30|weixin7.0.16.apk <-- weixin7.0.15.apk|158906413|148405483|
|31|wps12.5.2.apk <-- wps12.5.1.apk|51293286|51136905|
|32|yuanshichuanqi1.3.608.apk <-- yuanshichuanqi1.3.607.apk|192578139|192577253|
**changed test Program**:
**hdiffz ...** `-m-6` changed to `-m-1 -cache`
**hdiffz ...** `-s-64` changed to `-s-16`
**hdiffz** added diff with `-m-1 -cache -SD -block -d -f -p-1 {old} {new} {pat}`
**hdiffz -s** added diff with `-s-16 -SD -d -f -p-1 {old} {new} {pat}`
**sfpatcher -1 -zstd** diff with `-o-1 -c-zstd-21-24 -p-1 -block -cache -d {old} {new} {pat}`, patch with `-lp -p-8 {old} {pat} {new}`
( [sfpatcher](https://github.com/sisong/sfpatcher) optimized diff&patch between apk files )
**test result average**:
|Program|compress|diff mem(MB)|speed(MB/S)|patch mem(MB)|max mem(MB)|speed(MB/S)|
|:----|----:|----:|----:|----:|----:|----:|
|xdelta3 |59.92%|228|2.8|100|101|157.4|
|xdelta3 -B|59.51%|440|3.0|206|549|154.6|
|bsdiff |59.76%|1035|1.0|244|752|41.1|
|hdiffz -BSD |59.50%|524|5.5|13|14|42.6|
|hdiffz -bzip2|59.54%|524|5.6|7|7|55.7|
|hdiffz|59.87%|524|7.2|4|4|658.4|
|hdiffz -zlib|59.10%|524|6.7|4|4|504.9|
|hdiffz -lzma2|58.67%|537|3.5|20|20|279.8|
|hdiffz -zstd|58.74%|536|4.1|20|21|596.5|
|hdiffz -s|60.46%|133|32.5|4|4|679.9|
|hdiffz -s -zlib|59.52%|133|23.6|4|4|555.8|
|hdiffz -s -lzma2 |59.02%|210|5.5|20|20|268.4|
|hdiffz -s -zstd|59.26%|139|8.6|20|20|619.2|
|sfpatcher -1 -zstd|31.70%|773|2.7|24|29|399.8|
---
## Contact
housisong@hotmail.com