@bravemobile/react-native-code-push 12.4.1 → 13.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CodePush.podspec +47 -2
- package/README.md +118 -3
- package/THIRD-PARTY-NOTICES +96 -0
- package/android/app/build.gradle +26 -0
- package/android/app/proguard-rules.pro +5 -0
- package/android/app/src/main/cpp/CMakeLists.txt +38 -0
- package/android/app/src/main/cpp/binarypatch_jni.c +181 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/BinaryPatchAttempt.java +92 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/BinaryPatchResult.java +64 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java +46 -11
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushBinaryPatch.java +293 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushConstants.java +25 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushNativeModule.java +79 -88
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateManager.java +159 -4
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateUtils.java +13 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/HDiffPatchNative.java +52 -0
- package/android/app/src/test/java/com/microsoft/codepush/react/CodePushBinaryPatchTest.java +393 -0
- package/android/app/src/test/java/com/microsoft/codepush/react/CodePushUpdateManagerBinaryPatchTest.java +207 -0
- package/android/app/src/test/java/com/microsoft/codepush/react/CodePushUpdateManagerDownloadTest.java +449 -0
- package/android/codepush-export.gradle +115 -0
- package/cli/commands/bundleCommand/bundleCodePush.ts +39 -7
- package/cli/commands/bundleCommand/index.ts +9 -1
- package/cli/commands/releaseCommand/addToReleaseHistory.ts +7 -0
- package/cli/commands/releaseCommand/index.test.ts +118 -0
- package/cli/commands/releaseCommand/index.ts +20 -2
- package/cli/commands/releaseCommand/release.test.ts +582 -0
- package/cli/commands/releaseCommand/release.ts +232 -15
- package/cli/dist/commands/bundleCommand/bundleCodePush.js +26 -7
- package/cli/dist/commands/bundleCommand/index.js +5 -1
- package/cli/dist/commands/releaseCommand/addToReleaseHistory.js +6 -1
- package/cli/dist/commands/releaseCommand/index.js +8 -1
- package/cli/dist/commands/releaseCommand/index.test.js +96 -0
- package/cli/dist/commands/releaseCommand/release.js +165 -17
- package/cli/dist/commands/releaseCommand/release.test.js +367 -0
- package/cli/dist/functions/makeBinaryPatchBundle.js +248 -0
- package/cli/dist/functions/makeBinaryPatchBundle.test.js +321 -0
- package/cli/dist/functions/makeCodePushBundle.js +4 -2
- package/cli/dist/functions/runHermesEmitBinaryCommand.js +98 -27
- package/cli/dist/functions/runHermesEmitBinaryCommand.test.js +100 -0
- package/cli/dist/jest.globalSetup.js +38 -0
- package/cli/dist/utils/binaryPatch.js +86 -0
- package/cli/dist/utils/binaryPatch.test.js +190 -0
- package/cli/functions/makeBinaryPatchBundle.test.ts +410 -0
- package/cli/functions/makeBinaryPatchBundle.ts +367 -0
- package/cli/functions/makeCodePushBundle.ts +4 -2
- package/cli/functions/runHermesEmitBinaryCommand.test.ts +124 -0
- package/cli/functions/runHermesEmitBinaryCommand.ts +112 -26
- package/cli/jest.globalSetup.ts +43 -0
- package/cli/utils/binaryPatch.test.ts +234 -0
- package/cli/utils/binaryPatch.ts +103 -0
- package/cpp/binarypatch/binarypatch_zstd_decompressor.c +187 -0
- package/cpp/binarypatch/binarypatch_zstd_decompressor.h +47 -0
- package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/checksum_plugin.h +52 -0
- package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/hpatch_mt/hpatch_mt.h +147 -0
- package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch.c +3315 -0
- package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch.h +268 -0
- package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch_private.h +297 -0
- package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch_types.h +434 -0
- package/cpp/binarypatch/vendor/zstd/common/allocations.h +55 -0
- package/cpp/binarypatch/vendor/zstd/common/bits.h +205 -0
- package/cpp/binarypatch/vendor/zstd/common/bitstream.h +454 -0
- package/cpp/binarypatch/vendor/zstd/common/compiler.h +464 -0
- package/cpp/binarypatch/vendor/zstd/common/cpu.h +249 -0
- package/cpp/binarypatch/vendor/zstd/common/debug.c +30 -0
- package/cpp/binarypatch/vendor/zstd/common/debug.h +107 -0
- package/cpp/binarypatch/vendor/zstd/common/entropy_common.c +340 -0
- package/cpp/binarypatch/vendor/zstd/common/error_private.c +64 -0
- package/cpp/binarypatch/vendor/zstd/common/error_private.h +158 -0
- package/cpp/binarypatch/vendor/zstd/common/fse.h +625 -0
- package/cpp/binarypatch/vendor/zstd/common/fse_decompress.c +315 -0
- package/cpp/binarypatch/vendor/zstd/common/huf.h +277 -0
- package/cpp/binarypatch/vendor/zstd/common/mem.h +422 -0
- package/cpp/binarypatch/vendor/zstd/common/pool.c +371 -0
- package/cpp/binarypatch/vendor/zstd/common/pool.h +81 -0
- package/cpp/binarypatch/vendor/zstd/common/portability_macros.h +171 -0
- package/cpp/binarypatch/vendor/zstd/common/threading.c +182 -0
- package/cpp/binarypatch/vendor/zstd/common/threading.h +142 -0
- package/cpp/binarypatch/vendor/zstd/common/xxhash.c +18 -0
- package/cpp/binarypatch/vendor/zstd/common/xxhash.h +7094 -0
- package/cpp/binarypatch/vendor/zstd/common/zstd_common.c +48 -0
- package/cpp/binarypatch/vendor/zstd/common/zstd_deps.h +123 -0
- package/cpp/binarypatch/vendor/zstd/common/zstd_internal.h +324 -0
- package/cpp/binarypatch/vendor/zstd/common/zstd_trace.h +156 -0
- package/cpp/binarypatch/vendor/zstd/decompress/huf_decompress.c +1944 -0
- package/cpp/binarypatch/vendor/zstd/decompress/zstd_ddict.c +244 -0
- package/cpp/binarypatch/vendor/zstd/decompress/zstd_ddict.h +44 -0
- package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress.c +2410 -0
- package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress_block.c +2209 -0
- package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress_block.h +73 -0
- package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress_internal.h +240 -0
- package/cpp/binarypatch/vendor/zstd/zstd.h +3206 -0
- package/cpp/binarypatch/vendor/zstd/zstd_errors.h +107 -0
- package/ios/CodePush/CodePush.h +58 -9
- package/ios/CodePush/{CodePush.m → CodePush.mm} +77 -63
- package/ios/CodePush/CodePushBinaryPatch.h +80 -0
- package/ios/CodePush/CodePushBinaryPatch.m +473 -0
- package/ios/CodePush/CodePushPackage.m +183 -2
- package/ios/CodePush/CodePushUpdateUtils.m +49 -1
- package/ios/CodePush.xcodeproj/project.pbxproj +6 -6
- package/package.json +28 -4
- package/react-native.config.js +3 -1
- package/scripts/binary-patch/build-hdiffpatch.sh +86 -0
- package/scripts/export-embedded-bundle.sh +76 -0
- package/src/CodePush.js +190 -96
- package/src/CodePush.test.js +382 -0
- package/src/native/NativeCodePush.ts +162 -0
- package/src/package-mixins.js +24 -11
- package/src/specs/NativeCodePush.ts +43 -0
- package/typings/react-native-code-push.d.ts +77 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushDialog.java +0 -102
- package/src/AlertAdapter.js +0 -24
package/CodePush.podspec
CHANGED
|
@@ -12,14 +12,59 @@ Pod::Spec.new do |s|
|
|
|
12
12
|
s.source = { :git => 'https://github.com/Soomgo-Mobile/react-native-code-push.git', :tag => "v#{s.version}"}
|
|
13
13
|
s.ios.deployment_target = '15.5'
|
|
14
14
|
s.tvos.deployment_target = '15.5'
|
|
15
|
-
s.preserve_paths = '*.js'
|
|
15
|
+
s.preserve_paths = ['*.js', 'cpp/binarypatch/**/*.h']
|
|
16
16
|
s.library = 'z'
|
|
17
|
-
|
|
17
|
+
# The applier that installs a binary patch update is compiled from the C sources the
|
|
18
|
+
# other platform and the host build compile as well, referenced where they live rather
|
|
19
|
+
# than copied here, which is what keeps the appliers of the platforms from drifting
|
|
20
|
+
# apart. Only the decompressing half of zstd is needed, and its assembly fast path is
|
|
21
|
+
# deliberately not vendored, so this list is the same one the other platform builds.
|
|
22
|
+
s.source_files = [
|
|
23
|
+
'ios/CodePush/*.{h,m,mm}',
|
|
24
|
+
'cpp/binarypatch/binarypatch_zstd_decompressor.c',
|
|
25
|
+
'cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch.c',
|
|
26
|
+
'cpp/binarypatch/vendor/zstd/common/debug.c',
|
|
27
|
+
'cpp/binarypatch/vendor/zstd/common/entropy_common.c',
|
|
28
|
+
'cpp/binarypatch/vendor/zstd/common/error_private.c',
|
|
29
|
+
'cpp/binarypatch/vendor/zstd/common/fse_decompress.c',
|
|
30
|
+
'cpp/binarypatch/vendor/zstd/common/xxhash.c',
|
|
31
|
+
'cpp/binarypatch/vendor/zstd/common/zstd_common.c',
|
|
32
|
+
'cpp/binarypatch/vendor/zstd/decompress/huf_decompress.c',
|
|
33
|
+
'cpp/binarypatch/vendor/zstd/decompress/zstd_ddict.c',
|
|
34
|
+
'cpp/binarypatch/vendor/zstd/decompress/zstd_decompress.c',
|
|
35
|
+
'cpp/binarypatch/vendor/zstd/decompress/zstd_decompress_block.c'
|
|
36
|
+
]
|
|
18
37
|
s.public_header_files = ['ios/CodePush/CodePush.h']
|
|
19
38
|
|
|
39
|
+
binary_patch_header_search_paths = '"$(PODS_TARGET_SRCROOT)/cpp/binarypatch" "$(PODS_TARGET_SRCROOT)/cpp/binarypatch/vendor/HDiffPatch" "$(PODS_TARGET_SRCROOT)/cpp/binarypatch/vendor/zstd"'
|
|
40
|
+
# ZSTD_DISABLE_ASM: the assembly fast path is intentionally not vendored.
|
|
41
|
+
# _IS_USED_MULTITHREAD=0: patches are applied on the thread that downloads them.
|
|
42
|
+
binary_patch_preprocessor_definitions = '$(inherited) ZSTD_DISABLE_ASM=1 _IS_USED_MULTITHREAD=0'
|
|
43
|
+
|
|
44
|
+
s.pod_target_xcconfig = {
|
|
45
|
+
'HEADER_SEARCH_PATHS' => binary_patch_header_search_paths,
|
|
46
|
+
'GCC_PREPROCESSOR_DEFINITIONS' => binary_patch_preprocessor_definitions
|
|
47
|
+
}
|
|
48
|
+
|
|
20
49
|
# Note: Even though there are copy/pasted versions of some of these dependencies in the repo,
|
|
21
50
|
# we explicitly let CocoaPods pull in the versions below so all dependencies are resolved and
|
|
22
51
|
# linked properly at a parent workspace level.
|
|
23
52
|
s.dependency 'React-Core'
|
|
24
53
|
s.dependency 'SSZipArchive', '~> 2.5.5'
|
|
54
|
+
|
|
55
|
+
if ENV['RCT_NEW_ARCH_ENABLED'] == '1'
|
|
56
|
+
s.compiler_flags = '-DRCT_NEW_ARCH_ENABLED=1'
|
|
57
|
+
# This replaces pod_target_xcconfig rather than adding to it, so what the applier
|
|
58
|
+
# needs to build has to be carried into it as well.
|
|
59
|
+
s.pod_target_xcconfig = {
|
|
60
|
+
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17',
|
|
61
|
+
'HEADER_SEARCH_PATHS' => "\"$(PODS_ROOT)/Headers/Public/ReactCodegen\" \"${PODS_CONFIGURATION_BUILD_DIR}/ReactCodegen/ReactCodegen.framework/Headers\" #{binary_patch_header_search_paths}",
|
|
62
|
+
'GCC_PREPROCESSOR_DEFINITIONS' => binary_patch_preprocessor_definitions
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
s.dependency 'ReactCodegen'
|
|
66
|
+
s.dependency 'RCTRequired'
|
|
67
|
+
s.dependency 'RCTTypeSafety'
|
|
68
|
+
s.dependency 'ReactCommon/turbomodule/core'
|
|
69
|
+
end
|
|
25
70
|
end
|
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# @bravemobile/react-native-code-push
|
|
2
2
|
|
|
3
|
-
###
|
|
3
|
+
### CodePush without an update server
|
|
4
4
|
|
|
5
5
|
- **No API Server Needed** – Use static hosting solutions (e.g., AWS S3) without maintaining additional API servers.
|
|
6
|
-
- **Familiar API** – Built on `microsoft/react-native-code-push`,
|
|
6
|
+
- **Familiar API** – Built on `microsoft/react-native-code-push`, with the native module migrated to TurboModule.
|
|
7
7
|
- **Flexible Deployment** – Implement your own release workflow, giving you complete control over the deployment process.
|
|
8
8
|
|
|
9
9
|
### 🚀 New Architecture support
|
|
@@ -13,6 +13,9 @@ Supports React Native 0.77 ~ 0.86.
|
|
|
13
13
|
> [!NOTE]
|
|
14
14
|
> If you are using React Native 0.76 or lower, please use version `12.0.2` of this library.
|
|
15
15
|
|
|
16
|
+
> [!IMPORTANT]
|
|
17
|
+
> `13.0.0` migrates the native module to TurboModule. A `13.0.0` binary only runs OTA bundles built with `13.0.0` or later, so ship a new binary version and start its release history fresh when you upgrade.
|
|
18
|
+
|
|
16
19
|
(Tested on the React Native CLI template apps)
|
|
17
20
|
|
|
18
21
|
### ✅ Requirements
|
|
@@ -33,7 +36,7 @@ npm install @bravemobile/react-native-code-push
|
|
|
33
36
|
1. Edit `android/app/build.gradle` file to remove the `apply from: "../../node_modules/..../codepush.gradle"` line.
|
|
34
37
|
|
|
35
38
|
2. The following changes are optional but recommended for cleaning up the old configuration:
|
|
36
|
-
- Since the deployment key is no longer
|
|
39
|
+
- Since the deployment key is no longer used, it is recommended to remove it from your `Info.plist`, `strings.xml`, or JavaScript code.
|
|
37
40
|
- Thanks to Auto Linking, you can remove the `react-native-code-push` module settings from `settings.gradle`.
|
|
38
41
|
|
|
39
42
|
3. Follow the installation guide starting from **'4. "CodePush-ify" your app'**.
|
|
@@ -78,6 +81,10 @@ Run `cd ios && pod install && cd ..`
|
|
|
78
81
|
|
|
79
82
|
(`npx pod-install`, `bundle exec pod install --project-directory=./ios`, ..)
|
|
80
83
|
|
|
84
|
+
The pod compiles the applier that installs binary patch updates from the C sources it
|
|
85
|
+
carries, so there is nothing else to install: CocoaPods builds them along with the rest
|
|
86
|
+
of the pod.
|
|
87
|
+
|
|
81
88
|
|
|
82
89
|
#### (2) Edit `AppDelegate` Code
|
|
83
90
|
|
|
@@ -291,6 +298,36 @@ Please refer to the [CodePushOptions](https://github.com/Soomgo-Mobile/react-nat
|
|
|
291
298
|
- **onDownloadStart:** Triggered when the bundle download begins.
|
|
292
299
|
- **onDownloadSuccess:** Triggered when the bundle download completes successfully.
|
|
293
300
|
- **onSyncError:** Triggered when an unknown error occurs during the update process. (`CodePush.SyncStatus.UNKNOWN_ERROR` status)
|
|
301
|
+
- **onBinaryPatchResult:** Triggered when an update that was published with a binary patch has been downloaded, with the release label and how the patch went. Unlike the callbacks above, this one can also be passed to a single `sync()` call - see below.
|
|
302
|
+
|
|
303
|
+
`onBinaryPatchResult` is called with `{ status: "applied" | "fallback", fallbackReason?: string, applyDurationMs: number }`.
|
|
304
|
+
A `"fallback"` is not a failed update: the update is downloaded in full instead and installed as usual, so the result
|
|
305
|
+
is there to be observed and nothing more. The library neither stores it nor sends it anywhere - an app that wants it in
|
|
306
|
+
its telemetry sends it itself. A callback that throws never fails the update - the error is only logged to the console.
|
|
307
|
+
|
|
308
|
+
Register it on `CodePush({ ... })` like the callbacks above and it covers every sync, whatever the `checkFrequency` is
|
|
309
|
+
and including the `CodePush.sync()` calls you make yourself.
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
export default CodePush({
|
|
313
|
+
checkFrequency: CodePush.CheckFrequency.MANUAL, // or something else
|
|
314
|
+
releaseHistoryFetcher: releaseHistoryFetcher,
|
|
315
|
+
onBinaryPatchResult: (label, result) => {
|
|
316
|
+
// Send it to your own telemetry, if you want it there.
|
|
317
|
+
},
|
|
318
|
+
})(MyApp);
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Pass it to a `sync()` call to override the registered one for that call - to tag the result of one particular sync,
|
|
322
|
+
for instance.
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
CodePush.sync({
|
|
326
|
+
onBinaryPatchResult: (label, result) => {
|
|
327
|
+
// Send it to your own telemetry, if you want it there.
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
```
|
|
294
331
|
|
|
295
332
|
|
|
296
333
|
### 5. Configure the CLI Tool
|
|
@@ -393,6 +430,84 @@ module.exports = Config;
|
|
|
393
430
|
```
|
|
394
431
|
|
|
395
432
|
|
|
433
|
+
### 6. Export the Embedded Bundle (Optional)
|
|
434
|
+
|
|
435
|
+
Only needed if you want to release **binary patch updates** (`release --binary-bundle-path`).
|
|
436
|
+
A binary patch is the difference between the update and the JS bundle that is already
|
|
437
|
+
inside the installed app, so releasing one means holding on to that bundle: the exact
|
|
438
|
+
bytes the build you shipped to the store embedded.
|
|
439
|
+
|
|
440
|
+
The library ships a hook for each platform that copies the freshly compiled bundle out of
|
|
441
|
+
the build, together with a `binary-patch-base.json` record describing it.
|
|
442
|
+
|
|
443
|
+
**Android** - apply the Gradle script in your app module's `android/app/build.gradle`:
|
|
444
|
+
|
|
445
|
+
```groovy
|
|
446
|
+
apply plugin: "com.android.application"
|
|
447
|
+
apply plugin: "com.facebook.react"
|
|
448
|
+
|
|
449
|
+
react {
|
|
450
|
+
// ...
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
apply from: "../../node_modules/@bravemobile/react-native-code-push/android/codepush-export.gradle"
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
The line can go anywhere in the file (if you use `ext.codePushExportDir`, set it before the
|
|
457
|
+
`apply from:` line). Every variant that bundles JS then exports to
|
|
458
|
+
`android/app/build/codepush/embedded-bundle/<variant>/` after it is bundled. Pass
|
|
459
|
+
`-PcodePushExportDir=<path>` (or set `ext.codePushExportDir`) to export somewhere else; the
|
|
460
|
+
`<variant>` directory is appended either way.
|
|
461
|
+
|
|
462
|
+
**iOS** - call the export script at the end of the **"Bundle React Native code and images"**
|
|
463
|
+
build phase. In Xcode, open that phase
|
|
464
|
+
in your app target's **Build Phases** tab and append the last line below to its script:
|
|
465
|
+
|
|
466
|
+
```bash
|
|
467
|
+
/bin/sh -c "\"$WITH_ENVIRONMENT\" \"$REACT_NATIVE_XCODE\""
|
|
468
|
+
|
|
469
|
+
"$SRCROOT/../node_modules/@bravemobile/react-native-code-push/scripts/export-embedded-bundle.sh"
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
The export lands in `$BUILD_DIR/codepush/embedded-bundle/$CONFIGURATION-$PLATFORM_NAME/`.
|
|
473
|
+
Set the `CODEPUSH_EXPORT_DIR` environment variable to export somewhere else; the
|
|
474
|
+
`$CONFIGURATION-$PLATFORM_NAME` directory is appended either way.
|
|
475
|
+
|
|
476
|
+
**Archive the export per binary release.** Whatever pipeline builds your store binary
|
|
477
|
+
should keep that build's export somewhere durable, organized by binary version, so a later
|
|
478
|
+
release can fetch the bundle that matches the binary it targets:
|
|
479
|
+
|
|
480
|
+
```bash
|
|
481
|
+
# Android, after ./gradlew :app:assembleRelease
|
|
482
|
+
aws s3 cp --recursive \
|
|
483
|
+
android/app/build/codepush/embedded-bundle/release \
|
|
484
|
+
"s3://your-bucket/binaries/android/$BINARY_VERSION/"
|
|
485
|
+
|
|
486
|
+
# iOS - point the export at a path the pipeline knows, since $BUILD_DIR only exists inside the build
|
|
487
|
+
export CODEPUSH_EXPORT_DIR="$PWD/codepush-export"
|
|
488
|
+
xcodebuild -workspace ios/YourApp.xcworkspace -scheme YourApp -configuration Release archive # ...
|
|
489
|
+
aws s3 cp --recursive \
|
|
490
|
+
"$CODEPUSH_EXPORT_DIR/Release-iphoneos" \
|
|
491
|
+
"s3://your-bucket/binaries/ios/$BINARY_VERSION/"
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
To release a patch later, download the export and pass the bundle's path to
|
|
495
|
+
`--binary-bundle-path`:
|
|
496
|
+
|
|
497
|
+
```bash
|
|
498
|
+
aws s3 cp --recursive "s3://your-bucket/binaries/android/1.0.0/" ./binary/
|
|
499
|
+
npx code-push release -b 1.0.0 -v 1.0.1 -p android \
|
|
500
|
+
--binary-bundle-path ./binary/index.android.bundle
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
`release` also uses the record to verify the base bundle it was handed - see
|
|
504
|
+
[Verifying the base bundle](cli/README.md#verifying-the-base-bundle) for details.
|
|
505
|
+
|
|
506
|
+
> [!NOTE]
|
|
507
|
+
> Applying these hooks automatically through the Expo config plugin (`app.plugin.js`) is
|
|
508
|
+
> not implemented yet.
|
|
509
|
+
|
|
510
|
+
|
|
396
511
|
## 🚀 CLI Tool Usage
|
|
397
512
|
|
|
398
513
|
> [!TIP]
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
THIRD PARTY NOTICES
|
|
2
|
+
===================
|
|
3
|
+
|
|
4
|
+
This package includes third party source code. The notices below apply to that
|
|
5
|
+
code only; the rest of this project is covered by LICENSE.md.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-------------------------------------------------------------------------------
|
|
9
|
+
HDiffPatch
|
|
10
|
+
-------------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
Version: v5.1.3
|
|
13
|
+
Source: https://github.com/sisong/HDiffPatch/tree/v5.1.3
|
|
14
|
+
License: MIT
|
|
15
|
+
Vendored: cpp/binarypatch/vendor/HDiffPatch/
|
|
16
|
+
|
|
17
|
+
Only the files required to apply a patch on device are vendored, from
|
|
18
|
+
libHDiffPatch/HPatch: patch.c, patch.h, patch_private.h, patch_types.h,
|
|
19
|
+
checksum_plugin.h and hpatch_mt/hpatch_mt.h. The diff side of HDiffPatch is not
|
|
20
|
+
vendored; the `hdiffz` command line tool is built from upstream sources by
|
|
21
|
+
scripts/binary-patch/build-hdiffpatch.sh and is not distributed with this package.
|
|
22
|
+
|
|
23
|
+
MIT License
|
|
24
|
+
|
|
25
|
+
HDiffPatch
|
|
26
|
+
Copyright (c) 2012-2025 housisong
|
|
27
|
+
|
|
28
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
29
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
30
|
+
in the Software without restriction, including without limitation the rights
|
|
31
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
32
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
33
|
+
furnished to do so, subject to the following conditions:
|
|
34
|
+
|
|
35
|
+
The above copyright notice and this permission notice shall be included in all
|
|
36
|
+
copies or substantial portions of the Software.
|
|
37
|
+
|
|
38
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
39
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
40
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
41
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
42
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
43
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
44
|
+
SOFTWARE.
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
-------------------------------------------------------------------------------
|
|
48
|
+
Zstandard (zstd)
|
|
49
|
+
-------------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
Version: 1.5.7
|
|
52
|
+
Commit: 68c88c7c7ad22b5e6882a5296ef96d27dc8750c4
|
|
53
|
+
Source: https://github.com/sisong/zstd
|
|
54
|
+
(the fork HDiffPatch builds against, derived from
|
|
55
|
+
https://github.com/facebook/zstd)
|
|
56
|
+
The files here are a snapshot of that fork; the copies in this
|
|
57
|
+
repository are the version the appliers are built from, and
|
|
58
|
+
scripts/binary-patch/build-hdiffpatch.sh pins the same commit so the
|
|
59
|
+
`hdiffz` it builds compresses with the code that is vendored here.
|
|
60
|
+
License: BSD-3-Clause
|
|
61
|
+
Vendored: cpp/binarypatch/vendor/zstd/
|
|
62
|
+
|
|
63
|
+
Only the decompression path is vendored (lib/common and lib/decompress). The
|
|
64
|
+
amd64 assembly implementation of the Huffman decoder is not vendored because the
|
|
65
|
+
appliers are compiled with ZSTD_DISABLE_ASM=1.
|
|
66
|
+
|
|
67
|
+
BSD License
|
|
68
|
+
|
|
69
|
+
For Zstandard software
|
|
70
|
+
|
|
71
|
+
Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.
|
|
72
|
+
|
|
73
|
+
Redistribution and use in source and binary forms, with or without modification,
|
|
74
|
+
are permitted provided that the following conditions are met:
|
|
75
|
+
|
|
76
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
77
|
+
list of conditions and the following disclaimer.
|
|
78
|
+
|
|
79
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
80
|
+
this list of conditions and the following disclaimer in the documentation
|
|
81
|
+
and/or other materials provided with the distribution.
|
|
82
|
+
|
|
83
|
+
* Neither the name Facebook, nor Meta, nor the names of its contributors may
|
|
84
|
+
be used to endorse or promote products derived from this software without
|
|
85
|
+
specific prior written permission.
|
|
86
|
+
|
|
87
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
88
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
89
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
90
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
91
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
92
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
93
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
94
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
95
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
96
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/android/app/build.gradle
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
apply plugin: "com.android.library"
|
|
2
|
+
apply plugin: "com.facebook.react"
|
|
2
3
|
|
|
3
4
|
def DEFAULT_COMPILE_SDK_VERSION = 26
|
|
4
5
|
def DEFAULT_BUILD_TOOLS_VERSION = "26.0.3"
|
|
5
6
|
def DEFAULT_TARGET_SDK_VERSION = 26
|
|
6
7
|
def DEFAULT_MIN_SDK_VERSION = 16
|
|
7
8
|
|
|
9
|
+
react {
|
|
10
|
+
jsRootDir = file("../..")
|
|
11
|
+
libraryName = "RNCodePushSpec"
|
|
12
|
+
codegenJavaPackageName = "com.microsoft.codepush.react"
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
android {
|
|
9
16
|
namespace "com.microsoft.codepush.react"
|
|
10
17
|
|
|
@@ -25,8 +32,27 @@ android {
|
|
|
25
32
|
defaultConfig {
|
|
26
33
|
consumerProguardFiles 'proguard-rules.pro'
|
|
27
34
|
}
|
|
35
|
+
|
|
36
|
+
// Builds the binary patch applier. Its sources are the shared ones at the root of the
|
|
37
|
+
// repository, which the CMake file points at; nothing is compiled twice.
|
|
38
|
+
externalNativeBuild {
|
|
39
|
+
cmake {
|
|
40
|
+
path "src/main/cpp/CMakeLists.txt"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
testOptions {
|
|
45
|
+
// The unit tests exercise plain logic, so the android framework classes they touch
|
|
46
|
+
// in passing (logging above all) are allowed to do nothing instead of throwing.
|
|
47
|
+
unitTests.returnDefaultValues = true
|
|
48
|
+
}
|
|
28
49
|
}
|
|
29
50
|
|
|
30
51
|
dependencies {
|
|
31
52
|
implementation "com.facebook.react:react-native:+"
|
|
53
|
+
|
|
54
|
+
testImplementation "junit:junit:4.13.2"
|
|
55
|
+
// org.json ships as stubs in the android framework jar, and the unit tests parse real
|
|
56
|
+
// manifests, so they need an implementation that actually parses.
|
|
57
|
+
testImplementation "org.json:json:20231013"
|
|
32
58
|
}
|
|
@@ -37,3 +37,8 @@
|
|
|
37
37
|
|
|
38
38
|
# Can't find referenced class org.bouncycastle.**
|
|
39
39
|
-dontwarn com.nimbusds.jose.**
|
|
40
|
+
|
|
41
|
+
# The binary patch applier is found by name from the native library.
|
|
42
|
+
-keepclasseswithmembernames class com.microsoft.codepush.react.HDiffPatchNative {
|
|
43
|
+
native <methods>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.13)
|
|
2
|
+
|
|
3
|
+
project(codepush-binarypatch C)
|
|
4
|
+
|
|
5
|
+
# The applier sources live outside the platform directories because the host build and
|
|
6
|
+
# the other platform compile the very same files. Referencing them where they are is
|
|
7
|
+
# what keeps the appliers of the platforms from drifting apart, so they are deliberately
|
|
8
|
+
# not copied into this directory.
|
|
9
|
+
set(BINARY_PATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../cpp/binarypatch)
|
|
10
|
+
|
|
11
|
+
add_library(codepush-binarypatch SHARED
|
|
12
|
+
${CMAKE_CURRENT_SOURCE_DIR}/binarypatch_jni.c
|
|
13
|
+
${BINARY_PATCH_DIR}/binarypatch_zstd_decompressor.c
|
|
14
|
+
${BINARY_PATCH_DIR}/vendor/HDiffPatch/libHDiffPatch/HPatch/patch.c
|
|
15
|
+
${BINARY_PATCH_DIR}/vendor/zstd/common/debug.c
|
|
16
|
+
${BINARY_PATCH_DIR}/vendor/zstd/common/entropy_common.c
|
|
17
|
+
${BINARY_PATCH_DIR}/vendor/zstd/common/error_private.c
|
|
18
|
+
${BINARY_PATCH_DIR}/vendor/zstd/common/fse_decompress.c
|
|
19
|
+
${BINARY_PATCH_DIR}/vendor/zstd/common/xxhash.c
|
|
20
|
+
${BINARY_PATCH_DIR}/vendor/zstd/common/zstd_common.c
|
|
21
|
+
${BINARY_PATCH_DIR}/vendor/zstd/decompress/huf_decompress.c
|
|
22
|
+
${BINARY_PATCH_DIR}/vendor/zstd/decompress/zstd_ddict.c
|
|
23
|
+
${BINARY_PATCH_DIR}/vendor/zstd/decompress/zstd_decompress.c
|
|
24
|
+
${BINARY_PATCH_DIR}/vendor/zstd/decompress/zstd_decompress_block.c)
|
|
25
|
+
|
|
26
|
+
target_include_directories(codepush-binarypatch PRIVATE
|
|
27
|
+
${BINARY_PATCH_DIR}
|
|
28
|
+
${BINARY_PATCH_DIR}/vendor/HDiffPatch
|
|
29
|
+
${BINARY_PATCH_DIR}/vendor/zstd)
|
|
30
|
+
|
|
31
|
+
# ZSTD_DISABLE_ASM: the assembly fast path is intentionally not vendored.
|
|
32
|
+
# _IS_USED_MULTITHREAD=0: patches are applied on the thread that downloads them.
|
|
33
|
+
target_compile_definitions(codepush-binarypatch PRIVATE
|
|
34
|
+
ZSTD_DISABLE_ASM=1
|
|
35
|
+
_IS_USED_MULTITHREAD=0)
|
|
36
|
+
|
|
37
|
+
find_library(log-lib log)
|
|
38
|
+
target_link_libraries(codepush-binarypatch ${log-lib})
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* JNI entry point of the CodePush binary patch applier.
|
|
3
|
+
*
|
|
4
|
+
* The applier itself is the shared C code one directory tree up, which the host build and
|
|
5
|
+
* the other platform compile as well; this file only moves data across the JNI boundary
|
|
6
|
+
* and turns a failure into the result code `HDiffPatchNative` hands back to Java.
|
|
7
|
+
*
|
|
8
|
+
* Memory contract, the same one the host build documents:
|
|
9
|
+
* - the base bundle is held whole, because the patches are produced with `hdiffz -m`,
|
|
10
|
+
* which patches with random access to the base data
|
|
11
|
+
* - the patch is held whole, being far smaller than either bundle
|
|
12
|
+
* - the restored bundle is written sequentially to a file, so a patch session never
|
|
13
|
+
* holds two bundles at once
|
|
14
|
+
*
|
|
15
|
+
* A patch carries no checksum of the base data and its zstd streams carry no content
|
|
16
|
+
* checksum, so a successful apply here is not proof of a correct result. The caller
|
|
17
|
+
* verifies the base and target hashes; this file cannot.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
#include <jni.h>
|
|
21
|
+
#include <stdlib.h>
|
|
22
|
+
#include <stdio.h>
|
|
23
|
+
#include <string.h>
|
|
24
|
+
|
|
25
|
+
#include <android/log.h>
|
|
26
|
+
|
|
27
|
+
#include "binarypatch_zstd_decompressor.h"
|
|
28
|
+
#include "libHDiffPatch/HPatch/patch.h"
|
|
29
|
+
|
|
30
|
+
#define LOG_TAG "ReactNative"
|
|
31
|
+
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
|
32
|
+
|
|
33
|
+
/* Mirrored by CodePushBinaryPatch.PatchApplier; the two lists have to stay in step. */
|
|
34
|
+
#define RESULT_OK 0
|
|
35
|
+
#define RESULT_INVALID_ARGUMENT 1
|
|
36
|
+
#define RESULT_IO_ERROR 2
|
|
37
|
+
#define RESULT_INVALID_HEADER 3
|
|
38
|
+
#define RESULT_UNSUPPORTED_COMPRESSION 4
|
|
39
|
+
#define RESULT_SIZE_MISMATCH 5
|
|
40
|
+
#define RESULT_APPLY_FAILED 6
|
|
41
|
+
|
|
42
|
+
/* Scratch buffer handed to patch_decompress_with_cache() to reduce stream reads. */
|
|
43
|
+
#define APPLY_CACHE_SIZE (1024 * 1024)
|
|
44
|
+
|
|
45
|
+
typedef struct {
|
|
46
|
+
FILE* file;
|
|
47
|
+
hpatch_StreamPos_t writtenSize;
|
|
48
|
+
} TSequentialFileWriter;
|
|
49
|
+
|
|
50
|
+
static hpatch_BOOL _write_sequential(const hpatch_TStreamOutput* stream,
|
|
51
|
+
hpatch_StreamPos_t writeToPos,
|
|
52
|
+
const unsigned char* data,
|
|
53
|
+
const unsigned char* data_end) {
|
|
54
|
+
TSequentialFileWriter* self = (TSequentialFileWriter*)stream->streamImport;
|
|
55
|
+
const size_t length = (size_t)(data_end - data);
|
|
56
|
+
/* patch_decompress_with_cache() only ever appends; anything else is a bug. */
|
|
57
|
+
if (writeToPos != self->writtenSize) {
|
|
58
|
+
return hpatch_FALSE;
|
|
59
|
+
}
|
|
60
|
+
if (fwrite(data, 1, length, self->file) != length) {
|
|
61
|
+
return hpatch_FALSE;
|
|
62
|
+
}
|
|
63
|
+
self->writtenSize += length;
|
|
64
|
+
return hpatch_TRUE;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
JNIEXPORT jint JNICALL
|
|
68
|
+
Java_com_microsoft_codepush_react_HDiffPatchNative_applyPatch(JNIEnv* env,
|
|
69
|
+
jclass clazz,
|
|
70
|
+
jbyteArray base,
|
|
71
|
+
jbyteArray patch,
|
|
72
|
+
jstring outputPath,
|
|
73
|
+
jlong expectedTargetSize) {
|
|
74
|
+
jbyte* baseData = NULL;
|
|
75
|
+
jbyte* patchData = NULL;
|
|
76
|
+
const char* outputPathChars = NULL;
|
|
77
|
+
unsigned char* cache = NULL;
|
|
78
|
+
jsize baseSize = 0;
|
|
79
|
+
jsize patchSize = 0;
|
|
80
|
+
hpatch_compressedDiffInfo diffInfo;
|
|
81
|
+
hpatch_TDecompress decompressor;
|
|
82
|
+
hpatch_TStreamInput baseStream;
|
|
83
|
+
hpatch_TStreamInput patchStream;
|
|
84
|
+
hpatch_TStreamOutput targetStream;
|
|
85
|
+
TSequentialFileWriter writer;
|
|
86
|
+
hpatch_BOOL applied;
|
|
87
|
+
int result = RESULT_OK;
|
|
88
|
+
|
|
89
|
+
(void)clazz;
|
|
90
|
+
|
|
91
|
+
if ((base == NULL) || (patch == NULL) || (outputPath == NULL) || (expectedTargetSize <= 0)) {
|
|
92
|
+
return RESULT_INVALID_ARGUMENT;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
baseSize = (*env)->GetArrayLength(env, base);
|
|
96
|
+
patchSize = (*env)->GetArrayLength(env, patch);
|
|
97
|
+
baseData = (*env)->GetByteArrayElements(env, base, NULL);
|
|
98
|
+
patchData = (*env)->GetByteArrayElements(env, patch, NULL);
|
|
99
|
+
outputPathChars = (*env)->GetStringUTFChars(env, outputPath, NULL);
|
|
100
|
+
if ((baseData == NULL) || (patchData == NULL) || (outputPathChars == NULL)) {
|
|
101
|
+
LOGE("[CodePush] out of memory while reading the binary patch inputs");
|
|
102
|
+
result = RESULT_IO_ERROR;
|
|
103
|
+
goto cleanup;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
binarypatch_zstd_decompressor_init(&decompressor);
|
|
107
|
+
|
|
108
|
+
if (!getCompressedDiffInfo_mem(&diffInfo, (const unsigned char*)patchData,
|
|
109
|
+
(const unsigned char*)patchData + patchSize)) {
|
|
110
|
+
LOGE("[CodePush] the binary patch header could not be read");
|
|
111
|
+
result = RESULT_INVALID_HEADER;
|
|
112
|
+
goto cleanup;
|
|
113
|
+
}
|
|
114
|
+
if ((strlen(diffInfo.compressType) > 0) && !decompressor.is_can_open(diffInfo.compressType)) {
|
|
115
|
+
LOGE("[CodePush] the binary patch uses an unsupported codec: %s", diffInfo.compressType);
|
|
116
|
+
result = RESULT_UNSUPPORTED_COMPRESSION;
|
|
117
|
+
goto cleanup;
|
|
118
|
+
}
|
|
119
|
+
if (diffInfo.oldDataSize != (hpatch_StreamPos_t)baseSize) {
|
|
120
|
+
LOGE("[CodePush] the binary patch expects a %llu byte base bundle, this one is %llu bytes",
|
|
121
|
+
(unsigned long long)diffInfo.oldDataSize, (unsigned long long)baseSize);
|
|
122
|
+
result = RESULT_SIZE_MISMATCH;
|
|
123
|
+
goto cleanup;
|
|
124
|
+
}
|
|
125
|
+
if (diffInfo.newDataSize != (hpatch_StreamPos_t)expectedTargetSize) {
|
|
126
|
+
LOGE("[CodePush] the binary patch produces %llu bytes, the manifest promises %llu",
|
|
127
|
+
(unsigned long long)diffInfo.newDataSize, (unsigned long long)expectedTargetSize);
|
|
128
|
+
result = RESULT_SIZE_MISMATCH;
|
|
129
|
+
goto cleanup;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
cache = (unsigned char*)malloc(APPLY_CACHE_SIZE);
|
|
133
|
+
if (cache == NULL) {
|
|
134
|
+
LOGE("[CodePush] out of memory while allocating the binary patch cache");
|
|
135
|
+
result = RESULT_IO_ERROR;
|
|
136
|
+
goto cleanup;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
writer.file = fopen(outputPathChars, "wb");
|
|
140
|
+
writer.writtenSize = 0;
|
|
141
|
+
if (writer.file == NULL) {
|
|
142
|
+
LOGE("[CodePush] the restored bundle could not be opened for writing");
|
|
143
|
+
result = RESULT_IO_ERROR;
|
|
144
|
+
goto cleanup;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
mem_as_hStreamInput(&baseStream, (const unsigned char*)baseData, (const unsigned char*)baseData + baseSize);
|
|
148
|
+
mem_as_hStreamInput(&patchStream, (const unsigned char*)patchData, (const unsigned char*)patchData + patchSize);
|
|
149
|
+
memset(&targetStream, 0, sizeof(targetStream));
|
|
150
|
+
targetStream.streamImport = &writer;
|
|
151
|
+
targetStream.streamSize = diffInfo.newDataSize;
|
|
152
|
+
targetStream.write = _write_sequential;
|
|
153
|
+
|
|
154
|
+
applied = patch_decompress_with_cache(&targetStream, &baseStream, &patchStream, &decompressor,
|
|
155
|
+
cache, cache + APPLY_CACHE_SIZE);
|
|
156
|
+
if (fclose(writer.file) != 0) {
|
|
157
|
+
LOGE("[CodePush] the restored bundle could not be flushed to disk");
|
|
158
|
+
result = RESULT_IO_ERROR;
|
|
159
|
+
goto cleanup;
|
|
160
|
+
}
|
|
161
|
+
if (!applied) {
|
|
162
|
+
LOGE("[CodePush] applying the binary patch failed (decError=%d)", (int)decompressor.decError);
|
|
163
|
+
result = RESULT_APPLY_FAILED;
|
|
164
|
+
goto cleanup;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
cleanup:
|
|
168
|
+
free(cache);
|
|
169
|
+
if (outputPathChars != NULL) {
|
|
170
|
+
(*env)->ReleaseStringUTFChars(env, outputPath, outputPathChars);
|
|
171
|
+
}
|
|
172
|
+
/* JNI_ABORT: neither array is written to, so nothing has to be copied back. */
|
|
173
|
+
if (patchData != NULL) {
|
|
174
|
+
(*env)->ReleaseByteArrayElements(env, patch, patchData, JNI_ABORT);
|
|
175
|
+
}
|
|
176
|
+
if (baseData != NULL) {
|
|
177
|
+
(*env)->ReleaseByteArrayElements(env, base, baseData, JNI_ABORT);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return (jint)result;
|
|
181
|
+
}
|