mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-21 05:50:55 +08:00
Overwrite present number keys in the lookup that checks for growth (#1056)
This commit is contained in:
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5712-map-number-overwrite-at-growth.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -1003,10 +1003,19 @@ static inline bool scr_map_append_grows_index(const ScrMap *m) {
|
||||
|
||||
/* Number keys arrive normalized (+0 for -0, canonical NaN). A directly
|
||||
* indexed map overwrites or appends through its table; a key the table
|
||||
* cannot hold converts it to hashing first. */
|
||||
* cannot hold converts it to hashing first. When an append would grow a
|
||||
* hashed map's index, the lookup that decides whether the key is new also
|
||||
* overwrites a key that is already present. */
|
||||
static void scr_map_set_f64_slot(ScrMap *m, uint64_t key, uint64_t val) {
|
||||
double k = scr_map_slot_to_f64(key);
|
||||
if (!m->ndense && scr_map_append_grows_index(m) && scr_map_find_f64(m, k) == SCR_MAP_EMPTY) {
|
||||
if (!m->ndense && scr_map_append_grows_index(m)) {
|
||||
size_t e = scr_map_find_f64(m, k);
|
||||
if (e != SCR_MAP_EMPTY) {
|
||||
uint64_t old = m->entries[e].val;
|
||||
m->entries[e].val = val; /* unlink before releasing (cycle collector) */
|
||||
scr_map_release_val(m, old);
|
||||
return;
|
||||
}
|
||||
scr_map_enter_dense(m, k); /* the append would grow the index */
|
||||
}
|
||||
if (m->ndense) {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// Number-key Map.set and Set.add of present keys while the map is at its
|
||||
// bucket-table growth boundary (the next new key grows the index):
|
||||
// overwrites keep the entry, its position and the size, and release the
|
||||
// value they replace.
|
||||
|
||||
interface Box {
|
||||
label: string;
|
||||
}
|
||||
|
||||
function sparse(i: number): number {
|
||||
return 1000 + i * 7919;
|
||||
}
|
||||
|
||||
for (const size of [8, 64, 1024]) {
|
||||
const nums = new Map<number, number>();
|
||||
const boxes = new Map<number, Box>();
|
||||
const names = new Map<number, string>();
|
||||
const seen = new Set<number>();
|
||||
for (let i = 0; i < size; i++) {
|
||||
nums.set(sparse(i), i);
|
||||
boxes.set(sparse(i), { label: `b${i}` });
|
||||
names.set(sparse(i), `n${i}`);
|
||||
seen.add(sparse(i));
|
||||
}
|
||||
for (let round = 0; round < 3; round++) {
|
||||
for (let i = size - 1; i >= 0; i--) {
|
||||
nums.set(sparse(i), i * 10 + round);
|
||||
boxes.set(sparse(i), { label: `b${i}.${round}` });
|
||||
names.set(sparse(i), `n${i}.${round}`.repeat(2));
|
||||
seen.add(sparse(i));
|
||||
}
|
||||
}
|
||||
let sum = 0;
|
||||
for (const v of nums.values()) sum += v;
|
||||
const firstKeys = [...nums.keys()].slice(0, 3).join(",");
|
||||
console.log(
|
||||
size,
|
||||
nums.size,
|
||||
boxes.size,
|
||||
names.size,
|
||||
seen.size,
|
||||
sum,
|
||||
firstKeys,
|
||||
boxes.get(sparse(size - 1))?.label,
|
||||
names.get(sparse(0)),
|
||||
[...seen].slice(0, 2).join(","),
|
||||
);
|
||||
// One more key grows the index; existing entries are unchanged.
|
||||
nums.set(-1, -1);
|
||||
boxes.set(-1, { label: "new" });
|
||||
console.log(size, "grown", nums.size, nums.get(sparse(size >> 1)), nums.get(-1), boxes.get(-1)?.label, boxes.get(sparse(1))?.label);
|
||||
}
|
||||
|
||||
// At the boundary after deletes: a delete frees no bucket, so re-adding
|
||||
// the deleted key compacts instead of growing.
|
||||
const churn = new Map<number, number>();
|
||||
for (let i = 0; i < 64; i++) churn.set(sparse(i), i);
|
||||
churn.delete(sparse(10));
|
||||
churn.set(sparse(11), 111);
|
||||
churn.set(sparse(10), 110);
|
||||
console.log("churn", churn.size, churn.get(sparse(10)), churn.get(sparse(11)), [...churn.keys()].indexOf(sparse(10)));
|
||||
Reference in New Issue
Block a user