mirror of
https://github.com/denoland/std.git
synced 2024-11-21 20:50:22 +00:00
perf(assert): add fast path for primitive keyed collections in equal()
(#5913)
* feat(assert): add fast path for primitive keyed collections * remove fix for #5912 (to be redone as separate pr) * add extra-fast-path with Set#symmetricDifference * Changes from code review
This commit is contained in:
parent
70f981e605
commit
2fe19de48f
@ -1,7 +1,14 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// This module is browser compatible.
|
||||
function isKeyedCollection(x: unknown): x is Set<unknown> {
|
||||
return [Symbol.iterator, "size"].every((k) => k in (x as Set<unknown>));
|
||||
|
||||
type KeyedCollection = Set<unknown> | Map<unknown, unknown>;
|
||||
|
||||
function isKeyedCollection(x: unknown): x is KeyedCollection {
|
||||
return x instanceof Set || x instanceof Map;
|
||||
}
|
||||
|
||||
function getValFromKeyedCollection(kc: KeyedCollection, key: unknown) {
|
||||
return kc instanceof Map ? kc.get(key) : key;
|
||||
}
|
||||
|
||||
function constructorsEqual(a: object, b: object) {
|
||||
@ -82,12 +89,41 @@ export function equal(c: unknown, d: unknown): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
const aKeys = [...a.keys()];
|
||||
const primitiveKeysFastPath = aKeys.every((k) => {
|
||||
return typeof k === "string" ||
|
||||
typeof k === "number" ||
|
||||
typeof k === "boolean" ||
|
||||
typeof k === "bigint" ||
|
||||
typeof k === "symbol" ||
|
||||
k == null;
|
||||
});
|
||||
if (primitiveKeysFastPath) {
|
||||
if (a instanceof Set) {
|
||||
return a.symmetricDifference(b).size === 0;
|
||||
}
|
||||
|
||||
for (const key of aKeys) {
|
||||
if (
|
||||
!b.has(key) ||
|
||||
!compare(
|
||||
getValFromKeyedCollection(a, key),
|
||||
getValFromKeyedCollection(b, key),
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let unmatchedEntries = a.size;
|
||||
|
||||
for (const [aKey, aValue] of a.entries()) {
|
||||
for (const [bKey, bValue] of b.entries()) {
|
||||
/* Given that Map keys can be references, we need
|
||||
* to ensure that they are also deeply equal */
|
||||
|
||||
if (
|
||||
(aKey === aValue && bKey === bValue && compare(aKey, bKey)) ||
|
||||
(compare(aKey, bKey) && compare(aValue, bValue))
|
||||
|
@ -276,3 +276,31 @@ Deno.test("equal() WeakMap, WeakRef and WeakSet", () => {
|
||||
assertFalse(equal(new WeakSet(), { constructor: WeakSet }));
|
||||
assertFalse(equal(new WeakRef({}), { constructor: WeakRef }));
|
||||
});
|
||||
|
||||
Deno.test("equal() fast path for primitive keyed collections", () => {
|
||||
const arr = Array.from({ length: 10_000 }, (_, i) => i);
|
||||
|
||||
const set1 = new Set(arr);
|
||||
const set2 = new Set(arr);
|
||||
assert(equal(set1, set2));
|
||||
|
||||
const map1 = new Map(arr.map((v) => [v, v]));
|
||||
const map2 = new Map(arr.map((v) => [v, v]));
|
||||
assert(equal(map1, map2));
|
||||
|
||||
const set3 = new Set(arr);
|
||||
const set4 = new Set(arr.with(-1, -1));
|
||||
assertFalse(equal(set3, set4));
|
||||
|
||||
// entries [...] 9998 => 9998, 9999 => 9999
|
||||
const map3 = new Map(arr.map((v) => [v, v]));
|
||||
// entries [...] 9998 => 9998, -1 => -1
|
||||
const map4 = new Map(arr.with(-1, -1).map((v) => [v, v]));
|
||||
assertFalse(equal(map3, map4));
|
||||
|
||||
// entries [...] 9998 => 9998, 9999 => 9999
|
||||
const map5 = new Map(arr.map((v, i) => [i, v]));
|
||||
// entries [...] 9998 => 9998, 9999 => -1
|
||||
const map6 = new Map(arr.with(-1, -1).map((v, i) => [i, v]));
|
||||
assertFalse(equal(map5, map6));
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user