english | 中文版
hsynz is a library for delta update using sync algorithm, like zsync.
rsync over http(s); implement the sync algorithm on client side, and server side only need http(s) cdn. support compressor zstd & libdeflate & zlib, support large file & directory(folder), support multi-thread.
hsynz defines its own file format (.hsyni and .hsynz files), and this library is also compatible with the file format of zsync (including apply and create .zsync and their .gz files).
Recommended scenarios: Very large number of older versions or where older versions are not available (not saved or modified, etc.) so that all deltas cannot be calculated in advance.
The server uses hsync_make to process the latest version of the data once, generating a summary info file(hsyni) of the new version of the data in chunks, and optionally compressing the new version of the data in chunks to get the release file(hsynz), which would be the hsynz equivalent if the new version of the original file were not compressed.
The client first downloads the hsyni file from the server or another user's share, calculates the updated blocks it needs to download based on its old version, and learns the location of these blocks in hsynz based on the information in hsyni, selects a communication method to download them on demand from the server's hsynz file, and merges the downloaded blocks with the existing data locally to get the latest version of the data.
hsync_demo provides a test client demo for local file testing.
hsync_http provides a download client demo with http(s) support for sync update from a server that provides an http(s) file download service(e.g CDN, support HTTP/1.1 multi range Requests).
Tip: You can also customise other communication methods for sync.
additional, if you have the new file locally & not the old file, but can get a hash certificate file(.hsyni) of the old file, you can also create a hpatchz format patch file(usage scenario like [rsync]); see the demo cmdline hsign_diff.
Compare with zsync
- In addition to supporting source and target as files, support is also provided for directories(folders).
- In addition to supporting compressed release package by zlib; also supported libdeflate & zstd compressor, providing better compression ratio, i.e. smaller downloaded patch package.
- The server-side make support multi-threaded parallel acceleration.
- The client-side diff speed has been optimized, and also support multi-threaded parallel acceleration.
Download from latest release : Command line app for Windows, Linux, MacOS; and .so lib for Android.
( release files build by projects in path hsynz/builds
)
$ cd <dir>
$ git clone --recursive https://github.com/sisong/hsynz.git
$ cd hsynz
$ make
$ cd <dir>
$ git clone --recursive https://github.com/sisong/hsynz.git
build hsynz/builds/vc/hsynz.sln
with Visual Studio
- install Android NDK
$ cd <dir>/hsynz/builds/android_ndk_jni_mk
$ build_libs_static.sh
(or$ build_libs_static.bat
on windows, then got *.so files)- import file
com/github/sisong/hsynz.java
(fromhsynz/builds/android_ndk_jni_mk/java/
) & .so files, java code can call the sync patch function in libhsynz.so
hsync_make: [options] newDataPath out_hsyni_file [out_hsynz_file]
newDataPath can be file or directory(folder),
if newDataPath is a file & no -c-... option, out_hsynz_file can empty.
options:
-s-matchBlockSize
matchBlockSize>=128, DEFAULT -s-2k, recommended 1024,4k,...
-b-safeBit
set allow patch fail hash clash probability: 1/2^safeBit;
safeBit>=14, DEFAULT -b-24, recommended 20,32...
-p-parallelThreadNumber
DEFAULT -p-4;
if parallelThreadNumber>1 then open multi-thread Parallel mode;
-c-compressType[-compressLevel]
set out_hsynz_file Compress type & level, DEFAULT uncompress;
support compress type & level:
-c-zlib[-{1..9}[-dictBits]] DEFAULT level 9
dictBits can 9--15, DEFAULT 15.
-c-gzip[-{1..9}[-dictBits]] DEFAULT level 9
dictBits can 9--15, DEFAULT 15.
compress by zlib, out_hsynz_file is .gz file format.
-c-ldef[-{1..12}] DEFAULT level 12 (dictBits always 15).
compress by libdeflate, compatible with zlib's deflate encoding.
-c-lgzip[-{1..12}] DEFAULT level 12 (dictBits always 15)
compress by libdeflate, out_hsynz_file is .gz file format.
-c-zstd[-{10..22}[-dictBits]] DEFAULT level 21
dictBits can 15--30, DEFAULT 24.
-C-checksumType
set strong Checksum type for block data, DEFAULT -C-xxh128;
support checksum type:
-C-xxh128
-C-md5
-C-sha512
-C-sha256
-C-crc32
WARNING: crc32 is not strong & secure enough!
-zsync[#KeY#=...#ValuE#=...[#KeY#=...#ValuE#=...]]
create out_hsyni_file(.zsync file format) or out_hsynz_file(.gz file
format) compatible with zsync.
checksum default used sha1 & md4, not need set -C
-s-matchBlockSize size must 2^N; if used -c-gzip or -c-lgzip (out
.gz file), recommend set 4k at most, >=8k may fail.
key-value string pairs will be write in out_hsyni_file; if needed,
you can set Filename,Z-Filename,URL,Z-URL,MTime,Recompress,...
zsync project https://zsync.moria.org.uk
-n-maxOpenFileNumber
limit Number of open files at same time when newDataPath is directory;
maxOpenFileNumber>=8, DEFAULT -n-48, the best limit value by different
operating system.
-g#ignorePath[#ignorePath#...]
set iGnore path list in newDataPath directory; 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;
-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 hsync_demo mode.
-v output Version info.
-h or -?
output Help info (this usage).
download : [options] -dl#hsyni_file_url hsyni_file
local diff: [options] oldPath hsyni_file hsynz_file_url -diff#outDiffFile
local patch: [options] oldPath hsyni_file -patch#diffFile outNewPath
sync infos: [options] oldPath hsyni_file [-diffi#cacheTempFile]
sync patch: [options] oldPath [-dl#hsyni_file_url] hsyni_file hsynz_file_url [-diffi#cacheTempFile] outNewPath
oldPath can be file or directory(folder),
if oldPath is empty input parameter ""
options:
-dl#hsyni_file_url
download hsyni_file from hsyni_file_url befor sync patch;
-diff#outDiffFile
create diffFile from ranges of hsynz_file_url befor local patch;
-diffi#cacheTempFile
saving diffInfo to cache file for optimize speed when continue sync patch;
-patch#diffFile
local patch(oldPath+diffFile) to outNewPath;
-U
set hsynz_file_url is the original file before compress, please ignore the
compress info in hsyni_file and directly access the data of hsynz file
without decompress.
-cdl-{0|1} or -cdl-{off|on}
continue download data from breakpoint;
DEFAULT -cdl-1 opened, need set -cdl-0 or -cdl-off to close continue mode;
-rdl-retryDownloadNumber
number of auto retry connection, when network disconnected while downloading;
DEFAULT -rdl-0 retry closed; recommended 5,1k,1g,...
-r-stepRangeNumber
DEFAULT -r-32, recommended 16,20,...
limit the maximum number of .hsynz data ranges that can be downloaded
in a single request step;
if http(s) server not support multi-ranges request, must set -r-1
-p-parallelThreadNumber
DEFAULT -p-4;
if parallelThreadNumber>1 then open multi-thread Parallel mode;
NOTE: now download data always used single-thread.
-n-maxOpenFileNumber
limit Number of open files at same time when oldPath is directory;
maxOpenFileNumber>=8, DEFAULT -n-24, the best limit value by different
operating system.
-g#ignorePath[#ignorePath#...]
set iGnore path list in oldPath directory; 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;
-f Force overwrite, ignore write path already exists;
DEFAULT (no -f) not overwrite and then return error;
not support oldPath outNewPath same path!
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.
-v output Version info.
-h or -?
output Help info (this usage).
This cmdline is used for local sync tests, replacing the actual URL remote file with local file, see the hsync_http usage.
case list(download from OneDrive):
newFile <-- oldFile | newSize | oldSize | |
---|---|---|---|
1 | 7-Zip_22.01.win.tar <-- 7-Zip_21.07.win.tar | 5908992 | 5748224 |
2 | Chrome_107.0.5304.122-x64-Stable.win.tar <-- 106.0.5249.119 | 278658560 | 273026560 |
3 | cpu-z_2.03-en.win.tar <-- cpu-z_2.02-en.win.tar | 8718336 | 8643072 |
4 | curl_7.86.0.src.tar <-- curl_7.85.0.src.tar | 26275840 | 26030080 |
5 | douyin_1.5.1.mac.tar <-- douyin_1.4.2.mac.tar | 407940608 | 407642624 |
6 | Emacs_28.2-universal.mac.tar <-- Emacs_27.2-3-universal.mac.tar | 196380160 | 257496064 |
7 | FFmpeg-n_5.1.2.src.tar <-- FFmpeg-n_4.4.3.src.tar | 80527360 | 76154880 |
8 | gcc_12.2.0.src.tar <-- gcc_11.3.0.src.tar | 865884160 | 824309760 |
9 | git_2.33.0-intel-universal-mavericks.mac.tar <-- 2.31.0 | 73302528 | 70990848 |
10 | go_1.19.3.linux-amd64.tar <-- go_1.19.2.linux-amd64.tar | 468835840 | 468796416 |
11 | jdk_x64_mac_openj9_16.0.1_9_openj9-0.26.0.tar <-- 9_15.0.2_7-0.24.0 | 363765760 | 327188480 |
12 | jre_1.8.0_351-linux-x64.tar <-- jre_1.8.0_311-linux-x64.tar | 267796480 | 257996800 |
13 | linux_5.19.9.src.tar <-- linux_5.15.80.src.tar | 1269637120 | 1138933760 |
14 | Minecraft_175.win.tar <-- Minecraft_172.win.tar | 166643200 | 180084736 |
15 | OpenOffice_4.1.13.mac.tar <-- OpenOffice_4.1.10.mac.tar | 408364032 | 408336896 |
16 | postgresql_15.1.src.tar <-- postgresql_14.6.src.tar | 151787520 | 147660800 |
17 | QQ_9.6.9.win.tar <-- QQ_9.6.8.win.tar | 465045504 | 464837120 |
18 | tensorflow_2.10.1.src.tar <-- tensorflow_2.8.4.src.tar | 275548160 | 259246080 |
19 | VSCode-win32-x64_1.73.1.tar <-- VSCode-win32-x64_1.69.2.tar | 364025856 | 340256768 |
20 | WeChat_3.8.0.41.win.tar <-- WeChat_3.8.0.33.win.tar | 505876992 | 505018368 |
test PC: Windows11, CPU R9-7945HX, SSD PCIe4.0x4 4T, DDR5 5200MHz 32Gx2
Program version: hsynz 1.3.0, zsync 0.6.3
test Program:
zsync run make with zsyncmake -b 2048 -o {out_newi} {new}
,
client sync diff&patch by zsync -i {old} -o {out_new} {newi}
(all files are local)
zsync -z run make with zsyncmake -b 2048 -z -u {new.gz} -o {out_newi} {new}
hsynz run make with hsync_make -s-2k {new} {out_newi} [{-c-?} {out_newz}]
,
run make with -zsync
means create compatible zsync's file format,
client sync diff&patch by hsync_demo {old} {newi} {newz} {out_new}
(all files are local)
hsynz p1 run make without compressor & out_newz , add -p-1
hsynz p8 run make without compressor & out_newz , add -p-8
hsynz p1 zlib run make with -p-1 -c-zlib-9
(run hsync_demo
with -p-1
)
hsynz p8 zlib run make with -p-8 -c-zlib-9
(run hsync_demo
with -p-8
)
hsynz p1 gz run make with -p-1 -c-gzip-9
(run hsync_demo
with -p-1
)
hsynz p8 gz run make with -p-8 -c-gzip-9
(run hsync_demo
with -p-8
)
hsynz p1 ldef run make with -p-1 -c-ldef-12
(run hsync_demo
with -p-1
)
hsynz p8 ldef run make with -p-8 -c-ldef-12
(run hsync_demo
with -p-8
)
hsynz p1 lgz run make with -p-1 -c-lgzip-12
(run hsync_demo
with -p-1
)
hsynz p8 lgz run make with -p-8 -c-lgzip-12
(run hsync_demo
with -p-8
)
hsynz p1 zstd run make with -p-1 -c-zstd-21-24
(run hsync_demo
with -p-1
)
hsynz p8 zstd run make with -p-8 -c-zstd-21-24
(run hsync_demo
with -p-8
)
additional tests added hsign_diff, which can create hpatchz-compatible patch files using only old data's .hsyni file and the new data.
test result average:
make | mem | speed | patch | compress | mem | max mem | speed |
---|---|---|---|---|---|---|---|
zsyncmake | 1M | 381.6MB/s | hsynz p1 | 45.74% | 6M | 18M | 181MB/s |
zsyncmake | 1M | 381.6MB/s | hsynz p8 | 45.74% | 13M | 27M | 276MB/s |
zsyncmake | 1M | 383.4MB/s | zsync |
45.75% | 9M | 38M | 122MB/s |
zsyncmake -z | 1M | 14.8MB/s | hsynz p1 | 16.56% | 6M | 21M | 169MB/s |
zsyncmake -z | 1M | 14.8MB/s | hsynz p8 | 16.56% | 13M | 29M | 245MB/s |
zsyncmake -z | 1M | 14.9MB/s | zsync |
16.58% | 13M | 52M | 71MB/s |
hsync_make p1 -zsync | 4M | 430.9MB/s | hsynz p1 | 45.39% | 6M | 19M | 187MB/s |
hsync_make p8 -zsync | 14M | 591.0MB/s | hsynz p8 | 45.39% | 13M | 27M | 275MB/s |
hsync_make p8 -zsync | 14M | 590.9MB/s | zsync |
45.39% | 9M | 37M | 119MB/s |
hsync_make p1 gz -zsync | 6M | 16.9MB/s | hsynz p1 | 16.59% | 6M | 21M | 174MB/s |
hsync_make p8 gz -zsync | 29M | 101.8MB/s | hsynz p8 | 16.59% | 13M | 29M | 252MB/s |
hsync_make p8 gz -zsync | 29M | 101.8MB/s | zsync |
16.60% | 14M | 52M | 70MB/s |
hsync_make p1 lgz -zsync | 14M | 7.8MB/s | hsynz p1 | 16.21% | 6M | 21M | 176MB/s |
hsync_make p8 lgz -zsync | 95M | 40.3MB/s | hsynz p8 | 16.21% | 14M | 29M | 252MB/s |
hsync_make p8 lgz -zsync | 95M | 40.1MB/s | zsync |
16.22% | 14M | 52M | 70MB/s |
hsync_make p1 | 5M | 2179.7MB/s | hsynz p1 | 44.57% | 5M | 19M | 301MB/s |
hsync_make p8 | 12M | 3709.2MB/s | hsynz p8 | 44.57% | 13M | 27M | 478MB/s |
hsync_make p1 zlib | 7M | 17.5MB/s | hsynz p1 | 16.05% | 6M | 22M | 270MB/s |
hsync_make p8 zlib | 30M | 105.0MB/s | hsynz p8 | 16.05% | 13M | 29M | 407MB/s |
hsync_make p1 ldef | 15M | 8.0MB/s | hsynz p1 | 15.68% | 6M | 22M | 270MB/s |
hsync_make p8 ldef | 96M | 40.6MB/s | hsynz p8 | 15.68% | 13M | 29M | 405MB/s |
hsync_make p1 gz | 7M | 17.6MB/s | hsynz p1 | 16.12% | 6M | 22M | 271MB/s |
hsync_make p8 gz | 30M | 104.9MB/s | hsynz p8 | 16.12% | 13M | 29M | 408MB/s |
hsync_make p1 lgz | 15M | 7.9MB/s | hsynz p1 | 15.74% | 6M | 22M | 270MB/s |
hsync_make p8 lgz | 96M | 40.6MB/s | hsynz p8 | 15.74% | 13M | 29M | 405MB/s |
hsync_make p1 zstd | 532M | 1.8MB/s | hsynz p1 | 12.54% | 24M | 34M | 263MB/s |
hsync_make p8 zstd | 3353M | 8.1MB/s | hsynz p8 | 12.54% | 23M | 34M | 384MB/s |
hsign_diff p1 | 4M | 494.7MB/s | hpatchz | 43.13% | 3M | 4M | 2409MB/s |
hsign_diff p8 | 11M | 1412.3MB/s | hpatchz | 43.13% | 3M | 4M | 2422MB/s |
hsign_diff p1 zlib | 4M | 42.9MB/s | hpatchz | 14.78% | 4M | 4M | 926MB/s |
hsign_diff p8 zlib | 13M | 246.3MB/s | hpatchz | 14.79% | 3M | 4M | 927MB/s |
hsign_diff p1 ldef | 16M | 20.1MB/s | hpatchz | 14.38% | 3M | 4M | 920MB/s |
hsign_diff p8 ldef | 115M | 104.4MB/s | hpatchz | 14.38% | 3M | 4M | 917MB/s |
hsign_diff p1 zstd | 205M | 8.3MB/s | hpatchz | 11.17% | 17M | 21M | 1369MB/s |
hsign_diff p8 zstd | 1348M | 17.2MB/s | hpatchz | 11.17% | 18M | 21M | 1316MB/s |
case list:
changed test Program:
zsync ... make -b 2048
changed to -b 1024
hsynz ... make -s-2k
changed to -s-1k
test result average:
make | mem | speed | patch | compress | mem | max mem | speed |
---|---|---|---|---|---|---|---|
zsyncmake | 1M | 352.4MB/s | hsynz p1 | 60.20% | 4M | 11M | 163MB/s |
zsyncmake | 1M | 352.4MB/s | hsynz p8 | 60.20% | 12M | 19M | 228MB/s |
zsyncmake | 1M | 353.6MB/s | zsync |
60.20% | 6M | 18M | 107MB/s |
zsyncmake -z | 1M | 20.0MB/s | hsynz p1 | 57.16% | 4M | 12M | 170MB/s |
zsyncmake -z | 1M | 20.0MB/s | hsynz p8 | 57.16% | 12M | 20M | 240MB/s |
zsyncmake -z | 1M | 20.0MB/s | zsync |
57.16% | 9M | 25M | 79MB/s |
hsync_make p1 -zsync | 4M | 412.6MB/s | hsynz p1 | 60.25% | 4M | 11M | 165MB/s |
hsync_make p8 -zsync | 14M | 569.4MB/s | hsynz p8 | 60.25% | 12M | 19M | 231MB/s |
hsync_make p8 -zsync | 14M | 568.6MB/s | zsync |
60.25% | 6M | 18M | 106MB/s |
hsync_make p1 gz -zsync | 5M | 22.2MB/s | hsynz p1 | 57.24% | 4M | 12M | 169MB/s |
hsync_make p8 gz -zsync | 29M | 119.3MB/s | hsynz p8 | 57.24% | 12M | 20M | 240MB/s |
hsync_make p8 gz -zsync | 29M | 119.7MB/s | zsync |
57.25% | 9M | 25M | 79MB/s |
hsync_make p1 lgz -zsync | 13M | 23.2MB/s | hsynz p1 | 57.18% | 4M | 12M | 168MB/s |
hsync_make p8 lgz -zsync | 96M | 111.5MB/s | hsynz p8 | 57.18% | 12M | 20M | 235MB/s |
hsync_make p8 lgz -zsync | 96M | 111.6MB/s | zsync |
57.19% | 9M | 25M | 77MB/s |
hsync_make p1 | 4M | 1517.4MB/s | hsynz p1 | 59.82% | 4M | 10M | 229MB/s |
hsync_make p8 | 10M | 2200.1MB/s | hsynz p8 | 59.82% | 12M | 18M | 355MB/s |
hsync_make p1 zlib | 5M | 22.9MB/s | hsynz p1 | 56.17% | 4M | 11M | 242MB/s |
hsync_make p8 zlib | 29M | 121.8MB/s | hsynz p8 | 56.17% | 12M | 19M | 383MB/s |
hsync_make p1 ldef | 14M | 24.1MB/s | hsynz p1 | 56.11% | 4M | 11M | 242MB/s |
hsync_make p8 ldef | 96M | 112.0MB/s | hsynz p8 | 56.11% | 12M | 19M | 382MB/s |
hsync_make p1 gz | 5M | 23.1MB/s | hsynz p1 | 56.44% | 4M | 11M | 242MB/s |
hsync_make p8 gz | 29M | 122.3MB/s | hsynz p8 | 56.44% | 12M | 19M | 382MB/s |
hsync_make p1 lgz | 14M | 24.0MB/s | hsynz p1 | 56.38% | 4M | 11M | 240MB/s |
hsync_make p8 lgz | 96M | 111.7MB/s | hsynz p8 | 56.38% | 12M | 19M | 380MB/s |
hsync_make p1 zstd | 534M | 2.6MB/s | hsynz p1 | 55.22% | 24M | 28M | 234MB/s |
hsync_make p8 zstd | 3443M | 10.0MB/s | hsynz p8 | 55.22% | 24M | 28M | 361MB/s |
hsign_diff p1 | 3M | 392.8MB/s | hpatchz | 58.92% | 3M | 4M | 2134MB/s |
hsign_diff p8 | 11M | 1043.7MB/s | hpatchz | 58.92% | 3M | 4M | 2152MB/s |
hsign_diff p1 zlib | 3M | 54.0MB/s | hpatchz | 54.77% | 4M | 4M | 978MB/s |
hsign_diff p8 zlib | 11M | 265.7MB/s | hpatchz | 54.77% | 4M | 4M | 972MB/s |
hsign_diff p1 ldef | 15M | 53.4MB/s | hpatchz | 54.64% | 4M | 4M | 667MB/s |
hsign_diff p8 ldef | 119M | 212.4MB/s | hpatchz | 54.64% | 4M | 4M | 661MB/s |
hsign_diff p1 zstd | 213M | 11.2MB/s | hpatchz | 53.89% | 20M | 20M | 1461MB/s |
hsign_diff p8 zstd | 1087M | 13.3MB/s | hpatchz | 53.89% | 20M | 20M | 1459MB/s |