mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
assert,util: improve deep comparison performance
PR-URL: https://github.com/nodejs/node/pull/61076 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
committed by
Node.js GitHub Bot
parent
416db75e42
commit
a968e4e672
@@ -4,7 +4,6 @@ const {
|
||||
Array,
|
||||
ArrayBuffer,
|
||||
ArrayIsArray,
|
||||
ArrayPrototypeFilter,
|
||||
ArrayPrototypePush,
|
||||
BigInt,
|
||||
BigInt64Array,
|
||||
@@ -91,7 +90,7 @@ const wellKnownConstructors = new SafeSet()
|
||||
.add(WeakMap)
|
||||
.add(WeakSet);
|
||||
|
||||
if (Float16Array) { // TODO(BridgeAR): Remove when regularly supported
|
||||
if (Float16Array) { // TODO(BridgeAR): Remove when Flag got removed from V8
|
||||
wellKnownConstructors.add(Float16Array);
|
||||
}
|
||||
|
||||
@@ -127,6 +126,11 @@ const {
|
||||
getOwnNonIndexProperties,
|
||||
} = internalBinding('util');
|
||||
|
||||
let kKeyObject;
|
||||
let kExtractable;
|
||||
let kAlgorithm;
|
||||
let kKeyUsages;
|
||||
|
||||
const kStrict = 2;
|
||||
const kStrictWithoutPrototypes = 3;
|
||||
const kLoose = 0;
|
||||
@@ -152,7 +156,7 @@ function isPartialUint8Array(a, b) {
|
||||
}
|
||||
let offsetA = 0;
|
||||
for (let offsetB = 0; offsetB < lenB; offsetB++) {
|
||||
while (!ObjectIs(a[offsetA], b[offsetB])) {
|
||||
while (a[offsetA] !== b[offsetB]) {
|
||||
offsetA++;
|
||||
if (offsetA > lenA - lenB + offsetB) {
|
||||
return false;
|
||||
@@ -187,11 +191,7 @@ function areSimilarFloatArrays(a, b) {
|
||||
}
|
||||
|
||||
function areSimilarTypedArrays(a, b) {
|
||||
if (a.byteLength !== b.byteLength) {
|
||||
return false;
|
||||
}
|
||||
return compare(new Uint8Array(a.buffer, a.byteOffset, a.byteLength),
|
||||
new Uint8Array(b.buffer, b.byteOffset, b.byteLength)) === 0;
|
||||
return a.byteLength === b.byteLength && compare(a, b) === 0;
|
||||
}
|
||||
|
||||
function areEqualArrayBuffers(buf1, buf2) {
|
||||
@@ -225,7 +225,7 @@ function isEqualBoxedPrimitive(val1, val2) {
|
||||
assert.fail(`Unknown boxed type ${val1}`);
|
||||
}
|
||||
|
||||
function isEnumerableOrIdentical(val1, val2, prop, mode, memos, method) {
|
||||
function isEnumerableOrIdentical(val1, val2, prop, mode, memos) {
|
||||
return hasEnumerable(val2, prop) || // This is handled by Object.keys()
|
||||
(mode === kPartial && (val2[prop] === undefined || (prop === 'message' && val2[prop] === ''))) ||
|
||||
innerDeepEqual(val1[prop], val2[prop], mode, memos);
|
||||
@@ -400,8 +400,10 @@ function objectComparisonStart(val1, val2, mode, memos) {
|
||||
return false;
|
||||
}
|
||||
} else if (isCryptoKey(val1)) {
|
||||
const { kKeyObject } = require('internal/crypto/util');
|
||||
const { kExtractable, kAlgorithm, kKeyUsages } = require('internal/crypto/keys');
|
||||
if (kKeyObject === undefined) {
|
||||
kKeyObject = require('internal/crypto/util').kKeyObject;
|
||||
({ kExtractable, kAlgorithm, kKeyUsages } = require('internal/crypto/keys'));
|
||||
}
|
||||
if (!isCryptoKey(val2) ||
|
||||
val1[kExtractable] !== val2[kExtractable] ||
|
||||
!innerDeepEqual(val1[kAlgorithm], val2[kAlgorithm], mode, memos) ||
|
||||
@@ -417,18 +419,11 @@ function objectComparisonStart(val1, val2, mode, memos) {
|
||||
return keyCheck(val1, val2, mode, memos, kNoIterator);
|
||||
}
|
||||
|
||||
function getEnumerables(val, keys) {
|
||||
return ArrayPrototypeFilter(keys, (key) => hasEnumerable(val, key));
|
||||
}
|
||||
|
||||
function partialSymbolEquiv(val1, val2, keys2) {
|
||||
const symbolKeys = getOwnSymbols(val2);
|
||||
if (symbolKeys.length !== 0) {
|
||||
for (const key of symbolKeys) {
|
||||
if (hasEnumerable(val2, key)) {
|
||||
if (!hasEnumerable(val1, key)) {
|
||||
return false;
|
||||
}
|
||||
ArrayPrototypePush(keys2, key);
|
||||
}
|
||||
}
|
||||
@@ -460,32 +455,19 @@ function keyCheck(val1, val2, mode, memos, iterationType, keys2) {
|
||||
} else if (keys2.length !== (keys1 = ObjectKeys(val1)).length) {
|
||||
return false;
|
||||
} else if (mode === kStrict || mode === kStrictWithoutPrototypes) {
|
||||
const symbolKeysA = getOwnSymbols(val1);
|
||||
if (symbolKeysA.length !== 0) {
|
||||
let count = 0;
|
||||
for (const key of symbolKeysA) {
|
||||
if (hasEnumerable(val1, key)) {
|
||||
if (!hasEnumerable(val2, key)) {
|
||||
return false;
|
||||
}
|
||||
ArrayPrototypePush(keys2, key);
|
||||
count++;
|
||||
} else if (hasEnumerable(val2, key)) {
|
||||
return false;
|
||||
}
|
||||
for (const key of getOwnSymbols(val1)) {
|
||||
if (hasEnumerable(val1, key)) {
|
||||
ArrayPrototypePush(keys1, key);
|
||||
}
|
||||
const symbolKeysB = getOwnSymbols(val2);
|
||||
if (symbolKeysA.length !== symbolKeysB.length &&
|
||||
getEnumerables(val2, symbolKeysB).length !== count) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const symbolKeysB = getOwnSymbols(val2);
|
||||
if (symbolKeysB.length !== 0 &&
|
||||
getEnumerables(val2, symbolKeysB).length !== 0) {
|
||||
return false;
|
||||
}
|
||||
for (const key of getOwnSymbols(val2)) {
|
||||
if (hasEnumerable(val2, key)) {
|
||||
ArrayPrototypePush(keys2, key);
|
||||
}
|
||||
}
|
||||
if (keys1.length !== keys2.length) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -647,16 +629,14 @@ function partialObjectSetEquiv(array, a, b, mode, memo) {
|
||||
}
|
||||
|
||||
function arrayHasEqualElement(array, val1, mode, memo, comparator, start, end) {
|
||||
let matched = false;
|
||||
for (let i = end - 1; i >= start; i--) {
|
||||
if (comparator(val1, array[i], mode, memo)) {
|
||||
// Remove the matching element to make sure we do not check that again.
|
||||
array.splice(i, 1);
|
||||
matched = true;
|
||||
break;
|
||||
// Move the matching element to make sure we do not check that again.
|
||||
array[i] = array[end];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return matched;
|
||||
return false;
|
||||
}
|
||||
|
||||
function setObjectEquiv(array, a, b, mode, memo) {
|
||||
@@ -798,18 +778,16 @@ function partialObjectMapEquiv(array, a, b, mode, memo) {
|
||||
}
|
||||
|
||||
function arrayHasEqualMapElement(array, key1, item1, b, mode, memo, comparator, start, end) {
|
||||
let matched = false;
|
||||
for (let i = end - 1; i >= start; i--) {
|
||||
const key2 = array[i];
|
||||
if (comparator(key1, key2, mode, memo) &&
|
||||
innerDeepEqual(item1, b.get(key2), mode, memo)) {
|
||||
// Remove the matching element to make sure we do not check that again.
|
||||
array.splice(i, 1);
|
||||
matched = true;
|
||||
break;
|
||||
// Move the matching element to make sure we do not check that again.
|
||||
array[i] = array[end];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return matched;
|
||||
return false;
|
||||
}
|
||||
|
||||
function mapObjectEquiv(array, a, b, mode, memo) {
|
||||
@@ -898,17 +876,21 @@ function mapEquiv(a, b, mode, memo) {
|
||||
}
|
||||
|
||||
function partialSparseArrayEquiv(a, b, mode, memos, startA, startB) {
|
||||
let aPos = 0;
|
||||
const keysA = ObjectKeys(a).slice(startA);
|
||||
const keysB = ObjectKeys(b).slice(startB);
|
||||
if (keysA.length < keysB.length) {
|
||||
let aPos = startA;
|
||||
const keysA = ObjectKeys(a);
|
||||
const keysB = ObjectKeys(b);
|
||||
const keysBLength = keysB.length;
|
||||
const keysALength = keysA.length;
|
||||
const lenA = keysALength - startA;
|
||||
const lenB = keysBLength - startB;
|
||||
if (lenA < lenB) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < keysB.length; i++) {
|
||||
const keyB = keysB[i];
|
||||
for (let i = 0; i < lenB; i++) {
|
||||
const keyB = keysB[startB + i];
|
||||
while (!innerDeepEqual(a[keysA[aPos]], b[keyB], mode, memos)) {
|
||||
aPos++;
|
||||
if (aPos > keysA.length - keysB.length + i) {
|
||||
if (aPos > keysALength - lenB + i) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -979,8 +961,11 @@ function objEquiv(a, b, mode, keys1, keys2, memos, iterationType) {
|
||||
// property in V8 13.0 compared to calling Object.propertyIsEnumerable()
|
||||
// and accessing the property regularly.
|
||||
const descriptor = ObjectGetOwnPropertyDescriptor(a, key);
|
||||
if (!descriptor?.enumerable ||
|
||||
!innerDeepEqual(descriptor.value !== undefined ? descriptor.value : a[key], b[key], mode, memos)) {
|
||||
if (descriptor === undefined || descriptor.enumerable !== true) {
|
||||
return false;
|
||||
}
|
||||
const value = descriptor.writable !== undefined ? descriptor.value : a[key];
|
||||
if (!innerDeepEqual(value, b[key], mode, memos)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1029,10 +1014,7 @@ module.exports = {
|
||||
return detectCycles(val1, val2, kLoose);
|
||||
},
|
||||
isDeepStrictEqual(val1, val2, skipPrototype) {
|
||||
if (skipPrototype) {
|
||||
return detectCycles(val1, val2, kStrictWithoutPrototypes);
|
||||
}
|
||||
return detectCycles(val1, val2, kStrict);
|
||||
return detectCycles(val1, val2, skipPrototype ? kStrictWithoutPrototypes : kStrict);
|
||||
},
|
||||
isPartialStrictEqual(val1, val2) {
|
||||
return detectCycles(val1, val2, kPartial);
|
||||
|
||||
Reference in New Issue
Block a user