-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgrowing_map.h
135 lines (109 loc) · 3.21 KB
/
growing_map.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include "../lists/dynarray.h"
#include "hashes.h"
typedef struct {
uint64_t key;
uint64_t val;
} MapEntry;
typedef struct {
DynArray(MapEntry) entries;
DynArray(int64_t) hashes;
Arena *alloc;
} Map;
void map_print_entries(Map *m) {
for (int i = 0; i < m->entries.size; i++) {
MapEntry e = m->entries.arr[i];
printf("%llu - %llu\n", e.key, e.val);
}
}
Map map_init(Arena *a, int elem_count) {
Map m;
m.alloc = a;
int start_count = elem_count;
if (start_count == 0) {
start_count = 8;
}
dyn_init(a, &m.entries, start_count);
dyn_init(a, &m.hashes, start_count);
// Set the whole hashmap to empty. -1 means the slot hasn't been filled yet
for (int i = 0; i < m.hashes.capacity; i++) {
m.hashes.arr[i] = -1;
}
return m;
}
void map_reinsert(Map *m, MapEntry entry, uint64_t entry_idx) {
uint64_t hash_val = fnv1a(entry.key) % m->hashes.capacity;
for (uint64_t i = 0; i < m->hashes.capacity; i++) {
uint64_t cur_hash_idx = (hash_val + i) % m->hashes.capacity;
int64_t cur_hash = m->hashes.arr[cur_hash_idx];
if (cur_hash == -1) {
m->hashes.arr[cur_hash_idx] = entry_idx;
return;
}
}
// This can't happen, if we're reinserting, we should already have grown
return;
}
void map_grow(Map *m) {
dyn_resize(&m->hashes, m->hashes.capacity * 2);
for (uint64_t i = 0; i < m->hashes.capacity; i++) {
m->hashes.arr[i] = -1;
}
for (uint64_t i = 0; i < m->entries.size; i++) {
MapEntry entry = m->entries.arr[i];
map_reinsert(m, entry, i);
}
}
bool map_insert(Map *m, uint64_t key, uint64_t val) {
MapEntry new_entry = {key, val};
// Grow before we insert, so we always have space for our hash
int64_t next_resize_window = (3 * m->hashes.capacity) / 4;
if (m->entries.size >= next_resize_window) {
map_grow(m);
}
uint64_t hash_val = fnv1a(key) % m->hashes.capacity;
for (uint64_t i = 0; i < m->hashes.capacity; i++) {
uint64_t cur_hash_idx = (hash_val + i) % m->hashes.capacity;
int64_t cur_hash = m->hashes.arr[cur_hash_idx];
// Did we find an empty slot?
if (cur_hash == -1) {
m->hashes.arr[cur_hash_idx] = m->entries.size;
dyn_append(&m->entries, new_entry);
return true;
// Is this key already in the map? If so, replace the entry with our new entry
} else if (m->entries.arr[cur_hash].key == key) {
m->entries.arr[cur_hash] = new_entry;
return true;
}
}
// Our hashmap should be growing, this should never happen!
return false;
}
bool map_get(Map *m, uint64_t key, uint64_t *result) {
uint64_t hash_val = fnv1a(key) % m->hashes.capacity;
for (uint64_t i = 0; i < m->hashes.capacity; i++) {
uint64_t cur_hash_idx = (hash_val + i) % m->hashes.capacity;
int64_t cur_hash = m->hashes.arr[cur_hash_idx];
// There's nothing in our hashmap with our key?
if (cur_hash == -1) {
*result = 0;
return false;
// Did we find it? Check to make sure the entry we found has a matching key
} else if (m->entries.arr[cur_hash].key == key) {
*result = m->entries.arr[cur_hash].val;
return true;
}
}
// The hashmap is completely full?
*result = 0;
return false;
}
void map_clear(Map *m) {
for (int i = 0; i < m->hashes.capacity; i++) {
m->hashes.arr[i] = -1;
}
m->entries.size = 0;
}