]> git.kaiwu.me - quickjs.git/commitdiff
reduced memory usage of Map hash table
authorFabrice Bellard <fabrice@bellard.org>
Sat, 22 Mar 2025 10:28:23 +0000 (11:28 +0100)
committerFabrice Bellard <fabrice@bellard.org>
Sat, 22 Mar 2025 10:28:23 +0000 (11:28 +0100)
quickjs.c

index d35a333d6569811972a080fdc16c6dd136c87cc1..0bcb9fe3ad9f322054b6aee8a3763eebfca55d57 100644 (file)
--- a/quickjs.c
+++ b/quickjs.c
@@ -45960,7 +45960,7 @@ typedef struct JSMapRecord {
     struct JSMapState *map;
     struct JSMapRecord *next_weak_ref;
     struct list_head link;
-    struct list_head hash_link;
+    struct JSMapRecord *hash_next;
     JSValue key;
     JSValue value;
 } JSMapRecord;
@@ -45969,7 +45969,7 @@ typedef struct JSMapState {
     BOOL is_weak; /* TRUE if WeakSet/WeakMap */
     struct list_head records; /* list of JSMapRecord.link */
     uint32_t record_count;
-    struct list_head *hash_table;
+    JSMapRecord **hash_table;
     uint32_t hash_size; /* must be a power of two */
     uint32_t record_count_threshold; /* count at which a hash table
                                         resize is needed */
@@ -45998,10 +45998,9 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
     s->is_weak = is_weak;
     JS_SetOpaque(obj, s);
     s->hash_size = 1;
-    s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size);
+    s->hash_table = js_mallocz(ctx, sizeof(s->hash_table[0]) * s->hash_size);
     if (!s->hash_table)
         goto fail;
-    init_list_head(&s->hash_table[0]);
     s->record_count_threshold = 4;
 
     arr = JS_UNDEFINED;
@@ -46100,7 +46099,7 @@ static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key)
 }
 
 /* XXX: better hash ? */
-static uint32_t map_hash_key(JSContext *ctx, JSValueConst key)
+static uint32_t map_hash_key(JSValueConst key)
 {
     uint32_t tag = JS_VALUE_GET_NORM_TAG(key);
     uint32_t h;
@@ -46158,12 +46157,10 @@ static uint32_t map_hash_key(JSContext *ctx, JSValueConst key)
 static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
                                     JSValueConst key)
 {
-    struct list_head *el;
     JSMapRecord *mr;
     uint32_t h;
-    h = map_hash_key(ctx, key) & (s->hash_size - 1);
-    list_for_each(el, &s->hash_table[h]) {
-        mr = list_entry(el, JSMapRecord, hash_link);
+    h = map_hash_key(key) & (s->hash_size - 1);
+    for(mr = s->hash_table[h]; mr != NULL; mr = mr->hash_next) {
         if (js_same_value_zero(ctx, mr->key, key))
             return mr;
     }
@@ -46172,10 +46169,10 @@ static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
 
 static void map_hash_resize(JSContext *ctx, JSMapState *s)
 {
-    uint32_t new_hash_size, i, h;
+    uint32_t new_hash_size, h;
     size_t slack;
-    struct list_head *new_hash_table, *el;
-    JSMapRecord *mr;
+    struct list_head *el;
+    JSMapRecord *mr, **new_hash_table;
 
     /* XXX: no reporting of memory allocation failure */
     if (s->hash_size == 1)
@@ -46187,14 +46184,14 @@ static void map_hash_resize(JSContext *ctx, JSMapState *s)
     if (!new_hash_table)
         return;
 
-    for(i = 0; i < new_hash_size; i++)
-        init_list_head(&new_hash_table[i]);
+    memset(new_hash_table, 0, sizeof(new_hash_table[0]) * new_hash_size);
 
     list_for_each(el, &s->records) {
         mr = list_entry(el, JSMapRecord, link);
         if (!mr->empty) {
-            h = map_hash_key(ctx, mr->key) & (new_hash_size - 1);
-            list_add_tail(&mr->hash_link, &new_hash_table[h]);
+            h = map_hash_key(mr->key) & (new_hash_size - 1);
+            mr->hash_next = new_hash_table[h];
+            new_hash_table[h] = mr;
         }
     }
     s->hash_table = new_hash_table;
@@ -46223,8 +46220,9 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
         JS_DupValue(ctx, key);
     }
     mr->key = (JSValue)key;
-    h = map_hash_key(ctx, key) & (s->hash_size - 1);
-    list_add_tail(&mr->hash_link, &s->hash_table[h]);
+    h = map_hash_key(key) & (s->hash_size - 1);
+    mr->hash_next = s->hash_table[h];
+    s->hash_table[h] = mr;
     list_add_tail(&mr->link, &s->records);
     s->record_count++;
     if (s->record_count >= s->record_count_threshold) {
@@ -46236,7 +46234,7 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
 /* Remove the weak reference from the object weak
    reference list. we don't use a doubly linked list to
    save space, assuming a given object has few weak
-       references to it */
+   references to it */
 static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr)
 {
     JSMapRecord **pmr, *mr1;
@@ -46254,11 +46252,12 @@ static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr)
     *pmr = mr1->next_weak_ref;
 }
 
+/* warning: the record must be removed from the hash table before */
 static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)
 {
     if (mr->empty)
         return;
-    list_del(&mr->hash_link);
+    
     if (s->is_weak) {
         delete_weak_ref(rt, mr);
     } else {
@@ -46289,16 +46288,31 @@ static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)
 
 static void reset_weak_ref(JSRuntime *rt, JSObject *p)
 {
-    JSMapRecord *mr, *mr_next;
+    JSMapRecord *mr, *mr_next, **pmr, *mr1;
     JSMapState *s;
-
+    uint32_t h;
+    JSValue key;
+    
     /* first pass to remove the records from the WeakMap/WeakSet
        lists */
+    key = JS_MKPTR(JS_TAG_OBJECT, p);
     for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) {
         s = mr->map;
         assert(s->is_weak);
         assert(!mr->empty); /* no iterator on WeakMap/WeakSet */
-        list_del(&mr->hash_link);
+
+        /* remove the record from hash table */
+        h = map_hash_key(key) & (s->hash_size - 1);
+        pmr = &s->hash_table[h];
+        for(;;) {
+            mr1 = *pmr;
+            assert(mr1 != NULL);
+            if (mr1 == mr)
+                break;
+            pmr = &mr1->hash_next;
+        }
+        *pmr = mr->hash_next;
+        
         list_del(&mr->link);
     }
 
@@ -46376,15 +46390,28 @@ static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,
                              int argc, JSValueConst *argv, int magic)
 {
     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
-    JSMapRecord *mr;
+    JSMapRecord *mr, **pmr;
     JSValueConst key;
+    uint32_t h;
 
     if (!s)
         return JS_EXCEPTION;
     key = map_normalize_key(ctx, argv[0]);
-    mr = map_find_record(ctx, s, key);
-    if (!mr)
-        return JS_FALSE;
+    
+    h = map_hash_key(key) & (s->hash_size - 1);
+    pmr = &s->hash_table[h];
+    for(;;) {
+        mr = *pmr;
+        if (mr == NULL)
+            return JS_FALSE;
+        if (js_same_value_zero(ctx, mr->key, key))
+            break;
+        pmr = &mr->hash_next;
+    }
+
+    /* remove from the hash table */
+    *pmr = mr->hash_next;
+    
     map_delete_record(ctx->rt, s, mr);
     return JS_TRUE;
 }
@@ -46398,6 +46425,10 @@ static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val,
 
     if (!s)
         return JS_EXCEPTION;
+
+    /* remove from the hash table */
+    memset(s->hash_table, 0, sizeof(s->hash_table[0]) * s->hash_size);
+    
     list_for_each_safe(el, el1, &s->records) {
         mr = list_entry(el, JSMapRecord, link);
         map_delete_record(ctx->rt, s, mr);