react-native-update 10.47.0 → 10.48.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/android/jni/Android.mk +2 -0
- package/android/jni/hpatch.c +57 -2
- package/android/lib/arm64-v8a/librnupdate.so +0 -0
- package/android/lib/armeabi-v7a/librnupdate.so +0 -0
- package/android/lib/x86/librnupdate.so +0 -0
- package/android/lib/x86_64/librnupdate.so +0 -0
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +19 -4
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java +2 -0
- package/android/src/main/java/cn/reactnative/modules/update/ErrorCodes.java +1 -0
- package/android/src/main/java/cn/reactnative/modules/update/NativeUpdateCore.java +12 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +30 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +13 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleSupport.java +1 -0
- package/android/src/newarch/cn/reactnative/modules/update/UpdateModule.java +5 -0
- package/android/src/oldarch/cn/reactnative/modules/update/UpdateModule.java +5 -0
- package/cpp/patch_core/error_codes.h +3 -0
- package/cpp/patch_core/hbc_transform.cpp +143 -0
- package/cpp/patch_core/hbc_transform.h +53 -0
- package/cpp/patch_core/hbc_transform_wire.cpp +321 -0
- package/cpp/patch_core/hbc_transform_wire.h +52 -0
- package/cpp/patch_core/patch_core.cpp +126 -4
- package/cpp/patch_core/patch_core.h +5 -0
- package/cpp/patch_core/patch_core_android.cpp +3 -1
- package/cpp/patch_core/tests/fixtures/rawstored.lzma2label.patch.bin +0 -0
- package/cpp/patch_core/tests/fixtures/rawstored.new.bin +0 -0
- package/cpp/patch_core/tests/fixtures/rawstored.old.bin +0 -0
- package/cpp/patch_core/tests/fixtures/v96.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v96.meta.json +1 -0
- package/cpp/patch_core/tests/fixtures/v96.streampatch.bin +0 -0
- package/cpp/patch_core/tests/fixtures/v96.t.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v96.tpatch.bin +0 -0
- package/cpp/patch_core/tests/fixtures/v96.tstreampatch.bin +0 -0
- package/cpp/patch_core/tests/fixtures/v96b.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v98.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v98.t.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v98b.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v98b.t.hbc +0 -0
- package/cpp/patch_core/tests/hbc_transform_test.cpp +229 -0
- package/cpp/patch_core/tests/patch_core_test.cpp +150 -1
- package/cpp/patch_core/update_core_android.cpp +10 -0
- package/harmony/pushy/src/main/cpp/CMakeLists.txt +2 -0
- package/harmony/pushy/src/main/cpp/pushy.cpp +18 -1
- package/harmony/pushy/src/main/ets/DownloadTask.ts +21 -0
- package/harmony/pushy/src/main/ets/NativePatchCore.ts +4 -0
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +12 -0
- package/harmony/pushy/src/main/ets/UpdateContext.ts +46 -0
- package/harmony/pushy.har +0 -0
- package/ios/RCTPushy/RCTPushy.mm +64 -0
- package/package.json +1 -1
- package/react-native-update.podspec +2 -0
- package/src/NativePushy.ts +1 -0
- package/src/client.ts +46 -0
- package/src/context.ts +4 -0
- package/src/core.ts +4 -0
- package/src/error.ts +1 -0
- package/src/locales/en.ts +2 -0
- package/src/locales/zh.ts +2 -0
- package/src/provider.tsx +9 -0
- package/src/type.ts +2 -0
package/android/jni/Android.mk
CHANGED
|
@@ -21,6 +21,8 @@ Hdp_Files := \
|
|
|
21
21
|
|
|
22
22
|
LOCAL_SRC_FILES := \
|
|
23
23
|
../../cpp/patch_core/archive_patch_core.cpp \
|
|
24
|
+
../../cpp/patch_core/hbc_transform.cpp \
|
|
25
|
+
../../cpp/patch_core/hbc_transform_wire.cpp \
|
|
24
26
|
../../cpp/patch_core/patch_core.cpp \
|
|
25
27
|
../../cpp/patch_core/patch_core_android.cpp \
|
|
26
28
|
../../cpp/patch_core/state_core.cpp \
|
package/android/jni/hpatch.c
CHANGED
|
@@ -63,7 +63,9 @@ static int hpatch_by_stream(const hpatch_TStreamInput* old,hpatch_BOOL isLoadOld
|
|
|
63
63
|
_check(old->streamSize==patInfo->oldDataSize,kHPatch_error_old_size);
|
|
64
64
|
_check(out_new->streamSize>=patInfo->newDataSize,kHPatch_error_new_size);
|
|
65
65
|
out_new->streamSize=patInfo->newDataSize;
|
|
66
|
-
|
|
66
|
+
// v5 writer 可能保留 lzma2 标签但以 RAW 存储小 diff;compressedSize==0
|
|
67
|
+
// 才是格式里的未压缩判据,不能按标签强行走解压。
|
|
68
|
+
if ((strlen(patInfo->compressType)>0)&&(patInfo->compressedSize>0)){
|
|
67
69
|
decompressPlugin=getDecompressPlugin(patInfo->compressType);
|
|
68
70
|
_check(decompressPlugin,kHPatch_error_compressType);
|
|
69
71
|
}
|
|
@@ -105,10 +107,57 @@ int hpatch_by_mem(const uint8_t* old,size_t oldsize,uint8_t* newBuf,size_t newsi
|
|
|
105
107
|
return hpatch_by_stream(&oldStream,hpatch_FALSE,&patStream,&newStream,patInfo);
|
|
106
108
|
}
|
|
107
109
|
|
|
110
|
+
// HDIFF13(diffStream 产物,v2 轨道的大 bundle patch):流式应用,
|
|
111
|
+
// 内存 = 解压缓存 + IO 缓冲(+ old ≤ 8MB 时的整载优化),与 single 路径同级。
|
|
112
|
+
static int hpatch_v13_by_stream(const hpatch_TStreamInput* old,hpatch_BOOL isLoadOldAllToMem,
|
|
113
|
+
const hpatch_TStreamInput* pat,hpatch_TStreamOutput* out_new){
|
|
114
|
+
int result=kHPatch_ok;
|
|
115
|
+
int _isInClear=hpatch_FALSE;
|
|
116
|
+
hpatch_TDecompress* decompressPlugin=0;
|
|
117
|
+
uint8_t* temp_cache=0;
|
|
118
|
+
size_t temp_cache_size;
|
|
119
|
+
hpatch_compressedDiffInfo patInfo;
|
|
120
|
+
hpatch_TStreamInput _old;
|
|
121
|
+
{// info
|
|
122
|
+
_check(getCompressedDiffInfo(&patInfo,pat),kHPatch_error_info);
|
|
123
|
+
_check(old->streamSize==patInfo.oldDataSize,kHPatch_error_old_size);
|
|
124
|
+
_check(out_new->streamSize>=patInfo.newDataSize,kHPatch_error_new_size);
|
|
125
|
+
out_new->streamSize=patInfo.newDataSize;
|
|
126
|
+
if (strlen(patInfo.compressType)>0){
|
|
127
|
+
decompressPlugin=getDecompressPlugin(patInfo.compressType);
|
|
128
|
+
_check(decompressPlugin,kHPatch_error_compressType);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
{// mem
|
|
132
|
+
size_t mem_size;
|
|
133
|
+
size_t oldSize=(size_t)old->streamSize;
|
|
134
|
+
isLoadOldAllToMem=isLoadOldAllToMem&&(old->streamSize<=kMaxLoadMemOldSize);
|
|
135
|
+
temp_cache_size=hpatch_kStreamCacheSize*8+hpatch_kFileIOBufBetterSize*3;
|
|
136
|
+
mem_size=temp_cache_size+(isLoadOldAllToMem?oldSize:0);
|
|
137
|
+
temp_cache=malloc(mem_size);
|
|
138
|
+
_check(temp_cache,kHPatch_error_malloc);
|
|
139
|
+
if (isLoadOldAllToMem){//load old to mem
|
|
140
|
+
uint8_t* oldMem=temp_cache+temp_cache_size;
|
|
141
|
+
_check(old->read(old,0,oldMem,oldMem+oldSize),kHPatch_error_old_fread);
|
|
142
|
+
mem_as_hStreamInput(&_old,oldMem,oldMem+oldSize);
|
|
143
|
+
old=&_old;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
_check(patch_decompress_with_cache(out_new,old,pat,decompressPlugin,
|
|
148
|
+
temp_cache,temp_cache+temp_cache_size),kHPatch_error_patch);
|
|
149
|
+
|
|
150
|
+
_clear:
|
|
151
|
+
_isInClear=hpatch_TRUE;
|
|
152
|
+
if (temp_cache){ free(temp_cache); temp_cache=0; }
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
|
|
108
156
|
int hpatch_by_file(const char* oldfile, const char* newfile, const char* patchfile){
|
|
109
157
|
int result=kHPatch_ok;
|
|
110
158
|
int _isInClear=hpatch_FALSE;
|
|
111
159
|
int patch_result;
|
|
160
|
+
hpatch_singleCompressedDiffInfo singleInfo;
|
|
112
161
|
hpatch_TFileStreamInput oldStream;
|
|
113
162
|
hpatch_TFileStreamInput patStream;
|
|
114
163
|
hpatch_TFileStreamOutput newStream;
|
|
@@ -120,7 +169,13 @@ int hpatch_by_file(const char* oldfile, const char* newfile, const char* patchfi
|
|
|
120
169
|
_check(hpatch_TFileStreamInput_open(&patStream,patchfile),kHPatch_error_pat_fopen);
|
|
121
170
|
_check(hpatch_TFileStreamOutput_open(&newStream,newfile,~(hpatch_StreamPos_t)0),kHPatch_error_new_fopen);
|
|
122
171
|
|
|
123
|
-
|
|
172
|
+
// 按 patch 头自动分派格式:single(HDIFFSF20,现状)或 stream(HDIFF13,
|
|
173
|
+
// v2 轨道大 bundle)。老客户端只会收到 single;能力门控在服务端。
|
|
174
|
+
if (getSingleCompressedDiffInfo(&singleInfo,&patStream.base,0)){
|
|
175
|
+
patch_result=hpatch_by_stream(&oldStream.base,hpatch_TRUE,&patStream.base,&newStream.base,&singleInfo);
|
|
176
|
+
}else{
|
|
177
|
+
patch_result=hpatch_v13_by_stream(&oldStream.base,hpatch_TRUE,&patStream.base,&newStream.base);
|
|
178
|
+
}
|
|
124
179
|
if (patch_result!=kHPatch_ok){
|
|
125
180
|
_check(!oldStream.fileError,kHPatch_error_old_fread);
|
|
126
181
|
_check(!patStream.fileError,kHPatch_error_pat_fread);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -53,6 +53,17 @@ class DownloadTask implements Runnable {
|
|
|
53
53
|
final ArrayList<String> copyFroms = new ArrayList<String>();
|
|
54
54
|
final ArrayList<String> copyTos = new ArrayList<String>();
|
|
55
55
|
final ArrayList<String> deletes = new ArrayList<String>();
|
|
56
|
+
// __diff.json 的 hbcTransform 元数据(按 patch 条目名索引);
|
|
57
|
+
// 为空对象/缺失时返回 ""(native 走现状路径)
|
|
58
|
+
JSONObject hbcTransform;
|
|
59
|
+
|
|
60
|
+
String hbcTransformMetaFor(String patchEntryName) {
|
|
61
|
+
if (hbcTransform == null) {
|
|
62
|
+
return "";
|
|
63
|
+
}
|
|
64
|
+
JSONObject meta = hbcTransform.optJSONObject(patchEntryName);
|
|
65
|
+
return meta != null ? meta.toString() : "";
|
|
66
|
+
}
|
|
56
67
|
// Maps a copy source path ("from") to the CRC32 of the file content,
|
|
57
68
|
// when provided by the manifest ("copiesCrc"). Lets the resource
|
|
58
69
|
// copier locate the file by content if the path is not present on
|
|
@@ -251,6 +262,7 @@ class DownloadTask implements Runnable {
|
|
|
251
262
|
contents.deletes,
|
|
252
263
|
contents.copyCrcs
|
|
253
264
|
);
|
|
265
|
+
contents.hbcTransform = manifest.optJSONObject("hbcTransform");
|
|
254
266
|
continue;
|
|
255
267
|
}
|
|
256
268
|
|
|
@@ -311,7 +323,8 @@ class DownloadTask implements Runnable {
|
|
|
311
323
|
false,
|
|
312
324
|
new String[0],
|
|
313
325
|
new String[0],
|
|
314
|
-
new String[0]
|
|
326
|
+
new String[0],
|
|
327
|
+
contents.hbcTransformMetaFor("index.bundlejs.patch")
|
|
315
328
|
);
|
|
316
329
|
} finally {
|
|
317
330
|
originBundleFile.delete();
|
|
@@ -345,7 +358,8 @@ class DownloadTask implements Runnable {
|
|
|
345
358
|
plan.enableMerge,
|
|
346
359
|
contents.copyFroms.toArray(new String[0]),
|
|
347
360
|
contents.copyTos.toArray(new String[0]),
|
|
348
|
-
contents.deletes.toArray(new String[0])
|
|
361
|
+
contents.deletes.toArray(new String[0]),
|
|
362
|
+
contents.hbcTransformMetaFor("index.bundlejs.patch")
|
|
349
363
|
);
|
|
350
364
|
if (params.targetFile.exists()) {
|
|
351
365
|
params.targetFile.delete();
|
|
@@ -357,7 +371,7 @@ class DownloadTask implements Runnable {
|
|
|
357
371
|
params.unzipDirectory.getAbsolutePath(),
|
|
358
372
|
params.hash,
|
|
359
373
|
params.originHash,
|
|
360
|
-
|
|
374
|
+
params.maxAgeDays
|
|
361
375
|
);
|
|
362
376
|
}
|
|
363
377
|
|
|
@@ -444,7 +458,8 @@ class DownloadTask implements Runnable {
|
|
|
444
458
|
boolean enableMerge,
|
|
445
459
|
String[] copyFroms,
|
|
446
460
|
String[] copyTos,
|
|
447
|
-
String[] deletes
|
|
461
|
+
String[] deletes,
|
|
462
|
+
String hbcTransformMeta
|
|
448
463
|
);
|
|
449
464
|
|
|
450
465
|
private static native void cleanupOldEntries(
|
|
@@ -16,6 +16,7 @@ final class ErrorCodes {
|
|
|
16
16
|
static final String SWITCH_VERSION_FAILED = "SWITCH_VERSION_FAILED";
|
|
17
17
|
static final String MARK_SUCCESS_FAILED = "MARK_SUCCESS_FAILED";
|
|
18
18
|
static final String RESTART_FAILED = "RESTART_FAILED";
|
|
19
|
+
static final String RESET_FAILED = "RESET_FAILED";
|
|
19
20
|
static final String INVALID_HASH_INFO = "INVALID_HASH_INFO";
|
|
20
21
|
static final String UNSUPPORTED_PLATFORM = "UNSUPPORTED_PLATFORM";
|
|
21
22
|
|
|
@@ -23,4 +23,16 @@ final class NativeUpdateCore {
|
|
|
23
23
|
|
|
24
24
|
loaded = true;
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 原生 patch 内核可消费的 diff 轨道版本(2 = hdiffv2 轨道)。
|
|
29
|
+
* 经 getConstants 暴露给 JS,再随 checkUpdate 以 diffV 上报,
|
|
30
|
+
* 服务端按能力门控下发。
|
|
31
|
+
*/
|
|
32
|
+
static int supportedDiffVersion() {
|
|
33
|
+
ensureLoaded();
|
|
34
|
+
return getSupportedDiffVersion();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private static native int getSupportedDiffVersion();
|
|
26
38
|
}
|
|
@@ -303,6 +303,36 @@ public class UpdateContext {
|
|
|
303
303
|
this.cleanUp();
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Reset to the bundle packaged in the binary: wipe the whole update state
|
|
308
|
+
* (so the next launch resolves to the built-in bundle) and delete every
|
|
309
|
+
* downloaded version. Only the client uuid survives — it identifies the
|
|
310
|
+
* install for gray release bucketing and must not change on reset.
|
|
311
|
+
*/
|
|
312
|
+
public void resetToPackagedBundle() {
|
|
313
|
+
StateCoreResult resetState = new StateCoreResult();
|
|
314
|
+
resetState.packageVersion = getPackageVersion();
|
|
315
|
+
resetState.buildTime = getBuildTime();
|
|
316
|
+
resetState.firstTime = false;
|
|
317
|
+
resetState.firstTimeOk = true;
|
|
318
|
+
String uuid = sp.getString("uuid", null);
|
|
319
|
+
SharedPreferences.Editor editor = sp.edit();
|
|
320
|
+
editor.clear();
|
|
321
|
+
applyState(editor, resetState);
|
|
322
|
+
if (uuid != null) {
|
|
323
|
+
editor.putString("uuid", uuid);
|
|
324
|
+
}
|
|
325
|
+
persistEditor(editor, "reset to packaged bundle");
|
|
326
|
+
ignoreRollback = false;
|
|
327
|
+
Log.i(TAG, "Reset to packaged bundle");
|
|
328
|
+
|
|
329
|
+
DownloadTaskParams params = new DownloadTaskParams();
|
|
330
|
+
params.type = DownloadTaskParams.TASK_TYPE_CLEANUP;
|
|
331
|
+
params.maxAgeDays = 0;
|
|
332
|
+
params.unzipDirectory = rootDir;
|
|
333
|
+
enqueue(params);
|
|
334
|
+
}
|
|
335
|
+
|
|
306
336
|
public void clearRollbackMark() {
|
|
307
337
|
StateCoreResult currentState = getStateSnapshot();
|
|
308
338
|
StateCoreResult nextState = runStateCore(
|
|
@@ -191,6 +191,19 @@ public class UpdateModuleImpl {
|
|
|
191
191
|
});
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
public static void resetToPackagedBundle(
|
|
195
|
+
final UpdateContext updateContext,
|
|
196
|
+
final Promise promise
|
|
197
|
+
) {
|
|
198
|
+
StateSerialRunner.run(promise, ErrorCodes.RESET_FAILED, "resetToPackagedBundle", new StateSerialRunner.Operation() {
|
|
199
|
+
@Override
|
|
200
|
+
public void run() {
|
|
201
|
+
updateContext.resetToPackagedBundle();
|
|
202
|
+
promise.resolve(true);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
194
207
|
private static void setUuidInternal(UpdateContext updateContext, String uuid) {
|
|
195
208
|
updateContext.setKv("uuid", uuid);
|
|
196
209
|
}
|
|
@@ -73,6 +73,11 @@ public class UpdateModule extends NativePushySpec {
|
|
|
73
73
|
UpdateModuleImpl.markSuccess(updateContext, promise);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
@Override
|
|
77
|
+
public void resetToPackagedBundle(Promise promise) {
|
|
78
|
+
UpdateModuleImpl.resetToPackagedBundle(updateContext, promise);
|
|
79
|
+
}
|
|
80
|
+
|
|
76
81
|
@Override
|
|
77
82
|
public void setUuid(String uuid, Promise promise) {
|
|
78
83
|
UpdateModuleImpl.setUuid(updateContext, uuid, promise);
|
|
@@ -75,6 +75,11 @@ public class UpdateModule extends ReactContextBaseJavaModule {
|
|
|
75
75
|
UpdateModuleImpl.markSuccess(updateContext);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
@ReactMethod
|
|
79
|
+
public void resetToPackagedBundle(Promise promise) {
|
|
80
|
+
UpdateModuleImpl.resetToPackagedBundle(updateContext, promise);
|
|
81
|
+
}
|
|
82
|
+
|
|
78
83
|
@ReactMethod
|
|
79
84
|
public void setUuid(String uuid) {
|
|
80
85
|
UpdateModuleImpl.setUuid(updateContext, uuid);
|
|
@@ -31,6 +31,9 @@ constexpr const char* kSwitchVersionFailed = "SWITCH_VERSION_FAILED";
|
|
|
31
31
|
constexpr const char* kMarkSuccessFailed = "MARK_SUCCESS_FAILED";
|
|
32
32
|
// reloadUpdate / restartApp failed.
|
|
33
33
|
constexpr const char* kRestartFailed = "RESTART_FAILED";
|
|
34
|
+
// resetToPackagedBundle failed (state wipe or cleanup could not run), or the
|
|
35
|
+
// installed native module predates the method (JS-layer detection).
|
|
36
|
+
constexpr const char* kResetFailed = "RESET_FAILED";
|
|
34
37
|
// Stored or provided hash info is not a valid JSON object.
|
|
35
38
|
constexpr const char* kInvalidHashInfo = "INVALID_HASH_INFO";
|
|
36
39
|
// The method is not supported on this platform (e.g. downloadAndInstallApk
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#include "hbc_transform.h"
|
|
2
|
+
|
|
3
|
+
#include <cstring>
|
|
4
|
+
|
|
5
|
+
namespace pushy {
|
|
6
|
+
namespace hbc {
|
|
7
|
+
|
|
8
|
+
namespace {
|
|
9
|
+
|
|
10
|
+
constexpr uint8_t kHbcMagic[8] =
|
|
11
|
+
{0xc6, 0x1f, 0xbc, 0x03, 0xc1, 0x03, 0x19, 0x1f};
|
|
12
|
+
constexpr size_t kHeaderSize = 128;
|
|
13
|
+
constexpr size_t kCountsOffset = 32; // magic(8) + version(4) + sourceHash(20)
|
|
14
|
+
constexpr uint32_t kMaxEntryCount = 0x0fffffff;
|
|
15
|
+
constexpr uint32_t kMaxEntrySize = 4096;
|
|
16
|
+
constexpr uint32_t kMaxSections = 64;
|
|
17
|
+
constexpr uint32_t kMaxCountFields = (kHeaderSize - kCountsOffset) / 4; // 24
|
|
18
|
+
|
|
19
|
+
inline uint32_t ReadU32(const uint8_t* p) {
|
|
20
|
+
return static_cast<uint32_t>(p[0]) | (static_cast<uint32_t>(p[1]) << 8) |
|
|
21
|
+
(static_cast<uint32_t>(p[2]) << 16) |
|
|
22
|
+
(static_cast<uint32_t>(p[3]) << 24);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
inline void WriteU32(uint8_t* p, uint32_t v) {
|
|
26
|
+
p[0] = static_cast<uint8_t>(v);
|
|
27
|
+
p[1] = static_cast<uint8_t>(v >> 8);
|
|
28
|
+
p[2] = static_cast<uint8_t>(v >> 16);
|
|
29
|
+
p[3] = static_cast<uint8_t>(v >> 24);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
inline uint64_t Align4(uint64_t x) {
|
|
33
|
+
return (x + 3) & ~static_cast<uint64_t>(3);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
struct ResolvedSection {
|
|
37
|
+
uint64_t start;
|
|
38
|
+
uint64_t size;
|
|
39
|
+
const HbcSectionDesc* desc;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
} // namespace
|
|
43
|
+
|
|
44
|
+
bool TransformHbcInPlace(
|
|
45
|
+
uint8_t* data,
|
|
46
|
+
size_t size,
|
|
47
|
+
const HbcLayoutDesc& layout,
|
|
48
|
+
bool inverse) {
|
|
49
|
+
// ---- 校验阶段:改写任何字节之前完成全部检查 ----
|
|
50
|
+
if (data == nullptr || size < kHeaderSize) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
if (std::memcmp(data, kHbcMagic, sizeof(kHbcMagic)) != 0) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
if (layout.headerCountFields < 2 ||
|
|
57
|
+
layout.headerCountFields > kMaxCountFields) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (layout.sections == nullptr || layout.sectionCount == 0 ||
|
|
61
|
+
layout.sectionCount > kMaxSections) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
uint32_t counts[kMaxCountFields];
|
|
66
|
+
for (uint32_t i = 0; i < layout.headerCountFields; ++i) {
|
|
67
|
+
counts[i] = ReadU32(data + kCountsOffset + static_cast<size_t>(i) * 4);
|
|
68
|
+
}
|
|
69
|
+
// 位置约定:槽位 0 = fileLength,最后一个槽位 = debugInfoOffset
|
|
70
|
+
const uint64_t fileLength = counts[0];
|
|
71
|
+
const uint64_t debugInfoOffset = counts[layout.headerCountFields - 1];
|
|
72
|
+
if (fileLength != size) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
if (debugInfoOffset < kHeaderSize || debugInfoOffset > size) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
ResolvedSection resolved[kMaxSections];
|
|
80
|
+
uint64_t off = kHeaderSize;
|
|
81
|
+
for (uint32_t i = 0; i < layout.sectionCount; ++i) {
|
|
82
|
+
const HbcSectionDesc& s = layout.sections[i];
|
|
83
|
+
if (s.countIndex >= layout.headerCountFields) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if (s.entrySize == 0 || s.entrySize > kMaxEntrySize) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const uint64_t count = counts[s.countIndex];
|
|
90
|
+
if (count > kMaxEntryCount) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
if (s.deltaFieldCount > 0 && s.deltaFields == nullptr) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
for (uint32_t f = 0; f < s.deltaFieldCount; ++f) {
|
|
97
|
+
const HbcDeltaField& field = s.deltaFields[f];
|
|
98
|
+
if (field.bits < 1 || field.bits > 32 || field.bit + field.bits > 32 ||
|
|
99
|
+
static_cast<uint64_t>(field.byte) + 4 > s.entrySize) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const uint64_t sectionSize = count * s.entrySize; // ≤ 2^28 × 2^12 < 2^40
|
|
104
|
+
off = Align4(off);
|
|
105
|
+
if (off + sectionSize > debugInfoOffset) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
resolved[i] = {off, sectionSize, &s};
|
|
109
|
+
off += sectionSize;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---- 改写阶段:校验通过后不再有失败路径 ----
|
|
113
|
+
for (uint32_t i = 0; i < layout.sectionCount; ++i) {
|
|
114
|
+
const ResolvedSection& r = resolved[i];
|
|
115
|
+
const HbcSectionDesc& s = *r.desc;
|
|
116
|
+
for (uint32_t f = 0; f < s.deltaFieldCount; ++f) {
|
|
117
|
+
const HbcDeltaField& field = s.deltaFields[f];
|
|
118
|
+
const uint32_t fieldMask =
|
|
119
|
+
field.bits == 32 ? 0xffffffffu : ((1u << field.bits) - 1u);
|
|
120
|
+
const uint32_t mask = fieldMask << field.bit;
|
|
121
|
+
uint32_t prev = 0;
|
|
122
|
+
const uint64_t end = r.start + r.size;
|
|
123
|
+
for (uint64_t p = r.start + field.byte; p < end; p += s.entrySize) {
|
|
124
|
+
uint8_t* wordPtr = data + p;
|
|
125
|
+
const uint32_t word = ReadU32(wordPtr);
|
|
126
|
+
const uint32_t val = (word >> field.bit) & fieldMask;
|
|
127
|
+
uint32_t enc;
|
|
128
|
+
if (!inverse) {
|
|
129
|
+
enc = (val - prev) & fieldMask;
|
|
130
|
+
prev = val;
|
|
131
|
+
} else {
|
|
132
|
+
enc = (val + prev) & fieldMask;
|
|
133
|
+
prev = enc;
|
|
134
|
+
}
|
|
135
|
+
WriteU32(wordPtr, (word & ~mask) | (enc << field.bit));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
} // namespace hbc
|
|
143
|
+
} // namespace pushy
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cstddef>
|
|
4
|
+
#include <cstdint>
|
|
5
|
+
|
|
6
|
+
namespace pushy {
|
|
7
|
+
namespace hbc {
|
|
8
|
+
|
|
9
|
+
// Hermes 字节码(HBC)delta-friendly 可逆变换的通用解释器。
|
|
10
|
+
//
|
|
11
|
+
// 布局描述表来自 patch 的 __diff.json 元数据(生成端 CLI 写入),本解释器
|
|
12
|
+
// 不含任何 HBC 版本分支——Hermes 版本演进只需要生成端更新描述表,客户端
|
|
13
|
+
// 零跟进。描述表被视为不可信输入:所有段边界、条目大小、位域范围在改写
|
|
14
|
+
// 任何字节之前完成全量校验;校验失败时 buffer 保持原样。
|
|
15
|
+
//
|
|
16
|
+
// 变换语义与生成端(react-native-update-cli src/utils/hbcTransform.ts)
|
|
17
|
+
// 严格一致:对描述的偏移位域做前项差分(wrapping,模字段位宽)。
|
|
18
|
+
// wrapping 保证与数据单调性无关的严格可逆。
|
|
19
|
+
|
|
20
|
+
struct HbcDeltaField {
|
|
21
|
+
// 条目内字节偏移处按小端读取 u32,取 [bit, bit+bits) 位做差分
|
|
22
|
+
uint32_t byte;
|
|
23
|
+
uint32_t bit;
|
|
24
|
+
uint32_t bits;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
struct HbcSectionDesc {
|
|
28
|
+
// 段大小 = headerCounts[countIndex] × entrySize(字节段 entrySize 为 1)
|
|
29
|
+
uint32_t countIndex;
|
|
30
|
+
uint32_t entrySize;
|
|
31
|
+
const HbcDeltaField* deltaFields;
|
|
32
|
+
uint32_t deltaFieldCount;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// 与 wire 格式的位置约定一致:counts 槽位 0 = fileLength,
|
|
36
|
+
// 最后一个槽位 = debugInfoOffset(结构校验依赖)。
|
|
37
|
+
struct HbcLayoutDesc {
|
|
38
|
+
uint32_t headerCountFields;
|
|
39
|
+
const HbcSectionDesc* sections;
|
|
40
|
+
uint32_t sectionCount;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// 对 data 原地执行变换(inverse=false)或逆变换(inverse=true)。
|
|
44
|
+
// 返回 false 表示 data 不是该描述表下结构合法的 HBC(或描述表本身非法),
|
|
45
|
+
// 此时 data 未被修改。
|
|
46
|
+
bool TransformHbcInPlace(
|
|
47
|
+
uint8_t* data,
|
|
48
|
+
size_t size,
|
|
49
|
+
const HbcLayoutDesc& layout,
|
|
50
|
+
bool inverse);
|
|
51
|
+
|
|
52
|
+
} // namespace hbc
|
|
53
|
+
} // namespace pushy
|