Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions bench/snippets/assert.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,37 @@ bench("deepStrictEqual Map subclass of 10,000 entries", () => {
assert.deepStrictEqual(customMap, customMapCopy);
});

const ids = Array.from({ length: 1000 }, (_, i) => i);
const objectSet = order => new Set(order.map(id => ({ id })));
const objectKeyMap = order => new Map(order.map(id => [{ id }, id]));

const objects = objectSet(ids);
const sameOrderObjects = objectSet(ids);
bench("deepStrictEqual Set of 1,000 objects", () => {
assert.deepStrictEqual(objects, sameOrderObjects);
});

const reversedObjects = objectSet(ids.toReversed());
bench("deepStrictEqual reversed Set of 1,000 objects", () => {
assert.deepStrictEqual(objects, reversedObjects);
});

const objectKeys = objectKeyMap(ids);
const sameOrderObjectKeys = objectKeyMap(ids);
bench("deepStrictEqual Map of 1,000 object keys", () => {
assert.deepStrictEqual(objectKeys, sameOrderObjectKeys);
});

const smallSet = objectSet(ids.slice(0, 16));
const otherSmallSet = objectSet(ids.slice(16, 32));
bench("notDeepStrictEqual Sets of 16 objects, none equal", () => {
assert.notDeepStrictEqual(smallSet, otherSmallSet);
});

const smallMap = objectKeyMap(ids.slice(0, 16));
const otherSmallMap = objectKeyMap(ids.slice(16, 32));
bench("notDeepStrictEqual Maps of 16 object keys, none equal", () => {
assert.notDeepStrictEqual(smallMap, otherSmallMap);
});

await run();
74 changes: 74 additions & 0 deletions src/jsc/bindings/PendingEntries.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#pragma once

#include "root.h"
#include <wtf/Vector.h>

namespace Bun {

enum class Claim : uint8_t {
Missed, // nothing claimed (also returned when `matches` threw; check the scope first)
Claimed,
Exhausted, // claimed the last open entry
};

// One side's entries still needing a counterpart, claimed one at a time while the other side is walked. Plain copies: the caller roots them.
template<typename Entry>
struct PendingEntries {
using Queue = WTF::Vector<Entry, 8>;

Queue entries;
size_t first { 0 };
size_t end;
// Node's guess: keep probing the end that hit last, so same-order and reversed inputs stay linear.
bool probeFront { true };

explicit PendingEntries(Queue&& queued)
: entries(std::move(queued))
, end(entries.size())
{
ASSERT(end);
}

size_t openCount() const { return end - first; }

// What a walk leaves behind for an entry known to match nothing, without running the probes.
void recordMiss() { probeFront = true; }

// Probe order of node's partialObject{Set,Map}Equiv: front guess, back guess, then the rest back to front.
template<typename Matches>
Claim claimWith(JSC::ThrowScope& scope, const Matches& matches)
{
ASSERT(first < end);
size_t scanFrom = first;
if (probeFront) {
bool hit = matches(entries[first]);
RETURN_IF_EXCEPTION(scope, Claim::Missed);
if (hit) {
first++;
return first == end ? Claim::Exhausted : Claim::Claimed;
}
if (openCount() == 1)
return Claim::Missed;
probeFront = false;
scanFrom++;
}
size_t i = end - 1;
bool hit = matches(entries[i]);
RETURN_IF_EXCEPTION(scope, Claim::Missed);
if (!hit) {
probeFront = true;
while (!hit && i > scanFrom) {
i--;
hit = matches(entries[i]);
RETURN_IF_EXCEPTION(scope, Claim::Missed);
}
if (!hit)
return Claim::Missed;
entries[i] = entries[end - 1];
}
end--;
return first == end ? Claim::Exhausted : Claim::Claimed;
}
};

} // namespace Bun
Loading
Loading