Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/native-benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ on:
schedule:
- cron: '23 5 * * 3'
workflow_dispatch:
inputs:
sizes_mib:
description: Comma-separated input sizes in MiB
required: true
default: '1,10,50'
type: string

permissions:
contents: read
Expand Down Expand Up @@ -33,6 +39,7 @@ jobs:
- name: Run deterministic benchmark
env:
BENCHMARK_OUTPUT: native-core-${{ runner.os }}.json
BENCHMARK_SIZES_MIB: ${{ inputs.sizes_mib || '1,10,50' }}
run: node scripts/benchmark-native.mjs

- name: Upload benchmark report
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0

- name: Upload GitHub Pages artifact
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: site-dist

Expand All @@ -71,4 +71,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
61 changes: 46 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<p align="center">
<a href="https://bs-dff-patch.corerobin.com/docs/">Documentation</a> ·
<a href="https://bs-dff-patch.corerobin.com/#playground">Live Playground</a> ·
<a href="https://bs-dff-patch.corerobin.com/tools/">Binary Patch Toolkit</a> ·
<a href="./README.zh-CN.md">中文说明</a> ·
<a href="https://www.npmjs.com/package/react-native-bs-diff-patch">npm</a>
</p>
Expand Down Expand Up @@ -50,15 +51,18 @@ replaces live data.
cap input/output sizes, and avoid exposing partial output files.
- **No patch service required:** Web diffing and patching happen locally in the
browser.
- **Inspect and prove compatibility:** read patch metadata and verify restored
bytes through the same API shape on native and Web.

## Platform overview

| | Android / iOS | React Native Web |
| -------------- | ------------------------------ | -------------------------------------------------- |
| Input | Absolute file paths | `ArrayBuffer`, typed arrays, `DataView`, or `Blob` |
| Basic API | `diff()` / `patch()` | `diffBytes()` / `patchBytes()` |
| Controlled API | `startDiff()` / `startPatch()` | `AbortSignal` and binary limits |
| Engine | Native C via JNI / ObjC++ | Same C core via WASM Worker |
| | Android / iOS | React Native Web |
| -------------- | -------------------------------------------- | -------------------------------------------------- |
| Input | Absolute file paths | `ArrayBuffer`, typed arrays, `DataView`, or `Blob` |
| Basic API | `diff()` / `patch()` | `diffBytes()` / `patchBytes()` |
| Controlled API | `startDiff()` / `startPatch()` | `AbortSignal` and binary limits |
| Verification | Paths via `inspectPatch()` / `verifyPatch()` | Binary values via the same APIs |
| Engine | Native C via JNI / ObjC++ | Same C core via WASM Worker |

## Install

Expand Down Expand Up @@ -138,17 +142,44 @@ Web calls return a new `Uint8Array` and leave caller-owned buffers usable.
Aborted operations reject with `EABORTED`; configured binary limits reject with
`ERESOURCE`.

## Inspect and verify a patch

Use `inspectPatch()` for a cheap structural check, then `verifyPatch()` to apply
into a temporary result and compare it with the expected target byte-for-byte:

```ts
import { inspectPatch, verifyPatch } from 'react-native-bs-diff-patch';

// Android / iOS use paths. Web uses File, Blob, ArrayBuffer, or typed arrays.
const metadata = await inspectPatch(patchPath);
const result = await verifyPatch(oldPath, patchPath, expectedPath, {
maxInputBytes: 64 * 1024 * 1024,
maxOutputBytes: 128 * 1024 * 1024,
});

if (!metadata.valid || !result.verified) {
throw new Error('Patch compatibility check failed');
}
```

The native verification output is temporary and always cleaned up. The Web
form accepts `oldFile`, `patchFile`, and `expectedFile` in the same argument
order. Structural validity is diagnostic; authenticate trusted hashes in your
update manifest before replacing live data.

## API matrix

| API | Android | iOS | Web |
| ------------------------------------------ | ------- | --- | --- |
| `diff(oldPath, newPath, patchPath)` | Yes | Yes | No |
| `patch(oldPath, outputPath, patchPath)` | Yes | Yes | No |
| `startDiff(...)` / `startPatch(...)` | Yes | Yes | No |
| `diffBytes(oldData, newData, options?)` | No | No | Yes |
| `patchBytes(oldData, patchData, options?)` | No | No | Yes |
| Legacy architecture, while supplied by RN | Yes | Yes | N/A |
| New Architecture / TurboModule | Yes | Yes | N/A |
| API | Android | iOS | Web |
| --------------------------------------------- | ------- | --- | --- |
| `diff(oldPath, newPath, patchPath)` | Yes | Yes | No |
| `patch(oldPath, outputPath, patchPath)` | Yes | Yes | No |
| `startDiff(...)` / `startPatch(...)` | Yes | Yes | No |
| `diffBytes(oldData, newData, options?)` | No | No | Yes |
| `patchBytes(oldData, patchData, options?)` | No | No | Yes |
| `inspectPatch(path or binary, options?)` | Yes | Yes | Yes |
| `verifyPatch(old, patch, expected, options?)` | Yes | Yes | Yes |
| Legacy architecture, while supplied by RN | Yes | Yes | N/A |
| New Architecture / TurboModule | Yes | Yes | N/A |

Unavailable platform APIs reject with `EUNSUPPORTED`; the package never
silently switches to a different input model.
Expand Down
59 changes: 44 additions & 15 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<p align="center">
<a href="https://bs-dff-patch.corerobin.com/docs/zh-CN/">中文文档</a> ·
<a href="https://bs-dff-patch.corerobin.com/#playground">在线 Playground</a> ·
<a href="https://bs-dff-patch.corerobin.com/zh-CN/tools/">二进制补丁工具箱</a> ·
<a href="./README.md">English</a> ·
<a href="https://www.npmjs.com/package/react-native-bs-diff-patch">npm</a>
</p>
Expand All @@ -47,15 +48,17 @@
- **可控制高成本任务:** 原生 job 支持进度、协作式取消、输入/输出限制,并避免
暴露未完成的输出文件。
- **Web 无需补丁服务:** 差分和还原完全在浏览器本地执行。
- **检查并证明兼容性:** 原生与 Web 使用相同 API 读取补丁元数据,并验证还原字节。

## 平台概览

| | Android / iOS | React Native Web |
| -------- | ------------------------------ | ----------------------------------------------- |
| 输入 | 绝对文件路径 | `ArrayBuffer`、TypedArray、`DataView` 或 `Blob` |
| 基础 API | `diff()` / `patch()` | `diffBytes()` / `patchBytes()` |
| 可控 API | `startDiff()` / `startPatch()` | `AbortSignal` 与二进制大小限制 |
| 执行核心 | JNI / ObjC++ 调用原生 C | WASM Worker 运行同一 C 核心 |
| | Android / iOS | React Native Web |
| -------- | ----------------------------------------- | ----------------------------------------------- |
| 输入 | 绝对文件路径 | `ArrayBuffer`、TypedArray、`DataView` 或 `Blob` |
| 基础 API | `diff()` / `patch()` | `diffBytes()` / `patchBytes()` |
| 可控 API | `startDiff()` / `startPatch()` | `AbortSignal` 与二进制大小限制 |
| 验证能力 | 路径版 `inspectPatch()` / `verifyPatch()` | 相同 API 的二进制输入 |
| 执行核心 | JNI / ObjC++ 调用原生 C | WASM Worker 运行同一 C 核心 |

## 安装

Expand Down Expand Up @@ -134,17 +137,43 @@ const restoredBytes = await patchBytes(oldBytes, patchBytesValue, {
Web API 返回新的 `Uint8Array`,不会转移或失效调用方的缓冲区。主动取消以
`EABORTED` 拒绝;命中二进制大小限制时以 `ERESOURCE` 拒绝。

## 检查并验证补丁

先用 `inspectPatch()` 完成低成本结构检查,再用 `verifyPatch()` 将补丁应用到临时
结果,并与预期目标逐字节比较:

```ts
import { inspectPatch, verifyPatch } from 'react-native-bs-diff-patch';

// Android / iOS 使用路径;Web 使用 File、Blob、ArrayBuffer 或 TypedArray。
const metadata = await inspectPatch(patchPath);
const result = await verifyPatch(oldPath, patchPath, expectedPath, {
maxInputBytes: 64 * 1024 * 1024,
maxOutputBytes: 128 * 1024 * 1024,
});

if (!metadata.valid || !result.verified) {
throw new Error('补丁兼容性验证失败');
}
```

原生验证产生的临时输出始终会被清理。Web 版本按相同顺序传入 `oldFile`、
`patchFile` 与 `expectedFile`。结构有效只用于诊断;替换业务数据前仍应认证更新清单
中的可信哈希。

## API 矩阵

| API | Android | iOS | Web |
| ------------------------------------------ | ------- | ------ | ------ |
| `diff(oldPath, newPath, patchPath)` | 支持 | 支持 | 不支持 |
| `patch(oldPath, outputPath, patchPath)` | 支持 | 支持 | 不支持 |
| `startDiff(...)` / `startPatch(...)` | 支持 | 支持 | 不支持 |
| `diffBytes(oldData, newData, options?)` | 不支持 | 不支持 | 支持 |
| `patchBytes(oldData, patchData, options?)` | 不支持 | 不支持 | 支持 |
| 旧架构(限 RN 仍提供时) | 支持 | 支持 | 不适用 |
| 新架构 / TurboModule | 支持 | 支持 | 不适用 |
| API | Android | iOS | Web |
| --------------------------------------------- | ------- | ------ | ------ |
| `diff(oldPath, newPath, patchPath)` | 支持 | 支持 | 不支持 |
| `patch(oldPath, outputPath, patchPath)` | 支持 | 支持 | 不支持 |
| `startDiff(...)` / `startPatch(...)` | 支持 | 支持 | 不支持 |
| `diffBytes(oldData, newData, options?)` | 不支持 | 不支持 | 支持 |
| `patchBytes(oldData, patchData, options?)` | 不支持 | 不支持 | 支持 |
| `inspectPatch(path 或 binary, options?)` | 支持 | 支持 | 支持 |
| `verifyPatch(old, patch, expected, options?)` | 支持 | 支持 | 支持 |
| 旧架构(限 RN 仍提供时) | 支持 | 支持 | 不适用 |
| 新架构 / TurboModule | 支持 | 支持 | 不适用 |

调用当前平台不可用的 API 会以 `EUNSUPPORTED` 拒绝,不会静默切换成其他输入
模型。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,25 @@ class BsDiffPatchModule(reactContext: ReactApplicationContext) :
override fun diff(oldFile: String, newFile: String, patchFile: String, promise: Promise) =
support.diff(oldFile, newFile, patchFile, promise)

override fun inspectPatch(patchFile: String, maxInputBytes: Double, promise: Promise) =
support.inspectPatch(patchFile, maxInputBytes, promise)

override fun verifyPatch(
oldFile: String,
patchFile: String,
expectedFile: String,
maxInputBytes: Double,
maxOutputBytes: Double,
promise: Promise
) = support.verifyPatch(
oldFile,
patchFile,
expectedFile,
maxInputBytes,
maxOutputBytes,
promise
)

override fun startPatch(
jobId: String,
oldFile: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,27 @@ class BsDiffPatchModule(reactContext: ReactApplicationContext) :
fun diff(oldFile: String, newFile: String, patchFile: String, promise: Promise) =
support.diff(oldFile, newFile, patchFile, promise)

@ReactMethod
fun inspectPatch(patchFile: String, maxInputBytes: Double, promise: Promise) =
support.inspectPatch(patchFile, maxInputBytes, promise)

@ReactMethod
fun verifyPatch(
oldFile: String,
patchFile: String,
expectedFile: String,
maxInputBytes: Double,
maxOutputBytes: Double,
promise: Promise
) = support.verifyPatch(
oldFile,
patchFile,
expectedFile,
maxInputBytes,
maxOutputBytes,
promise
)

@ReactMethod
fun startPatch(
jobId: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.modules.core.DeviceEventManagerModule
import java.io.File
import java.util.concurrent.atomic.AtomicInteger

internal class BsDiffPatchModuleSupport(
Expand All @@ -28,6 +29,47 @@ internal class BsDiffPatchModuleSupport(
}
}

fun inspectPatch(patchFile: String, maxInputBytes: Double, promise: Promise) {
taskRunner.execute(promise) {
BsDiffPatchNative.inspectPatch(patchFile, maxInputBytes)
}
}

fun verifyPatch(
oldFile: String,
patchFile: String,
expectedFile: String,
maxInputBytes: Double,
maxOutputBytes: Double,
promise: Promise
) {
taskRunner.execute(promise) {
val temporaryOutput = File.createTempFile(
"bsdiffpatch-verify-",
".tmp",
reactContext.cacheDir
)
if (!temporaryOutput.delete()) {
throw BsDiffPatchException(
"EUNSPECIFIED",
"could not prepare temporary verification output"
)
}
try {
BsDiffPatchNative.verifyPatch(
oldFile,
patchFile,
expectedFile,
temporaryOutput.absolutePath,
maxInputBytes,
maxOutputBytes
)
} finally {
temporaryOutput.delete()
}
}
}

fun startPatch(
jobId: String,
oldFile: String,
Expand Down
Loading
Loading