summaryrefslogtreecommitdiff
path: root/quickjs.c
diff options
context:
space:
mode:
authorFabrice Bellard <fabrice@bellard.org>2025-03-22 11:28:23 +0100
committerFabrice Bellard <fabrice@bellard.org>2025-03-22 11:28:23 +0100
commitd1bb520f296a4379d97a5df7fd09131e4189bc9d (patch)
tree88f4b448cda871ec46e1b3dee4a3c928a35fdd88 /quickjs.c
parent49413985ebb00315443597f5e04d0df1418ffdbc (diff)
downloadquickjs-d1bb520f296a4379d97a5df7fd09131e4189bc9d.tar.gz
quickjs-d1bb520f296a4379d97a5df7fd09131e4189bc9d.zip
reduced memory usage of Map hash table
Diffstat (limited to 'quickjs.c')
-rw-r--r--quickjs.c85
1 files changed, 58 insertions, 27 deletions
diff --git a/quickjs.c b/quickjs.c
index d35a333..0bcb9fe 100644
--- 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);