Skip to content

Commit a68469a

Browse files
Use hash-map for EnvEntry storage (closes #227)
1 parent 15750ae commit a68469a

3 files changed

Lines changed: 95 additions & 23 deletions

File tree

src/builtins.c

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,21 +1263,9 @@ static DeclType decl_type_from_name(const char *name) {
12631263
return TYPE_UNKNOWN;
12641264
}
12651265

1266-
static EnvEntry *env_find_local_entry(Env *env, const char *name) {
1267-
if (!env || !name) {
1268-
return NULL;
1269-
}
1270-
for (size_t i = 0; i < env->count; i++) {
1271-
if (strcmp(env->entries[i].name, name) == 0) {
1272-
return &env->entries[i];
1273-
}
1274-
}
1275-
return NULL;
1276-
}
1277-
12781266
static Env *env_find_owner(Env *env, const char *name) {
12791267
for (Env *e = env; e != NULL; e = e->parent) {
1280-
if (env_find_local_entry(e, name)) {
1268+
if (env_find_local(e, name)) {
12811269
return e;
12821270
}
12831271
}
@@ -3514,7 +3502,7 @@ static Env *deser_env(JsonValue *obj, UnserCtx *ctx, Interpreter *interp, const
35143502
for (size_t i = 0; i < declared->as.obj.count; i++) {
35153503
JsonPair *p = &declared->as.obj.items[i];
35163504
DeclType dt = decl_type_from_name(p->value && p->value->type == JSON_STR ? p->value->as.str : NULL);
3517-
if (!env_find_local_entry(env, p->key)) {
3505+
if (!env_find_local(env, p->key)) {
35183506
env_define(env, p->key, dt, 0, value_null());
35193507
}
35203508
}
@@ -3525,10 +3513,10 @@ static Env *deser_env(JsonValue *obj, UnserCtx *ctx, Interpreter *interp, const
35253513
for (size_t i = 0; i < values->as.obj.count; i++) {
35263514
JsonPair *p = &values->as.obj.items[i];
35273515
JsonValue *vv = p->value;
3528-
if (!env_find_local_entry(env, p->key)) {
3516+
if (!env_find_local(env, p->key)) {
35293517
env_define(env, p->key, TYPE_UNKNOWN, 0, value_null());
35303518
}
3531-
EnvEntry *entry = env_find_local_entry(env, p->key);
3519+
EnvEntry *entry = env_find_local(env, p->key);
35323520
if (vv && vv->type == JSON_OBJ) {
35333521
JsonValue *vt = json_obj_get(vv, "t");
35343522
if (vt && vt->type == JSON_STR && strcmp(vt->as.str, "PTR") == 0) {
@@ -3565,7 +3553,7 @@ static Env *deser_env(JsonValue *obj, UnserCtx *ctx, Interpreter *interp, const
35653553
if (schemas && schemas->type == JSON_OBJ) {
35663554
for (size_t i = 0; i < schemas->as.obj.count; i++) {
35673555
JsonPair *p = &schemas->as.obj.items[i];
3568-
EnvEntry *entry = env_find_local_entry(env, p->key);
3556+
EnvEntry *entry = env_find_local(env, p->key);
35693557
if (entry) {
35703558
Value s = deser_val(p->value, ctx, interp, err);
35713559
if (*err == NULL) {
@@ -3581,10 +3569,10 @@ static Env *deser_env(JsonValue *obj, UnserCtx *ctx, Interpreter *interp, const
35813569
for (size_t i = 0; i < frozen->as.arr.count; i++) {
35823570
JsonValue *it = frozen->as.arr.items[i];
35833571
if (it && it->type == JSON_STR) {
3584-
EnvEntry *e = env_find_local_entry(env, it->as.str);
3572+
EnvEntry *e = env_find_local(env, it->as.str);
35853573
if (!e) {
35863574
env_define(env, it->as.str, TYPE_UNKNOWN, 0, value_null());
3587-
e = env_find_local_entry(env, it->as.str);
3575+
e = env_find_local(env, it->as.str);
35883576
}
35893577
if (e) {
35903578
e->frozen = true;
@@ -3598,10 +3586,10 @@ static Env *deser_env(JsonValue *obj, UnserCtx *ctx, Interpreter *interp, const
35983586
for (size_t i = 0; i < perma->as.arr.count; i++) {
35993587
JsonValue *it = perma->as.arr.items[i];
36003588
if (it && it->type == JSON_STR) {
3601-
EnvEntry *e = env_find_local_entry(env, it->as.str);
3589+
EnvEntry *e = env_find_local(env, it->as.str);
36023590
if (!e) {
36033591
env_define(env, it->as.str, TYPE_UNKNOWN, 0, value_null());
3604-
e = env_find_local_entry(env, it->as.str);
3592+
e = env_find_local(env, it->as.str);
36053593
}
36063594
if (e) {
36073595
e->permafrozen = true;

src/env.c

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,78 @@
1919
#include <string.h>
2020

2121
/* Forward declarations for local helpers used before their definitions. */
22-
static EnvEntry *env_find_local(Env *env, const char *name);
22+
23+
/* ================================================================== */
24+
/* Open-addressing hash table for O(1) local name lookup */
25+
/* ================================================================== */
26+
27+
#define ENV_HT_LOAD_NUM 7
28+
#define ENV_HT_LOAD_DEN 10
29+
30+
static size_t env_hash_name(const char *name) {
31+
size_t h = 5381;
32+
int c;
33+
while ((c = (unsigned char)*name++) != 0) {
34+
h = ((h << 5) + h) ^ (size_t)c;
35+
}
36+
return h;
37+
}
38+
39+
static void env_ht_rebuild(Env *env) {
40+
size_t old_cap = env->ht_capacity;
41+
EnvEntry **old_slots = env->ht_slots;
42+
size_t new_cap = old_cap == 0 ? 8 : old_cap * 2;
43+
env->ht_slots = calloc(new_cap, sizeof(EnvEntry *));
44+
env->ht_capacity = new_cap;
45+
env->ht_count = 0;
46+
for (size_t i = 0; i < env->count; i++) {
47+
EnvEntry *entry = &env->entries[i];
48+
if (!entry->name) {
49+
continue;
50+
}
51+
size_t mask = new_cap - 1;
52+
size_t idx = env_hash_name(entry->name) & mask;
53+
while (env->ht_slots[idx] != NULL) {
54+
idx = (idx + 1) & mask;
55+
}
56+
env->ht_slots[idx] = entry;
57+
env->ht_count++;
58+
}
59+
free(old_slots);
60+
}
61+
62+
static void env_ht_insert(Env *env, EnvEntry *entry) {
63+
if (env->ht_capacity == 0) {
64+
size_t new_cap = 8;
65+
env->ht_slots = calloc(new_cap, sizeof(EnvEntry *));
66+
env->ht_capacity = new_cap;
67+
env->ht_count = 0;
68+
} else if (env->ht_count + 1 > env->ht_capacity * ENV_HT_LOAD_NUM / ENV_HT_LOAD_DEN) {
69+
env_ht_rebuild(env);
70+
}
71+
size_t mask = env->ht_capacity - 1;
72+
size_t idx = env_hash_name(entry->name) & mask;
73+
while (env->ht_slots[idx] != NULL) {
74+
idx = (idx + 1) & mask;
75+
}
76+
env->ht_slots[idx] = entry;
77+
env->ht_count++;
78+
}
79+
80+
static EnvEntry *env_ht_find(Env *env, const char *name) {
81+
if (env->ht_capacity == 0) {
82+
return NULL;
83+
}
84+
size_t mask = env->ht_capacity - 1;
85+
size_t idx = env_hash_name(name) & mask;
86+
while (env->ht_slots[idx] != NULL) {
87+
if (strcmp(env->ht_slots[idx]->name, name) == 0) {
88+
return env->ht_slots[idx];
89+
}
90+
idx = (idx + 1) & mask;
91+
}
92+
return NULL;
93+
}
2394

2495
/* ================================================================== */
2596
/* Thread-local snapshots for env_get_entry */
@@ -157,14 +228,19 @@ void env_free(Env *env) {
157228
}
158229
}
159230
free(env->entries);
231+
free(env->ht_slots);
160232
free(env);
161233
}
162234

163235
/* ================================================================== */
164236
/* Raw internal lookup helpers (no buffer interaction) */
165237
/* ================================================================== */
166238

167-
static EnvEntry *env_find_local(Env *env, const char *name) {
239+
EnvEntry *env_find_local(Env *env, const char *name) {
240+
EnvEntry *entry = env_ht_find(env, name);
241+
if (entry) {
242+
return entry;
243+
}
168244
for (size_t i = 0; i < env->count; i++) {
169245
if (strcmp(env->entries[i].name, name) == 0) {
170246
return &env->entries[i];
@@ -261,6 +337,7 @@ bool env_define_direct(Env *env, const char *name, DeclType type, int base, Valu
261337
exit(1);
262338
}
263339
env->capacity = new_cap;
340+
env_ht_rebuild(env);
264341
}
265342
EnvEntry *entry = &env->entries[env->count++];
266343
entry->name = strdup(name);
@@ -273,6 +350,7 @@ bool env_define_direct(Env *env, const char *name, DeclType type, int base, Valu
273350
entry->alias_target = NULL;
274351
entry->alias_target_env = NULL;
275352
entry->value = value_null();
353+
env_ht_insert(env, entry);
276354
return true;
277355
}
278356

src/env.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ typedef struct Env {
2929
size_t count;
3030
size_t capacity;
3131
int refcount;
32+
EnvEntry **ht_slots;
33+
size_t ht_capacity;
34+
size_t ht_count;
3235
} Env;
3336

3437
Env *env_create(Env *parent);
@@ -49,6 +52,9 @@ bool env_exists(Env *env, const char *name);
4952
// Returns NULL if not found.
5053
EnvEntry *env_get_entry(Env *env, const char *name);
5154

55+
// Local lookup helper (O(1) with hash table, falls back to linear scan).
56+
EnvEntry *env_find_local(Env *env, const char *name);
57+
5258
// Create or update an alias (pointer) binding: `name` will become an alias to `target_name`.
5359
// If declare_if_missing is true, `name` will be defined if absent. Returns true on success.
5460
bool env_set_alias(Env *env, const char *name, const char *target_name, DeclType type, int type_base,

0 commit comments

Comments
 (0)