Skip to content

Commit d1bb520

Browse files
author
Fabrice Bellard
committed
reduced memory usage of Map hash table
1 parent 4941398 commit d1bb520

1 file changed

Lines changed: 58 additions & 27 deletions

File tree

quickjs.c

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -45960,7 +45960,7 @@ typedef struct JSMapRecord {
4596045960
struct JSMapState *map;
4596145961
struct JSMapRecord *next_weak_ref;
4596245962
struct list_head link;
45963-
struct list_head hash_link;
45963+
struct JSMapRecord *hash_next;
4596445964
JSValue key;
4596545965
JSValue value;
4596645966
} JSMapRecord;
@@ -45969,7 +45969,7 @@ typedef struct JSMapState {
4596945969
BOOL is_weak; /* TRUE if WeakSet/WeakMap */
4597045970
struct list_head records; /* list of JSMapRecord.link */
4597145971
uint32_t record_count;
45972-
struct list_head *hash_table;
45972+
JSMapRecord **hash_table;
4597345973
uint32_t hash_size; /* must be a power of two */
4597445974
uint32_t record_count_threshold; /* count at which a hash table
4597545975
resize is needed */
@@ -45998,10 +45998,9 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
4599845998
s->is_weak = is_weak;
4599945999
JS_SetOpaque(obj, s);
4600046000
s->hash_size = 1;
46001-
s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size);
46001+
s->hash_table = js_mallocz(ctx, sizeof(s->hash_table[0]) * s->hash_size);
4600246002
if (!s->hash_table)
4600346003
goto fail;
46004-
init_list_head(&s->hash_table[0]);
4600546004
s->record_count_threshold = 4;
4600646005

4600746006
arr = JS_UNDEFINED;
@@ -46100,7 +46099,7 @@ static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key)
4610046099
}
4610146100

4610246101
/* XXX: better hash ? */
46103-
static uint32_t map_hash_key(JSContext *ctx, JSValueConst key)
46102+
static uint32_t map_hash_key(JSValueConst key)
4610446103
{
4610546104
uint32_t tag = JS_VALUE_GET_NORM_TAG(key);
4610646105
uint32_t h;
@@ -46158,12 +46157,10 @@ static uint32_t map_hash_key(JSContext *ctx, JSValueConst key)
4615846157
static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
4615946158
JSValueConst key)
4616046159
{
46161-
struct list_head *el;
4616246160
JSMapRecord *mr;
4616346161
uint32_t h;
46164-
h = map_hash_key(ctx, key) & (s->hash_size - 1);
46165-
list_for_each(el, &s->hash_table[h]) {
46166-
mr = list_entry(el, JSMapRecord, hash_link);
46162+
h = map_hash_key(key) & (s->hash_size - 1);
46163+
for(mr = s->hash_table[h]; mr != NULL; mr = mr->hash_next) {
4616746164
if (js_same_value_zero(ctx, mr->key, key))
4616846165
return mr;
4616946166
}
@@ -46172,10 +46169,10 @@ static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
4617246169

4617346170
static void map_hash_resize(JSContext *ctx, JSMapState *s)
4617446171
{
46175-
uint32_t new_hash_size, i, h;
46172+
uint32_t new_hash_size, h;
4617646173
size_t slack;
46177-
struct list_head *new_hash_table, *el;
46178-
JSMapRecord *mr;
46174+
struct list_head *el;
46175+
JSMapRecord *mr, **new_hash_table;
4617946176

4618046177
/* XXX: no reporting of memory allocation failure */
4618146178
if (s->hash_size == 1)
@@ -46187,14 +46184,14 @@ static void map_hash_resize(JSContext *ctx, JSMapState *s)
4618746184
if (!new_hash_table)
4618846185
return;
4618946186

46190-
for(i = 0; i < new_hash_size; i++)
46191-
init_list_head(&new_hash_table[i]);
46187+
memset(new_hash_table, 0, sizeof(new_hash_table[0]) * new_hash_size);
4619246188

4619346189
list_for_each(el, &s->records) {
4619446190
mr = list_entry(el, JSMapRecord, link);
4619546191
if (!mr->empty) {
46196-
h = map_hash_key(ctx, mr->key) & (new_hash_size - 1);
46197-
list_add_tail(&mr->hash_link, &new_hash_table[h]);
46192+
h = map_hash_key(mr->key) & (new_hash_size - 1);
46193+
mr->hash_next = new_hash_table[h];
46194+
new_hash_table[h] = mr;
4619846195
}
4619946196
}
4620046197
s->hash_table = new_hash_table;
@@ -46223,8 +46220,9 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
4622346220
JS_DupValue(ctx, key);
4622446221
}
4622546222
mr->key = (JSValue)key;
46226-
h = map_hash_key(ctx, key) & (s->hash_size - 1);
46227-
list_add_tail(&mr->hash_link, &s->hash_table[h]);
46223+
h = map_hash_key(key) & (s->hash_size - 1);
46224+
mr->hash_next = s->hash_table[h];
46225+
s->hash_table[h] = mr;
4622846226
list_add_tail(&mr->link, &s->records);
4622946227
s->record_count++;
4623046228
if (s->record_count >= s->record_count_threshold) {
@@ -46236,7 +46234,7 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
4623646234
/* Remove the weak reference from the object weak
4623746235
reference list. we don't use a doubly linked list to
4623846236
save space, assuming a given object has few weak
46239-
references to it */
46237+
references to it */
4624046238
static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr)
4624146239
{
4624246240
JSMapRecord **pmr, *mr1;
@@ -46254,11 +46252,12 @@ static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr)
4625446252
*pmr = mr1->next_weak_ref;
4625546253
}
4625646254

46255+
/* warning: the record must be removed from the hash table before */
4625746256
static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)
4625846257
{
4625946258
if (mr->empty)
4626046259
return;
46261-
list_del(&mr->hash_link);
46260+
4626246261
if (s->is_weak) {
4626346262
delete_weak_ref(rt, mr);
4626446263
} else {
@@ -46289,16 +46288,31 @@ static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)
4628946288

4629046289
static void reset_weak_ref(JSRuntime *rt, JSObject *p)
4629146290
{
46292-
JSMapRecord *mr, *mr_next;
46291+
JSMapRecord *mr, *mr_next, **pmr, *mr1;
4629346292
JSMapState *s;
46294-
46293+
uint32_t h;
46294+
JSValue key;
46295+
4629546296
/* first pass to remove the records from the WeakMap/WeakSet
4629646297
lists */
46298+
key = JS_MKPTR(JS_TAG_OBJECT, p);
4629746299
for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) {
4629846300
s = mr->map;
4629946301
assert(s->is_weak);
4630046302
assert(!mr->empty); /* no iterator on WeakMap/WeakSet */
46301-
list_del(&mr->hash_link);
46303+
46304+
/* remove the record from hash table */
46305+
h = map_hash_key(key) & (s->hash_size - 1);
46306+
pmr = &s->hash_table[h];
46307+
for(;;) {
46308+
mr1 = *pmr;
46309+
assert(mr1 != NULL);
46310+
if (mr1 == mr)
46311+
break;
46312+
pmr = &mr1->hash_next;
46313+
}
46314+
*pmr = mr->hash_next;
46315+
4630246316
list_del(&mr->link);
4630346317
}
4630446318

@@ -46376,15 +46390,28 @@ static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,
4637646390
int argc, JSValueConst *argv, int magic)
4637746391
{
4637846392
JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
46379-
JSMapRecord *mr;
46393+
JSMapRecord *mr, **pmr;
4638046394
JSValueConst key;
46395+
uint32_t h;
4638146396

4638246397
if (!s)
4638346398
return JS_EXCEPTION;
4638446399
key = map_normalize_key(ctx, argv[0]);
46385-
mr = map_find_record(ctx, s, key);
46386-
if (!mr)
46387-
return JS_FALSE;
46400+
46401+
h = map_hash_key(key) & (s->hash_size - 1);
46402+
pmr = &s->hash_table[h];
46403+
for(;;) {
46404+
mr = *pmr;
46405+
if (mr == NULL)
46406+
return JS_FALSE;
46407+
if (js_same_value_zero(ctx, mr->key, key))
46408+
break;
46409+
pmr = &mr->hash_next;
46410+
}
46411+
46412+
/* remove from the hash table */
46413+
*pmr = mr->hash_next;
46414+
4638846415
map_delete_record(ctx->rt, s, mr);
4638946416
return JS_TRUE;
4639046417
}
@@ -46398,6 +46425,10 @@ static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val,
4639846425

4639946426
if (!s)
4640046427
return JS_EXCEPTION;
46428+
46429+
/* remove from the hash table */
46430+
memset(s->hash_table, 0, sizeof(s->hash_table[0]) * s->hash_size);
46431+
4640146432
list_for_each_safe(el, el1, &s->records) {
4640246433
mr = list_entry(el, JSMapRecord, link);
4640346434
map_delete_record(ctx->rt, s, mr);

0 commit comments

Comments
 (0)