Skip to content

Commit 80f2d9c

Browse files
sunnylqmclaude
andcommitted
feat!: upgrade vendored HDiffPatch to v5.0.1, add diffSingleStream
BREAKING CHANGE: diffWithCovers is removed. It had no consumers (the HBC transform pipeline ended up not using covers), and dropping it lets the HDiffPatch submodule track pure upstream (v5.0.1, vendor/v5.0.1 branch on the fork) with no custom patches to carry forward. lzma submodule moves to v26.02 to match (new Z7_ST macro defined alongside _7ZIP_ST). New API: diffSingleStream(oldPath, newPath, outDiffPath[, cb]) — streaming block-match generation of the SAME single format diff() emits. Generation memory stays O(match block): 100MB inputs measured at ~27MB RSS delta (in-memory diff would need ~600MB). Output verified byte-exact through the legacy v3.1.1 client patch core, so already-shipped clients can apply these patches without any update. Compat verified both ways: legacy-generated patches apply via v5 patch() and v5-generated patches apply via the legacy prebuild; same-input patch sizes are byte-identical and diff speed is unchanged. v5's default diff progress printing is compiled out (_IS_OUT_DIFF_INFO=0) to keep server worker logs clean. diff parameters are pinned to the historical values (match score 3, step mem 256KB, lzma2 level 9 / 8MB dict / single thread) so artifact characteristics stay continuous across the upgrade. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 3c6ca8c commit 80f2d9c

12 files changed

Lines changed: 255 additions & 725 deletions

File tree

HDiffPatch

Submodule HDiffPatch updated 213 files

README.md

Lines changed: 12 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -35,57 +35,21 @@ bun run test:bun # run the same tests under the Bun runtime
3535

3636
Compare two buffers and return a new hdiffpatch patch as return value.
3737

38-
### diffWithCovers(originBuf, newBuf, covers[, options])
39-
40-
Create a standard hdiffpatch patch while using caller-provided cover lines when
41-
possible. By default, the supplied covers replace HDiffPatch's internal cover
42-
selection.
43-
44-
Each cover is `{ oldPos, newPos, len }`. Values may be numbers, decimal strings,
45-
or bigint values. The returned object is:
46-
47-
```js
48-
{
49-
diff: Buffer,
50-
usedCovers: boolean,
51-
requestedCoverCount: number,
52-
nativeCoverCapacity: number,
53-
finalCoverCount: number,
54-
coverMode: 'replace' | 'merge' | 'native-coalesce',
55-
nativeCovers?: HpatchCover[],
56-
finalCovers?: HpatchCover[]
57-
}
58-
```
59-
60-
The `diff` buffer is still a normal hdiffpatch payload and can be applied with
61-
`patch(originBuf, diff)` or an HDiffPatch-compatible apply side.
62-
63-
Set `options.mode` to `merge` to keep HDiffPatch's native covers and only add
64-
caller covers in new-file ranges not already covered by native covers:
65-
66-
```js
67-
const merged = hdiff.diffWithCovers(oldBuf, newBuf, covers, { mode: 'merge' });
68-
```
69-
70-
Set `options.mode` to `native-coalesce` to keep HDiffPatch's native cover
71-
selection but coalesce adjacent native covers that have the same old/new offset
72-
delta across small gaps. This remains a standard hdiffpatch payload and is useful
73-
as a costed post-processing experiment:
74-
75-
```js
76-
const coalesced = hdiff.diffWithCovers(oldBuf, newBuf, [], {
77-
mode: 'native-coalesce',
78-
});
79-
```
38+
### diffSingleStream(oldPath, newPath, outDiffPath[, cb])
8039

81-
Set `options.debugCovers` to `true` to include the native HDiffPatch cover list
82-
and the final listener cover list in the return value. This is for diagnostics;
83-
the default return shape avoids copying cover arrays.
40+
Create a **single-format** (same wire format as `diff()`) patch by streaming
41+
file paths with block matching — generation memory stays O(match block)
42+
regardless of input size (100MB inputs use ~30MB RSS). The output applies with
43+
`patch()`, `patchSingleStream()`, and any existing HDiffPatch single-format
44+
apply side, so legacy clients need no changes. Patch size is larger than the
45+
in-memory `diff()` for the same inputs; prefer `diff()` when memory allows.
46+
In sync mode returns `outDiffPath`; async callback signature is
47+
`(err, outDiffPath)`.
8448

8549
### patchSingleStream(oldPath, diffPath, outNewPath[, cb])
8650

87-
Apply a single-compressed hpatch payload created by `diff` or `diffWithCovers`
88-
from files. This is the file-level apply path for the normal in-memory `diff`
51+
Apply a single-compressed hpatch payload created by `diff` or
52+
`diffSingleStream` from files. This is the file-level apply path for the normal in-memory `diff`
8953
format. In sync mode returns `outNewPath`. In async mode, callback signature is
9054
`(err, outNewPath)`.
9155

@@ -111,7 +75,7 @@ hdp patch <oldFile> <diffFile> <outNew>
11175

11276
Note: `hdp patch` auto-detects the diff format by its header, so it can apply
11377
both streaming diffs created by `hdp diff` and single-compressed diffs created
114-
by the in-memory `diff()` / `diffWithCovers()` APIs.
78+
by the in-memory `diff()` / streaming `diffSingleStream()` APIs.
11579

11680
## License
11781

binding.gyp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
"HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/digest_matcher.cpp",
1818
"HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/stream_serialize.cpp",
1919
"HDiffPatch/libHDiffPatch/HDiff/private_diff/limit_mem_diff/adler_roll.c",
20+
"HDiffPatch/libHDiffPatch/HDiff/private_diff/match_block.cpp",
21+
"HDiffPatch/libHDiffPatch/HDiff/private_diff/match_inplace.cpp",
2022
"lzma/C/LzFind.c",
2123
"lzma/C/LzmaDec.c",
2224
"lzma/C/LzmaEnc.c",
@@ -26,7 +28,9 @@
2628
"defines": [
2729
"_IS_NEED_DIR_DIFF_PATCH=0",
2830
"_7ZIP_ST",
31+
"Z7_ST",
2932
"_IS_USED_MULTITHREAD=0",
33+
"_IS_OUT_DIFF_INFO=0",
3034
"NAPI_VERSION=8"
3135
],
3236
"include_dirs" : [

index.d.ts

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,10 @@ export type BinaryLike = Buffer | ArrayBufferView;
44

55
export type DiffCallback = (err: Error | null, result?: Buffer) => void;
66
export type StreamCallback = (err: Error | null, outPath?: string) => void;
7-
export type HpatchCover = {
8-
oldPos: number | string | bigint;
9-
newPos: number | string | bigint;
10-
len: number | string | bigint;
11-
};
12-
export type DiffWithCoversResult = {
13-
diff: Buffer;
14-
usedCovers: boolean;
15-
requestedCoverCount: number;
16-
nativeCoverCapacity: number;
17-
finalCoverCount: number;
18-
coverMode: 'replace' | 'merge' | 'native-coalesce';
19-
nativeCovers?: HpatchCover[];
20-
finalCovers?: HpatchCover[];
21-
};
22-
export type DiffWithCoversOptions = {
23-
mode?: 'replace' | 'merge' | 'native-coalesce' | 'native_coalesce';
24-
debugCovers?: boolean;
25-
};
267

278
export interface NativeAddon {
289
diff(oldBuf: BinaryLike, newBuf: BinaryLike): Buffer;
2910
diff(oldBuf: BinaryLike, newBuf: BinaryLike, cb: DiffCallback): void;
30-
diffWithCovers(
31-
oldBuf: BinaryLike,
32-
newBuf: BinaryLike,
33-
covers: HpatchCover[],
34-
options?: DiffWithCoversOptions
35-
): DiffWithCoversResult;
3611
patch(oldBuf: BinaryLike, diffBuf: BinaryLike): Buffer;
3712
patch(oldBuf: BinaryLike, diffBuf: BinaryLike, cb: DiffCallback): void;
3813
diffStream(oldPath: string, newPath: string, outDiffPath: string): string;
@@ -49,6 +24,13 @@ export interface NativeAddon {
4924
outNewPath: string,
5025
cb: StreamCallback
5126
): void;
27+
diffSingleStream(oldPath: string, newPath: string, outDiffPath: string): string;
28+
diffSingleStream(
29+
oldPath: string,
30+
newPath: string,
31+
outDiffPath: string,
32+
cb: StreamCallback,
33+
): void;
5234
patchSingleStream(oldPath: string, diffPath: string, outNewPath: string): string;
5335
patchSingleStream(
5436
oldPath: string,
@@ -66,12 +48,6 @@ export function diff(
6648
newBuf: BinaryLike,
6749
cb: DiffCallback
6850
): void;
69-
export function diffWithCovers(
70-
oldBuf: BinaryLike,
71-
newBuf: BinaryLike,
72-
covers: HpatchCover[],
73-
options?: DiffWithCoversOptions
74-
): DiffWithCoversResult;
7551

7652
export function patch(oldBuf: BinaryLike, diffBuf: BinaryLike): Buffer;
7753
export function patch(
@@ -103,6 +79,17 @@ export function patchStream(
10379
outNewPath: string,
10480
cb: StreamCallback
10581
): void;
82+
export function diffSingleStream(
83+
oldPath: string,
84+
newPath: string,
85+
outDiffPath: string,
86+
): string;
87+
export function diffSingleStream(
88+
oldPath: string,
89+
newPath: string,
90+
outDiffPath: string,
91+
cb: StreamCallback,
92+
): void;
10693
export function patchSingleStream(
10794
oldPath: string,
10895
diffPath: string,
@@ -118,10 +105,10 @@ export function patchSingleStream(
118105
declare const hdiffpatch: {
119106
native: NativeAddon;
120107
diff: typeof diff;
121-
diffWithCovers: typeof diffWithCovers;
122108
patch: typeof patch;
123109
diffStream: typeof diffStream;
124110
patchStream: typeof patchStream;
111+
diffSingleStream: typeof diffSingleStream;
125112
patchSingleStream: typeof patchSingleStream;
126113
};
127114

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ const native = loadNative();
3232
exports.native = native;
3333

3434
exports.diff = native.diff;
35-
exports.diffWithCovers = native.diffWithCovers;
3635
exports.patch = native.patch;
3736
exports.diffStream = native.diffStream;
3837
exports.patchStream = native.patchStream;
38+
exports.diffSingleStream = native.diffSingleStream;
3939
exports.patchSingleStream = native.patchSingleStream;

lzma

Submodule lzma updated 602 files

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "node-hdiffpatch",
3-
"version": "1.1.2",
3+
"version": "2.0.0",
44
"description": "hdiffpatch port to node.js",
55
"main": "index.js",
66
"types": "index.d.ts",

0 commit comments

Comments
 (0)