perf: optimize history sync memory and CPU usage
Aligns with Baileys upstream PR #2333 (jlucaso1). Surgical port of 4 micro-optimizations + 2 test files. No collision with InfiniteAPI's LID/PN, carousel, buttons, Bad MAC or app state sync resilience code. Changes: 1. WAProto longToString/longToNumber: use native BigInt instead of Long library division loops. ~4.6x faster per call (237ns -> 52ns). BigInt.toString() delegates to V8 native C++. Same change applied to fix-imports.js (template) and index.js (generated artifact) so future regenerations stay in sync. 2. downloadHistory: stream-pipe createInflate instead of buffering the full compressed payload then inflating. Cuts RSS peak ~50% on 50MB history payloads. 3. processHistoryMessage: drop the { ...chat } shallow copy before pushing to chats[]. The decoded protobuf object is not referenced after the push. 4. downloadEncryptedContent transform: skip Buffer.concat when remainingBytes is empty (the common case — chunks usually arrive AES-aligned). ~19x faster per chunk (78ms -> 4ms over 25k chunks). Tests: - bigint-validation.test.ts (NEW): 32 parameterized cases covering zero, MAX_SAFE_INTEGER boundary, max int64/uint64, negative values, tricky bit patterns and powers of 2; plus 200-iter fuzz comparing old Long vs new BigInt implementations for equivalence. - proto-tojson-long.test.ts: kept the existing fork-only test for string-in-Long-field graceful handling, appended 4 upstream tests covering Long->string fast path, large unsigned > MAX_SAFE_INTEGER, zero/small values and encode/decode roundtrip. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+22
-18
@@ -18,14 +18,18 @@ try {
|
||||
'\tif (typeof value === "number") {\n' +
|
||||
'\t\treturn String(value);\n' +
|
||||
'\t}\n' +
|
||||
'\tif (!$util.Long) {\n' +
|
||||
'\t\treturn String(value);\n' +
|
||||
'\t// Fast path: convert Long {low, high} directly via native BigInt\n' +
|
||||
'\t// BigInt.toString() is a native C++ operation, much faster than Long\'s pure JS division loops\n' +
|
||||
'\tif (value && typeof value.low === "number" && typeof value.high === "number") {\n' +
|
||||
'\t\tconst lo = BigInt(value.low >>> 0);\n' +
|
||||
'\t\tconst hi = BigInt(value.high >>> 0);\n' +
|
||||
'\t\tconst combined = (hi << 32n) | lo;\n' +
|
||||
'\t\tif (!unsigned && value.high < 0) {\n' +
|
||||
'\t\t\treturn (combined - (1n << 64n)).toString();\n' +
|
||||
'\t\t}\n' +
|
||||
'\t\treturn combined.toString();\n' +
|
||||
'\t}\n' +
|
||||
'\tconst normalized = $util.Long.fromValue(value);\n' +
|
||||
'\tconst prepared = unsigned && normalized && typeof normalized.toUnsigned === "function"\n' +
|
||||
'\t\t? normalized.toUnsigned()\n' +
|
||||
'\t\t: normalized;\n' +
|
||||
'\treturn prepared.toString();\n' +
|
||||
'\treturn String(value);\n' +
|
||||
'}\n\n'
|
||||
const longToNumberHelper =
|
||||
'function longToNumber(value, unsigned) {\n' +
|
||||
@@ -33,19 +37,19 @@ try {
|
||||
'\t\treturn value;\n' +
|
||||
'\t}\n' +
|
||||
'\tif (typeof value === "string") {\n' +
|
||||
'\t\tconst numeric = Number(value);\n' +
|
||||
'\t\treturn numeric;\n' +
|
||||
'\t}\n' +
|
||||
'\tif (!$util.Long) {\n' +
|
||||
'\t\treturn Number(value);\n' +
|
||||
'\t}\n' +
|
||||
'\tconst normalized = $util.Long.fromValue(value);\n' +
|
||||
'\tconst prepared = unsigned && normalized && typeof normalized.toUnsigned === "function"\n' +
|
||||
'\t\t? normalized.toUnsigned()\n' +
|
||||
'\t\t: typeof normalized.toSigned === "function"\n' +
|
||||
'\t\t\t? normalized.toSigned()\n' +
|
||||
'\t\t\t: normalized;\n' +
|
||||
'\treturn prepared.toNumber();\n' +
|
||||
'\t// Fast path: convert Long {low, high} directly via native BigInt\n' +
|
||||
'\tif (value && typeof value.low === "number" && typeof value.high === "number") {\n' +
|
||||
'\t\tconst lo = BigInt(value.low >>> 0);\n' +
|
||||
'\t\tconst hi = BigInt(value.high >>> 0);\n' +
|
||||
'\t\tconst combined = (hi << 32n) | lo;\n' +
|
||||
'\t\tif (!unsigned && value.high < 0) {\n' +
|
||||
'\t\t\treturn Number(combined - (1n << 64n));\n' +
|
||||
'\t\t}\n' +
|
||||
'\t\treturn Number(combined);\n' +
|
||||
'\t}\n' +
|
||||
'\treturn Number(value);\n' +
|
||||
'}\n\n'
|
||||
|
||||
if (!content.includes('function longToString(')) {
|
||||
|
||||
+22
-18
@@ -12,14 +12,18 @@ function longToString(value, unsigned) {
|
||||
if (typeof value === "number") {
|
||||
return String(value);
|
||||
}
|
||||
if (!$util.Long) {
|
||||
return String(value);
|
||||
// Fast path: convert Long {low, high} directly via native BigInt
|
||||
// BigInt.toString() is a native C++ operation, much faster than Long's pure JS division loops
|
||||
if (value && typeof value.low === "number" && typeof value.high === "number") {
|
||||
const lo = BigInt(value.low >>> 0);
|
||||
const hi = BigInt(value.high >>> 0);
|
||||
const combined = (hi << 32n) | lo;
|
||||
if (!unsigned && value.high < 0) {
|
||||
return (combined - (1n << 64n)).toString();
|
||||
}
|
||||
return combined.toString();
|
||||
}
|
||||
const normalized = $util.Long.fromValue(value);
|
||||
const prepared = unsigned && normalized && typeof normalized.toUnsigned === "function"
|
||||
? normalized.toUnsigned()
|
||||
: normalized;
|
||||
return prepared.toString();
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function longToNumber(value, unsigned) {
|
||||
@@ -27,19 +31,19 @@ function longToNumber(value, unsigned) {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
const numeric = Number(value);
|
||||
return numeric;
|
||||
}
|
||||
if (!$util.Long) {
|
||||
return Number(value);
|
||||
}
|
||||
const normalized = $util.Long.fromValue(value);
|
||||
const prepared = unsigned && normalized && typeof normalized.toUnsigned === "function"
|
||||
? normalized.toUnsigned()
|
||||
: typeof normalized.toSigned === "function"
|
||||
? normalized.toSigned()
|
||||
: normalized;
|
||||
return prepared.toNumber();
|
||||
// Fast path: convert Long {low, high} directly via native BigInt
|
||||
if (value && typeof value.low === "number" && typeof value.high === "number") {
|
||||
const lo = BigInt(value.low >>> 0);
|
||||
const hi = BigInt(value.high >>> 0);
|
||||
const combined = (hi << 32n) | lo;
|
||||
if (!unsigned && value.high < 0) {
|
||||
return Number(combined - (1n << 64n));
|
||||
}
|
||||
return Number(combined);
|
||||
}
|
||||
return Number(value);
|
||||
}
|
||||
|
||||
export const proto = $root.proto = (() => {
|
||||
|
||||
Reference in New Issue
Block a user