test: validate binary patch codec contract - #149
Open
floyd-soomgo wants to merge 3 commits into
Open
Conversation
Binary patch updates need HDiffPatch's patch applier and zstd's decompressor compiled into the native library, so the minimal set of sources for applying a patch is vendored here. Only the apply side is vendored: libHDiffPatch/HPatch plus zstd's common and decompress directories. zstd's amd64 assembly implementation is left out because the applier builds with ZSTD_DISABLE_ASM=1. The upstream directory layout is kept because the HDiffPatch headers include each other by repository path. The tree lives at the repository root rather than under android/ or ios/ because both platforms compile the same sources: iOS through the podspec at the root, Android through externalNativeBuild. One shared copy leaves nothing to drift.
…lier HDiffPatch takes the decompressor as a plugin. Upstream ships a demo header implementing a dozen codecs, which would pull in headers for codecs the applier never sees, so this is a standalone hpatch_TDecompress implementation for zstd - the only codec CodePush patches are compressed with. The window it accepts is capped at the 2^24 the generation options use, so a corrupted frame header asking for a wider window is rejected before anything is allocated. apply_patch_host is the reference applier built from those sources for the development machine. It loads the base bundle and the patch into memory and writes the restored bundle sequentially, which is the memory contract the Android and iOS appliers follow, and it maps each failure to its own exit code so a caller can tell a corrupt patch from a mismatched base.
Adds the CLI side of the codec - generatePatch and applyPatch spawn hdiffz and hpatchz with the fixed options that define the patch format - together with a test suite that pins the format down against real bytes and real binaries. The fixtures are committed and can be regenerated deterministically by scripts/binary-patch/generate-fixtures.mjs, so the tests verify the patch bytes that ship rather than bytes produced on the fly. hdiffz and hpatchz are built from upstream sources by scripts/binary-patch/build-hdiffpatch.sh, which the suite runs on demand when the tools are missing. Two properties are pinned down because callers have to handle them: a patch carries no checksum of the base data, and its zstd streams carry no content checksums. Applying a patch to a different base of the same size, or applying a patch whose body is corrupted, can therefore report success and still produce the wrong bytes. Verifying the base and target hashes stays the caller's responsibility. The applier sources ship in the npm package so both platforms can build them, minus the host harness, which only exists to run this suite.
An error occurred while trying to automatically change base from
codex-try-turbomodule-3
to
master
August 6, 2026 11:19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First PR of a series adding binary differential OTA updates: instead of downloading the full JS bundle on every release, clients will apply a small binary patch against the Hermes bytecode bundle embedded in the app binary, falling back to the full archive when anything mismatches.
This PR establishes the codec contract only — the diff/patch format, the vendored native applier sources, and automated tests that pin the format's behavior. Nothing is wired into the runtime yet: no gradle/CMake target, no podspec entry, and no change to the update flow. Follow-up PRs build on this contract (CLI artifact generation → release metadata → Android/iOS appliers → E2E).
Codec: HDiffPatch v5.1.3, patches generated with
hdiffz -f -m-6 -c-zstd-21-24(identifierhdiffpatch-m-zstd, format version 1). Chosen for patch size (~3x smaller than bsdiff on Hermes bundles in our measurements), generation speed, and a lightweight apply-side: the applier needs only the HPatch decoder sources plus a zstd decompressor.Changes
cpp/binarypatch/— platform-shared native sources (iOS will consume them via the podspec at the repo root, Android viaexternalNativeBuild; both land in follow-up PRs):vendor/— minimal vendored subset: HDiffPatch v5.1.3 patch-apply sources + zstd decompress-only sources (ZSTD_DISABLE_ASM=1,_IS_USED_MULTITHREAD=0; upstream layout preserved for traceability)binarypatch_zstd_decompressor.{h,c}— our ownhpatch_TDecompressadapter over zstd streaming decompression; window log capped at 24 to match the pinned generation options, so an over-wide (corrupt) frame header is rejected before allocationhost/— a small host-compilable harness (apply <old> <patch> <out>, exit codes distinguish failure modes) used by the test suite to prove the vendored applier restores byte-identical targets; excluded from the npm packagecli/utils/binaryPatch.ts—generatePatch/applyPatchwrappers aroundhdiffz/hpatchzwith the options pinnedscripts/binary-patch/build-hdiffpatch.sh— builds the pinned tools from source into.hdiffpatch-tools/(gitignored); minimal build needs onlysisong/zstdalongsidecli/fixtures/binary-patch/— deterministic base/target fixtures (generator script checked in) plus the committedupdate.patchcli/utils/binaryPatch.test.ts— the contract tests (see below)THIRD-PARTY-NOTICES— HDiffPatch (MIT) and zstd (BSD-3-Clause) attributionThe contract the tests pin
Test plan
Notes for reviewers
cpp/binarypatch/vendor/. The first-party surface is: the adapter, the host harness,cli/utils/binaryPatch.*, the fixture generator, the build script, and the notices.npm packshipscpp/(42 files) but excludes the dev-onlyhost/harness.