From 714afdcfe55b4e2b58612ffad1ea1698efa33c2c Mon Sep 17 00:00:00 2001 From: Malte Ubl Date: Sat, 10 Oct 2026 22:45:29 -0700 Subject: [PATCH] runtime: size a cloned map's direct index for its own keys new Map(m) and new Set(s) copied the source's direct-index size, which follows the source's peak. The copy now covers its largest live key, and keys that are sparse for their count hash instead. --- packages/runtime/src/scr_map.c | 12 +++++++++--- packages/runtime/src/scr_map.test.c | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/runtime/src/scr_map.c b/packages/runtime/src/scr_map.c index 6c1f4d1c8..7891ec812 100644 --- a/packages/runtime/src/scr_map.c +++ b/packages/runtime/src/scr_map.c @@ -1248,9 +1248,14 @@ ScrMap *scr_map_clone(const ScrMap *source, bool keys_only) { out->entries = malloc(count * sizeof(ScrMapEntry)); if (!out->entries) scr_map_oom(); out->ecap = count; + size_t top = 0; for (size_t i = 0; i < source->nentries; i++) { const ScrMapEntry *entry = &source->entries[i]; if (!entry->hash) continue; + if (source->ndense) { + size_t k = (size_t)scr_map_slot_to_f64(entry->key); + if (k > top) top = k; + } ScrMapEntry *copy = &out->entries[out->nentries++]; *copy = *entry; if (out->key_kind == SCR_MAP_KEY_STR) scr_str_retain(scr_map_slot_to_ptr(copy->key)); @@ -1259,9 +1264,10 @@ ScrMap *scr_map_clone(const ScrMap *source, bool keys_only) { else if (out->val_kind == SCR_MAP_VAL_REF) out->val_retain(scr_map_slot_to_ptr(copy->val)); } out->nlive = count; - if (source->ndense) { - /* Same keys, so the same table size; entry indices are compacted. */ - scr_map_dense_alloc(out, source->ndense); + /* Size the copy's direct index for its own keys, not for the source's + * peak; keys that are sparse for their count hash instead. */ + if (source->ndense && top / 2 < scr_map_dense_limit(count)) { + scr_map_dense_alloc(out, scr_map_dense_size(top)); scr_map_fill_dense(out); return out; } diff --git a/packages/runtime/src/scr_map.test.c b/packages/runtime/src/scr_map.test.c index ab8085718..6c6e22e01 100644 --- a/packages/runtime/src/scr_map.test.c +++ b/packages/runtime/src/scr_map.test.c @@ -372,6 +372,22 @@ static void delete_shrinks(void) { assert(scr_map_size(map) == 20 && map->ndense && map->ndense <= 64); for (size_t i = 0; i < 40; i++) assert(scr_map_has_f64(map, (double)i) == (i < 20)); scr_map_release(map); + + /* A copy indexes its own keys, not the source's peak (here a quarter of + * the entries survive, too many to compact the source yet). */ + map = scr_map_new(SCR_MAP_KEY_F64, SCR_MAP_VAL_F64, NULL, NULL, NULL); + for (size_t i = 0; i < 4096; i++) scr_map_set_f64_f64(map, (double)i, (double)i); + for (size_t i = 1024; i < 4096; i++) assert(scr_map_delete_f64(map, (double)i)); + assert(map->ndense == 4096 && scr_map_iter_count(map) == 4096); + ScrMap *copy = scr_map_clone(map, false); + assert(copy->ndense == 1024 && scr_map_size(copy) == 1024); + double out; + for (size_t i = 0; i < 1100; i++) { + assert(scr_map_get_f64_f64(copy, (double)i, &out) == (i < 1024)); + if (i < 1024) assert(out == (double)i); + } + scr_map_release(copy); + scr_map_release(map); } /* Grow-and-drain cycles against the ordered model: the map repeatedly