Skip to content

Commit 76e51ef

Browse files
authored
Merge pull request #301 from icgmilk/fix_hashmap
Switch hashmap implementation to open addressing
2 parents 1169c76 + 68c2b1e commit 76e51ef

File tree

2 files changed

+62
-74
lines changed

2 files changed

+62
-74
lines changed

src/defs.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,13 @@ typedef struct {
101101
typedef struct hashmap_node {
102102
char *key;
103103
void *val;
104-
struct hashmap_node *next;
104+
bool occupied;
105105
} hashmap_node_t;
106106

107107
typedef struct {
108108
int size;
109109
int cap;
110-
hashmap_node_t **buckets;
110+
hashmap_node_t *table;
111111
} hashmap_t;
112112

113113
/* lexer tokens */

src/globals.c

Lines changed: 60 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,9 @@ void arena_free(arena_t *arena)
374374
*/
375375
int hashmap_hash_index(int size, char *key)
376376
{
377+
if (!key)
378+
return 0;
379+
377380
int hash = 0x811c9dc5;
378381

379382
for (; *key; key++) {
@@ -415,90 +418,61 @@ hashmap_t *hashmap_create(int cap)
415418

416419
map->size = 0;
417420
map->cap = round_up_pow2(cap);
418-
map->buckets = calloc(map->cap, sizeof(hashmap_node_t *));
421+
map->table = calloc(map->cap, sizeof(hashmap_node_t));
419422

420-
if (!map->buckets) {
421-
printf("Failed to allocate buckets in hashmap_t\n");
423+
if (!map->table) {
424+
printf("Failed to allocate table in hashmap_t\n");
422425
free(map);
423426
return NULL;
424427
}
425428

426429
return map;
427430
}
428431

429-
/* Create a hashmap node on heap.
430-
* @key: The key of node. Must not be NULL.
431-
* @val: The value of node. Could be NULL.
432-
*
433-
* Return: The pointer of created node.
434-
*/
435-
hashmap_node_t *hashmap_node_new(char *key, void *val)
436-
{
437-
if (!key)
438-
return NULL;
439-
440-
const int len = strlen(key);
441-
hashmap_node_t *node = arena_alloc(HASHMAP_ARENA, sizeof(hashmap_node_t));
442-
443-
444-
if (!node) {
445-
printf("Failed to allocate hashmap_node_t\n");
446-
return NULL;
447-
}
448-
449-
node->key = arena_alloc(HASHMAP_ARENA, len + 1);
450-
if (!node->key) {
451-
printf("Failed to allocate hashmap_node_t key with size %d\n", len + 1);
452-
return NULL;
453-
}
454-
455-
strcpy(node->key, key);
456-
node->val = val;
457-
node->next = NULL;
458-
return node;
459-
}
460432

461433
void hashmap_rehash(hashmap_t *map)
462434
{
463435
if (!map)
464436
return;
465437

466438
int old_cap = map->cap;
467-
hashmap_node_t **old_buckets = map->buckets;
439+
hashmap_node_t *old_table = map->table;
468440

469441
map->cap <<= 1;
470-
map->buckets = calloc(map->cap, sizeof(hashmap_node_t *));
442+
map->table = calloc(map->cap, sizeof(hashmap_node_t));
471443

472-
if (!map->buckets) {
473-
printf("Failed to allocate new buckets in hashmap_t\n");
474-
map->buckets = old_buckets;
444+
if (!map->table) {
445+
printf("Failed to allocate new table in hashmap_t\n");
446+
map->table = old_table;
475447
map->cap = old_cap;
476448
return;
477449
}
478450

451+
map->size = 0;
452+
479453
for (int i = 0; i < old_cap; i++) {
480-
hashmap_node_t *cur = old_buckets[i];
481-
hashmap_node_t *next;
482-
hashmap_node_t *target_cur;
483-
484-
while (cur) {
485-
next = cur->next;
486-
cur->next = NULL;
487-
int index = hashmap_hash_index(map->cap, cur->key);
488-
target_cur = map->buckets[index];
489-
490-
if (!target_cur) {
491-
map->buckets[index] = cur;
492-
} else {
493-
cur->next = target_cur;
494-
map->buckets[index] = cur;
454+
if (old_table[i].occupied) {
455+
char *key = old_table[i].key;
456+
void *val = old_table[i].val;
457+
458+
int index = hashmap_hash_index(map->cap, key);
459+
int start = index;
460+
461+
while (map->table[index].occupied) {
462+
index = (index + 1) & (map->cap - 1);
463+
if (index == start) {
464+
printf("Error: New table is full during rehash\n");
465+
abort();
466+
}
495467
}
496468

497-
cur = next;
469+
map->table[index].key = key;
470+
map->table[index].val = val;
471+
map->table[index].occupied = true;
472+
map->size++;
498473
}
499474
}
500-
501-
free(old_buckets);
475+
free(old_table);
502476
}
503477

504478
/* Put a key-value pair into given hashmap.
@@ -514,22 +488,30 @@ void hashmap_put(hashmap_t *map, char *key, void *val)
514488
if (!map)
515489
return;
516490

491+
/* Check if size of map exceeds load factor 50% (or 1/2 of capacity) */
492+
if ((map->cap >> 1) <= map->size)
493+
hashmap_rehash(map);
494+
517495
int index = hashmap_hash_index(map->cap, key);
518-
hashmap_node_t *cur = map->buckets[index],
519-
*new_node = hashmap_node_new(key, val);
496+
int start = index;
520497

521-
if (!cur) {
522-
map->buckets[index] = new_node;
523-
} else {
524-
while (cur->next)
525-
cur = cur->next;
526-
cur->next = new_node;
498+
while (map->table[index].occupied) {
499+
if (!strcmp(map->table[index].key, key)) {
500+
map->table[index].val = val;
501+
return;
502+
}
503+
504+
index = (index + 1) & (map->cap - 1);
505+
if (index == start) {
506+
printf("Error: Hashmap is full\n");
507+
abort();
508+
}
527509
}
528510

511+
map->table[index].key = arena_strdup(HASHMAP_ARENA, key);
512+
map->table[index].val = val;
513+
map->table[index].occupied = true;
529514
map->size++;
530-
/* Check if size of map exceeds load factor 75% (or 3/4 of capacity) */
531-
if ((map->cap >> 2) + (map->cap >> 1) <= map->size)
532-
hashmap_rehash(map);
533515
}
534516

535517
/* Get key-value pair node from hashmap from given key.
@@ -545,10 +527,16 @@ hashmap_node_t *hashmap_get_node(hashmap_t *map, char *key)
545527
return NULL;
546528

547529
int index = hashmap_hash_index(map->cap, key);
530+
int start = index;
531+
532+
while (map->table[index].occupied) {
533+
if (!strcmp(map->table[index].key, key))
534+
return &map->table[index];
548535

549-
for (hashmap_node_t *cur = map->buckets[index]; cur; cur = cur->next)
550-
if (!strcmp(cur->key, key))
551-
return cur;
536+
index = (index + 1) & (map->cap - 1);
537+
if (index == start)
538+
return NULL;
539+
}
552540

553541
return NULL;
554542
}
@@ -586,7 +574,7 @@ void hashmap_free(hashmap_t *map)
586574
if (!map)
587575
return;
588576

589-
free(map->buckets);
577+
free(map->table);
590578
free(map);
591579
}
592580

0 commit comments

Comments
 (0)