Add diffWithCovers/patchSingleStream APIs, robust loading, full CI matrix#3
Conversation
- diffWithCovers(old, new, covers, options): create standard hdiffpatch payloads from caller-provided cover lines, with replace/merge/ native-coalesce modes and optional debugCovers introspection - patchSingleStream: file-level apply for single-compressed diffs produced by diff()/diffWithCovers() - Point HDiffPatch submodule at reactnativecn fork (over https) for the cover-listener capacity change; migrate module registration out of the namespace and guard diff-declared sizes against size_t truncation Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Loader prefers a local build/Release build (dev), then static per-platform prebuild paths incl. darwin-x64/win32-x64, and falls back to node-gyp-build with a clear error on unsupported platforms; a missing or ABI-incompatible prebuild no longer crashes require() - Drop the install script: the published package has no sources, so node-gyp fallback could only ever fail; move node-addon-api to devDependencies and declare engines.node >=14.17 - Remove redundant .npmignore (files field wins); anchor the version tag regex in prepublish.ts Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- New ci.yml: build + test on push/PR across linux x64/arm64, macOS Intel/arm64, and Windows, under both Node and Bun - publish.yml: restore macos-15-intel and windows-latest prebuilds, run tests before uploading artifacts, publish with --provenance - hdp patch now sniffs the diff header (HDIFF13 / HDIFFSF20) and applies the right patcher, so CLI can apply diff() output too - Tests: async callback paths for all five APIs, corrupt-diff error propagation, CLI coverage for both formats; ci-failure-email.yml tracked - README: platform support, development guide, format notes, licenses Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 26 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughAdds native cover-aware diffing ( ChangesCover diffing and single-stream patching feature
CI/CD, submodule, and packaging updates
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant JS as diffWithCovers (JS API)
participant Native as main.cc bindings
participant Hdiff as hdiff_with_covers
participant Listener as CoverLinesListener
JS->>Native: diffWithCovers(old, new, covers, options)
Native->>Hdiff: hdiff_with_covers(old, new, covers, mode)
Hdiff->>Hdiff: validate_covers(covers)
Hdiff->>Listener: create_single_compressed_diff(...)
Listener->>Listener: prepare_listener_covers(mode)
Listener-->>Hdiff: final covers, diff buffer
Hdiff-->>Native: HdiffWithCoversResult
Native-->>JS: diff buffer + cover metrics
sequenceDiagram
participant Publish as Publish Package workflow
participant GH as GitHub Actions runner
participant API as GitHub Issues API
Publish->>GH: workflow_run completed
alt conclusion is failure/timed_out/action_required
GH->>API: search open issues by title prefix
alt issue exists
GH->>API: update issue title/body
else no issue
GH->>API: create new issue
end
else conclusion is success
GH->>API: search open issues by title prefix
GH->>API: close matching issues with recovery comment
end
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
bin/hdiffpatch.js (1)
22-36: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value
fs.openSynccall isn't guarded.If
diffFiledoesn't exist or isn't readable,fs.openSync(line 25) throws before thetry/finallyblock, producing a raw NodeENOENT/EACCESstack trace instead of a CLI-friendly error viafail(). This is a new dedicated file-access path introduced ahead of the actual patch call, so it's worth handling consistently with the rest of the CLI's error reporting.Suggested fix
function detectDiffFormat(diffFile) { const header = Buffer.alloc(9); - const fd = fs.openSync(diffFile, 'r'); - let bytesRead; + let fd; + let bytesRead; try { + fd = fs.openSync(diffFile, 'r'); bytesRead = fs.readSync(fd, header, 0, header.length, 0); } finally { - fs.closeSync(fd); + if (fd !== undefined) fs.closeSync(fd); }This doesn't fully solve the friendliness aspect (the thrown error still propagates), but avoids a partially-opened resource if
readSyncthrows for reasons other than open failure. If the caller (line 71) isn't already wrapped in a top-level try/catch producing a clean CLI error message, consider verifying that.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@bin/hdiffpatch.js` around lines 22 - 36, The detectDiffFormat() helper opens the diff file before any error handling, so missing or unreadable inputs throw a raw fs.openSync() exception instead of a CLI-friendly failure. Wrap the fs.openSync()/fs.readSync() path in try/catch around detectDiffFormat(), and convert open/read failures into the existing fail() flow; keep the existing closeSync cleanup in a finally block and make sure the caller that uses detectDiffFormat() also surfaces a clean error message.index.js (1)
1-28: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winLocal build path bypasses all fallback/error handling.
Lines 7-9
require(localBuild)runs outside thetry/catchthat guards the prebuild switch below. If a stale or ABI-incompatible artifact exists atbuild/Release/hdiffpatch.node(e.g., built for a different Node major version), this throws an uncaught exception instead of falling through to the prebuild/node-gyp-buildpaths that exist specifically to handle such mismatches.Separately, the blanket
catch (err) {}around the prebuild switch (line 25) silently swallows any error, not just "file missing"/ABI mismatch — a corrupted prebuild or unrelated native crash would be hidden and quietly retried vianode-gyp-build, making failures harder to diagnose.Suggested tightening
const localBuild = path.join(__dirname, 'build/Release/hdiffpatch.node'); if (fs.existsSync(localBuild)) { - return require(localBuild); + try { + return require(localBuild); + } catch (err) { + // fall through to prebuild/node-gyp-build resolution + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@index.js` around lines 1 - 28, The native module loading in loadNative() bypasses fallback handling for the local build and silently swallows all prebuild errors. Move the local build require(localBuild) into the same guarded flow as the prebuild switch so ABI-incompatible or stale artifacts can fall back to node-gyp-build, and narrow the catch around the prebuild require cases to only handle expected load failures while preserving unexpected errors for diagnosis..github/workflows/ci.yml (1)
21-23: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winSet
persist-credentials: falseon checkout.Static analysis flags credential persistence via
actions/checkoutwithoutpersist-credentials: false. Since this workflow runs onpull_request(including forks) and doesn't need push access, disabling credential persistence reduces exposure.🔒 Suggested fix
- uses: actions/checkout@v5 with: submodules: recursive + persist-credentials: false🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/ci.yml around lines 21 - 23, The checkout step in the CI workflow should disable stored Git credentials by setting persist-credentials to false. Update the existing actions/checkout usage in the workflow that runs on pull_request so it keeps submodules: recursive but does not persist credentials, since no push access is needed. Refer to the checkout step in the CI workflow when making the change.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.gitmodules:
- Line 3: The HDiffPatch submodule remote is pointing to a repository that does
not contain the pinned commit, so update the .gitmodules entry to a remote that
actually exposes the existing HDiffPatch commit or repin the submodule to a
commit available on reactnativecn/HDiffPatch.git. Make sure the submodule
reference remains valid for fresh clones and CI by keeping the commit and remote
in sync for the HDiffPatch submodule entry.
In `@README.md`:
- Around line 111-115: The README note is outdated and contradicts the CLI’s
auto-detection behavior. Update the text near the hdp patch usage guidance to
say that the CLI can handle both streaming diffs from hdp diff and
single-compressed diffs from diff()/diffWithCovers() because bin/hdiffpatch.js
detects the format and routes to patchSingleStream when needed. Make sure the
wording reflects that patch() / patchSingleStream() are the underlying APIs, not
a limitation of the hdp patch command.
In `@src/hdiff.cpp`:
- Around line 129-170: The native coalescing in coalesce_native_covers is
merging adjacent covers based only on position and delta, which can skip over
bytes that were never verified. Update this logic to perform a byte comparison
for the gap before extending the previous hpatch_TCover, or otherwise restrict
native-coalesce to best-effort behavior only. Use the existing helper and flow
in coalesce_native_covers, cover_new_end, and cover_old_end to locate where the
merge decision is made.
In `@src/main.cc`:
- Around line 43-51: `parseUint64String` currently lets `std::strtoull` accept a
leading minus sign and wrap it into a huge unsigned value. Update this helper to
explicitly reject inputs starting with `-` before calling `std::strtoull`, while
keeping the existing empty/string-end/errno checks, so `oldPos`-style fields
fail fast with a clear invalid-number path instead of being converted to
`UINT64_MAX`.
---
Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 21-23: The checkout step in the CI workflow should disable stored
Git credentials by setting persist-credentials to false. Update the existing
actions/checkout usage in the workflow that runs on pull_request so it keeps
submodules: recursive but does not persist credentials, since no push access is
needed. Refer to the checkout step in the CI workflow when making the change.
In `@bin/hdiffpatch.js`:
- Around line 22-36: The detectDiffFormat() helper opens the diff file before
any error handling, so missing or unreadable inputs throw a raw fs.openSync()
exception instead of a CLI-friendly failure. Wrap the
fs.openSync()/fs.readSync() path in try/catch around detectDiffFormat(), and
convert open/read failures into the existing fail() flow; keep the existing
closeSync cleanup in a finally block and make sure the caller that uses
detectDiffFormat() also surfaces a clean error message.
In `@index.js`:
- Around line 1-28: The native module loading in loadNative() bypasses fallback
handling for the local build and silently swallows all prebuild errors. Move the
local build require(localBuild) into the same guarded flow as the prebuild
switch so ABI-incompatible or stale artifacts can fall back to node-gyp-build,
and narrow the catch around the prebuild require cases to only handle expected
load failures while preserving unexpected errors for diagnosis.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7446e453-e76a-4e84-8234-5b5e13062fde
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (18)
.github/workflows/ci-failure-email.yml.github/workflows/ci.yml.github/workflows/publish.yml.gitmodules.npmignoreHDiffPatchREADME.mdbin/hdiffpatch.jsindex.d.tsindex.jspackage.jsonscripts/prepublish.tssrc/hdiff.cppsrc/hdiff.hsrc/hpatch.cppsrc/hpatch.hsrc/main.cctest/test.js
💤 Files with no reviewable changes (1)
- .npmignore
| [submodule "HDiffPatch"] | ||
| path = HDiffPatch | ||
| url = https://github.com/sisong/HDiffPatch.git | ||
| url = https://github.com/reactnativecn/HDiffPatch.git |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sha=$(git submodule status --cached HDiffPatch | awk '{print $1}' | sed 's/^[+-]//')
git ls-remote https://github.com/reactnativecn/HDiffPatch.git "$sha"Repository: reactnativecn/node-hdiffpatch
Length of output: 167
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'cached submodule SHA: '
git submodule status --cached -- HDiffPatch | awk '{print $1}' | sed 's/^[+-]//'
sha=$(git submodule status --cached -- HDiffPatch | awk '{print $1}' | sed 's/^[+-]//')
printf 'matching refs on reactnativecn/HDiffPatch:\n'
git ls-remote https://github.com/reactnativecn/HDiffPatch.git "$sha" || true
printf '\nexistence check against the remote commit object:\n'
git ls-remote https://github.com/reactnativecn/HDiffPatch.git | awk -v sha="$sha" '$1 == sha {found=1} END {exit found ? 0 : 1}'
echo "found=$?"Repository: reactnativecn/node-hdiffpatch
Length of output: 332
Keep the HDiffPatch submodule on a remote that contains the pinned commit. The fork at reactnativecn/HDiffPatch.git does not expose 4a7eb78aee3bd5acbe6fe55621391685648030d5, so fresh clones and CI submodule updates will fail unless the submodule is repinned to a commit that exists on that remote.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.gitmodules at line 3, The HDiffPatch submodule remote is pointing to a
repository that does not contain the pinned commit, so update the .gitmodules
entry to a remote that actually exposes the existing HDiffPatch commit or repin
the submodule to a commit available on reactnativecn/HDiffPatch.git. Make sure
the submodule reference remains valid for fresh clones and CI by keeping the
commit and remote in sync for the HDiffPatch submodule entry.
| std::vector<hpatch_TCover> coalesce_native_covers(const hpatch_TCover* nativeCovers, | ||
| size_t nativeCoverCount) { | ||
| const uint64_t maxGapBytes = 64; | ||
| std::vector<hpatch_TCover> coalesced; | ||
| coalesced.reserve(nativeCoverCount); | ||
|
|
||
| for (size_t i = 0; i < nativeCoverCount; ++i) { | ||
| const hpatch_TCover& cover = nativeCovers[i]; | ||
| if (cover.length == 0) { | ||
| continue; | ||
| } | ||
| if (coalesced.empty()) { | ||
| coalesced.push_back(cover); | ||
| continue; | ||
| } | ||
|
|
||
| hpatch_TCover& previous = coalesced.back(); | ||
| const uint64_t previousNewEnd = cover_new_end(previous); | ||
| const uint64_t previousOldEnd = cover_old_end(previous); | ||
| const uint64_t coverNewPos = static_cast<uint64_t>(cover.newPos); | ||
| const uint64_t coverOldPos = static_cast<uint64_t>(cover.oldPos); | ||
| if (coverNewPos < previousNewEnd || coverOldPos < previousOldEnd) { | ||
| coalesced.push_back(cover); | ||
| continue; | ||
| } | ||
|
|
||
| const uint64_t newGap = coverNewPos - previousNewEnd; | ||
| const uint64_t oldGap = coverOldPos - previousOldEnd; | ||
| const int64_t previousDelta = | ||
| static_cast<int64_t>(previous.oldPos) - static_cast<int64_t>(previous.newPos); | ||
| const int64_t coverDelta = | ||
| static_cast<int64_t>(cover.oldPos) - static_cast<int64_t>(cover.newPos); | ||
| if (newGap <= maxGapBytes && oldGap == newGap && previousDelta == coverDelta) { | ||
| previous.length = static_cast<hpatch_StreamPos_t>(cover_new_end(cover) - previous.newPos); | ||
| continue; | ||
| } | ||
|
|
||
| coalesced.push_back(cover); | ||
| } | ||
|
|
||
| return coalesced; | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
HDiffPatch create_single_compressed_diff ICoverLinesListener cover coalescing semantics
💡 Result:
The HDiffPatch library provides the create_single_compressed_diff function as part of its v4 API to generate a single compressed patch file between two data sets [1][2][3]. Regarding your query about ICoverLinesListener and cover coalescing: ICoverLinesListener is a callback interface in HDiffPatch used during the multi-threaded diffing process to monitor and influence the search for matching data blocks (covers) [4][5]. While the HDiffPatch codebase uses internal mechanisms like tm_collate_covers (often called after multi-threaded search) to merge or manage overlapping/adjacent covers, the term "coalescing" in this context typically refers to the logic for combining smaller, fragmented match regions into more efficient, larger cover blocks [6]. The ICoverLinesListener interface allows external control over this process through methods such as: search_cover_limit: Used to determine if the search for covers should be limited [4]. research_cover: Provides an opportunity to refine or re-evaluate detected covers [4]. insert_cover: Allows for the explicit insertion of covers during the process [4]. search_cover_finish: Called when the cover search phase is complete, providing a final opportunity to process or filter the detected covers before serialization [4]. There is no specific "cover coalescing semantics" property defined in the API; rather, the listener provides hooks that allow you to influence how covers are searched, inserted, and finalized, which effectively dictates the structure and optimization of the resulting diff [4][6]. Coalescing, if desired, is generally handled by the library's internal logic or can be influenced by how the listener modifies the list of covers before they are passed to the serialization phase [6]. If you are encountering issues related to "coalescing" or line coverage in other contexts, note that unrelated software projects also use terms like "coalescing" and "coverage" (e.g., nullish coalescing in JavaScript/SonarQube or line coverage in JaCoCo), which are distinct from the HDiffPatch binary diffing process [7][8][9].
Citations:
- 1: https://github.com/sisong/HDiffPatch/blob/53034d0d/libHDiffPatch/HDiff/diff.h
- 2: https://github.com/sisong/HDiffPatch/blob/master/README.md
- 3: https://github.com/sisong/HDiffPatch/blob/53034d0d/README.md
- 4: https://github.com/sisong/HDiffPatch/blob/53034d0d/libHDiffPatch/HDiff/diff_types.h
- 5: https://deepwiki.com/sisong/HDiffPatch/5.1-hdiff-library
- 6: https://github.com/sisong/HDiffPatch/blob/master/libHDiffPatch/HDiff/diff.cpp
- 7: https://community.sonarsource.com/t/false-positive-condition-count-on-nullish-coalescing-lines-blocks-coverage-on-new-code-gate/183013
- 8: https://www.eclemma.org/jacoco/trunk/coverage/org.jacoco.core/org.jacoco.core.internal.analysis/LineImpl.java.html
- 9: Constraint function "covers" doesn't work correct with BAG OF lines claeis/ilivalidator#465
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## hdiff.cpp outline\n'
ast-grep outline src/hdiff.cpp --view expanded || true
printf '\n## relevant symbols\n'
rg -n "coalesce_native_covers|check_single_compressed_diff|NativeCoalesce|coverLines|prepare_listener_covers|validate_covers|create_single_compressed_diff|ICoverLinesListener" src/hdiff.cpp src -n -A 4 -B 4 || true
printf '\n## hdiff.cpp excerpt\n'
sed -n '1,420p' src/hdiff.cppRepository: reactnativecn/node-hdiffpatch
Length of output: 30576
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## diff.h / diff_types.h cover docs\n'
rg -n "hpatch_TCover|ICoverLinesListener|coverLines|search_cover|insert_cover|finish|cover" HDiffPatch/libHDiffPatch/HDiff/diff.h HDiffPatch/libHDiffPatch/HDiff/diff_types.h HDiffPatch -A 8 -B 4 || true
printf '\n## focused header excerpts\n'
sed -n '1,260p' HDiffPatch/libHDiffPatch/HDiff/diff_types.h
printf '\n--- diff.h ---\n'
sed -n '1,260p' HDiffPatch/libHDiffPatch/HDiff/diff.hRepository: reactnativecn/node-hdiffpatch
Length of output: 481
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## locate diff headers\n'
fd -a 'diff.h|diff_types.h' . || true
printf '\n## locate cover symbols\n'
rg -n "typedef struct.*hpatch_TCover|struct hpatch_TCover|ICoverLinesListener|coverLines|search_cover|insert_cover|search_cover_finish" . || trueRepository: reactnativecn/node-hdiffpatch
Length of output: 462
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## top-level tree\n'
find . -maxdepth 2 -type f | sed 's#^\./##' | sort | head -300
printf '\n## search for HDiffPatch headers by basename\n'
fd -a 'diff.h' .
fd -a 'diff_types.h' .
fd -a 'hdiff*.h' .
fd -a '*diff*types*' .Repository: reactnativecn/node-hdiffpatch
Length of output: 899
🌐 Web query:
HDiffPatch hpatch_TCover ICoverLinesListener semantics cover must be byte-identical merge adjacent covers
💡 Result:
In the HDiffPatch library, the ICoverLinesListener interface is used for managing and processing sequences of match covers (hpatch_TCover) during the diff generation process [1][2]. Its purpose is to allow custom logic for searching, inserting, and refining these covers, rather than enforcing a specific byte-identical merge requirement at the interface level [1]. The semantics of handling covers, including whether adjacent covers should be merged, are typically determined by the implementation of the ICoverLinesListener or the underlying diff algorithm, not by a hard-coded mandate in the interface itself [1][3]. 1. ICoverLinesListener is primarily an interface for listener hooks, such as search_cover_limit, research_cover, insert_cover, and search_cover_finish [1]. 2. The library's core diff functions manage the collection of covers [2][4], and while optimization processes (such as merging or extending covers) may occur during or after the search phase [2][5], these are handled by the specific logic within the diff generation pipeline (e.g., in diff.cpp or via optimization helpers) [3][4]. 3. If an implementation requires byte-identical merge of adjacent covers to optimize diff size or patch speed, it must be explicitly implemented within the logic of the ICoverLinesListener (e.g., inside an insert_cover call) or post-processing step [1][5]. There is no inherent semantic requirement in the ICoverLinesListener interface definition itself that forces byte-identical merging of adjacent covers [1]. Users are expected to handle cover refinement as dictated by their specific patching strategy or serialization requirements [1][4].
Citations:
- 1: https://github.com/sisong/HDiffPatch/blob/53034d0d/libHDiffPatch/HDiff/diff_types.h
- 2: https://github.com/sisong/HDiffPatch/blob/53034d0d/libHDiffPatch/HDiff/diff.h
- 3: https://github.com/sisong/HDiffPatch/blob/master/libHDiffPatch/HDiff/diff.cpp
- 4: https://github.com/sisong/HDiffPatch/blob/53034d0d/libHDiffPatch/HDiff/diff.cpp
- 5: sisong/HDiffPatch@e34db39
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cat -n src/hdiff.hRepository: reactnativecn/node-hdiffpatch
Length of output: 1601
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## README mentions\n'
rg -n "NativeCoalesce|native-coalesce|merge|cover" README.md src/main.cc src/hdiff.h src/hdiff.cpp -A 4 -B 4 || true
printf '\n## README excerpt\n'
sed -n '1,260p' README.mdRepository: reactnativecn/node-hdiffpatch
Length of output: 32880
🌐 Web query:
site:github.com/sisong/HDiffPatch diff_types.h ICoverLinesListener hpatch_TCover coverLines callback semantics
💡 Result:
In the HDiffPatch library, ICoverLinesListener is an interface (a C-style struct of function pointers) defined in diff_types.h used to hook into and manage the cover (match) generation process during the diff creation [1]. It does not have a single callback called "coverLines"; rather, it provides a suite of lifecycle and control callbacks to customize how matches are searched, processed, or optimized [1]. The term hpatch_TCover refers to the structure representing a matching block between the old and new data, containing the old position, new position, and the length of the match [1]. Key semantics of the ICoverLinesListener interface include: 1. Search Control and Multithreading: The listener allows integration with multithreaded matching through callbacks like next_search_block_MT, which manages the distribution of work, and begin_search_block, which initializes parallel search sessions [1][2]. 2. Cover Manipulation: Callbacks such as insert_cover are used to intercept, filter, or modify the sequence of discovered covers (hpatch_TCover) before they are serialized into the final diff [1][3]. For example, implementations like TCoversOptim use this to unpack or perform custom processing on blocks [3]. 3. Research and Optimization: Methods like research_cover provide hooks for performing additional matching or refinement on specific data ranges, while search_cover_limit and get_limit_cover_length allow fine-grained control over search parameters to manage performance or limit the size/depth of matches [1][2][4]. 4. Serialization Hooks: The map_streams_befor_serialize callback allows the listener to perform final adjustments or mapping to input streams immediately before the diff serialization process begins [1][5]. The interface is typically passed as an optional argument to high-level diff functions (such as those in bsdiff_wrapper or vcdiff_wrapper) to allow users to inject custom logic into the standard diffing pipeline [6][7][8].
Citations:
- 1: https://github.com/sisong/HDiffPatch/blob/53034d0d/libHDiffPatch/HDiff/diff_types.h
- 2: https://github.com/sisong/HDiffPatch/blob/master/libHDiffPatch/HDiff/diff.cpp
- 3: https://github.com/sisong/HDiffPatch/blob/53034d0d/libHDiffPatch/HDiff/match_block.h
- 4: https://github.com/sisong/HDiffPatch/blob/53034d0d/libHDiffPatch/HDiff/diff.cpp
- 5: https://github.com/sisong/HDiffPatch/blob/53034d0d/bsdiff_wrapper/bsdiff_wrapper.cpp
- 6: https://github.com/sisong/HDiffPatch/blob/53034d0d/libHDiffPatch/HDiff/diff.h
- 7: https://github.com/sisong/HDiffPatch/blob/53034d0d/vcdiff_wrapper/vcdiff_wrapper.h
- 8: https://github.com/sisong/HDiffPatch/blob/53034d0d/bsdiff_wrapper/bsdiff_wrapper.h
native-coalesce needs a byte check before widening covers. Matching newPos/oldPos and delta alone can merge across a gap whose bytes were never compared, so this mode can feed an invalid cover chain into the diff builder and turn the final check into a hard failure. Compare the bridged bytes before merging, or keep this mode explicitly best-effort only.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/hdiff.cpp` around lines 129 - 170, The native coalescing in
coalesce_native_covers is merging adjacent covers based only on position and
delta, which can skip over bytes that were never verified. Update this logic to
perform a byte comparison for the gap before extending the previous
hpatch_TCover, or otherwise restrict native-coalesce to best-effort behavior
only. Use the existing helper and flow in coalesce_native_covers, cover_new_end,
and cover_old_end to locate where the merge decision is made.
- Reject negative numeric strings in cover fields: strtoull wraps "-1" to UINT64_MAX (and e.g. "-18446744073709551615" silently to 1); add a regression test - Fix stale README note: hdp patch auto-detects both diff formats - Install node-gyp globally on Windows runners: bun creates exe shims while prebuildify spawns node-gyp.cmd, so the prebuild step failed Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summary
Three groups of changes:
New native APIs (covers feature)
diffWithCovers(old, new, covers, options)— create standard hdiffpatch payloads from caller-provided cover lines, withreplace/merge/native-coalescemodes and optionaldebugCoversintrospectionpatchSingleStream(oldPath, diffPath, outNewPath)— file-level apply for single-compressed diffs produced bydiff()/diffWithCovers()newDataSize,stepMemSize) are validated againstsize_tbefore allocationRobust module loading & packaging
build/Release(dev) → static per-platform prebuild paths (now incl.darwin-x64,win32-x64) →node-gyp-buildfallback with a clear error; a missing or ABI-incompatible prebuild no longer crashesrequire()on fresh clonesinstallscript (published package ships no sources, so the node-gyp fallback could only fail on unsupported platforms), movednode-addon-apito devDependencies, addedengines.node >= 14.17.npmignore; anchored the^vtag regex in prepublishCI, tests, CLI
ci.yml: build + test on push/PR across linux x64/arm64, macOS Intel/arm64, Windows — under both Node and Bun runtimespublish.yml: restoredmacos-15-intelandwindows-latestprebuild jobs, tests run before artifacts upload,npm publish --provenancehdp patchauto-detects the diff format via header magic (HDIFF13/HDIFFSF20), so CLI can now applydiff()output as wellNotes for review
build/Releasebuild now always wins over bundled prebuilds (same convention as node-gyp-build); deletebuild/if it goes staleTest plan
node test/test.js— 11/11 pass locally (darwin-arm64)bun ./test/test.js— 11/11 pass under Bun 1.3.14No native build was found for platform=…error🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Tests