-
Notifications
You must be signed in to change notification settings - Fork 7
/
address-map.h
210 lines (195 loc) · 7.29 KB
/
address-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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#ifndef ADDRESS_MAP_H
#define ADDRESS_MAP_H
#include <malloc.h>
#include <stdint.h>
#include <string.h>
#include "address-hash.h"
struct hash_map_entry {
uintptr_t k;
uintptr_t v;
};
struct hash_map {
struct hash_map_entry *data;
size_t size; // total number of slots
size_t n_items; // number of items in set
uint8_t *bits; // bitvector indicating set slots
};
static void hash_map_clear(struct hash_map *map) {
memset(map->bits, 0, map->size / 8);
map->n_items = 0;
}
// Size must be a power of 2.
static void hash_map_init(struct hash_map *map, size_t size) {
map->size = size;
map->data = malloc(sizeof(struct hash_map_entry) * size);
map->bits = malloc(size / 8);
hash_map_clear(map);
}
static void hash_map_destroy(struct hash_map *map) {
free(map->data);
free(map->bits);
}
static size_t hash_map_slot_index(struct hash_map *map, size_t idx) {
return idx & (map->size - 1);
}
static struct hash_map_entry* hash_map_slot_entry(struct hash_map *map,
size_t idx) {
return &map->data[hash_map_slot_index(map, idx)];
}
static int hash_map_slot_is_empty(struct hash_map *map, size_t idx) {
idx = hash_map_slot_index(map, idx);
return (map->bits[idx / 8] & (1 << (idx % 8))) == 0;
}
static void hash_map_slot_acquire(struct hash_map *map, size_t idx) {
idx = hash_map_slot_index(map, idx);
map->bits[idx / 8] |= (1 << (idx % 8));
map->n_items++;
}
static void hash_map_slot_release(struct hash_map *map, size_t idx) {
idx = hash_map_slot_index(map, idx);
map->bits[idx / 8] &= ~(1 << (idx % 8));
map->n_items--;
}
static size_t hash_map_slot_distance(struct hash_map *map, size_t idx) {
return hash_map_slot_index(map, idx - hash_map_slot_entry(map, idx)->k);
}
static int hash_map_should_shrink(struct hash_map *map) {
return map->size > 8 && map->n_items <= (map->size >> 3);
}
static int hash_map_should_grow(struct hash_map *map) {
return map->n_items >= map->size - (map->size >> 3);
}
static void hash_map_do_insert(struct hash_map *map, uintptr_t k, uintptr_t v) {
size_t displacement = 0;
while (!hash_map_slot_is_empty(map, k + displacement)
&& displacement < hash_map_slot_distance(map, k + displacement))
displacement++;
while (!hash_map_slot_is_empty(map, k + displacement)
&& displacement == hash_map_slot_distance(map, k + displacement)) {
if (hash_map_slot_entry(map, k + displacement)->k == k) {
hash_map_slot_entry(map, k + displacement)->v = v;
return;
}
displacement++;
}
size_t idx = k + displacement;
size_t slots_to_move = 0;
while (!hash_map_slot_is_empty(map, idx + slots_to_move))
slots_to_move++;
hash_map_slot_acquire(map, idx + slots_to_move);
while (slots_to_move--)
*hash_map_slot_entry(map, idx + slots_to_move + 1) =
*hash_map_slot_entry(map, idx + slots_to_move);
*hash_map_slot_entry(map, idx) = (struct hash_map_entry){ k, v };
}
static void hash_map_populate(struct hash_map *dst, struct hash_map *src) {
for (size_t i = 0; i < src->size; i++)
if (!hash_map_slot_is_empty(src, i))
hash_map_do_insert(dst, hash_map_slot_entry(src, i)->k,
hash_map_slot_entry(src, i)->v);
}
static void hash_map_grow(struct hash_map *map) {
struct hash_map fresh;
hash_map_init(&fresh, map->size << 1);
hash_map_populate(&fresh, map);
hash_map_destroy(map);
memcpy(map, &fresh, sizeof(fresh));
}
static void hash_map_shrink(struct hash_map *map) {
struct hash_map fresh;
hash_map_init(&fresh, map->size >> 1);
hash_map_populate(&fresh, map);
hash_map_destroy(map);
memcpy(map, &fresh, sizeof(fresh));
}
static void hash_map_insert(struct hash_map *map, uintptr_t k, uintptr_t v) {
if (hash_map_should_grow(map))
hash_map_grow(map);
hash_map_do_insert(map, k, v);
}
static void hash_map_remove(struct hash_map *map, uintptr_t k) {
size_t slot = k;
while (!hash_map_slot_is_empty(map, slot) && hash_map_slot_entry(map, slot)->k != k)
slot++;
if (hash_map_slot_is_empty(map, slot))
__builtin_trap();
while (!hash_map_slot_is_empty(map, slot + 1)
&& hash_map_slot_distance(map, slot + 1)) {
*hash_map_slot_entry(map, slot) = *hash_map_slot_entry(map, slot + 1);
slot++;
}
hash_map_slot_release(map, slot);
if (hash_map_should_shrink(map))
hash_map_shrink(map);
}
static int hash_map_contains(struct hash_map *map, uintptr_t k) {
for (size_t slot = k; !hash_map_slot_is_empty(map, slot); slot++) {
if (hash_map_slot_entry(map, slot)->k == k)
return 1;
if (hash_map_slot_distance(map, slot) < (slot - k))
return 0;
}
return 0;
}
static uintptr_t hash_map_lookup(struct hash_map *map, uintptr_t k, uintptr_t default_) {
for (size_t slot = k; !hash_map_slot_is_empty(map, slot); slot++) {
if (hash_map_slot_entry(map, slot)->k == k)
return hash_map_slot_entry(map, slot)->v;
if (hash_map_slot_distance(map, slot) < (slot - k))
break;
}
return default_;
}
static inline void hash_map_for_each (struct hash_map *map,
void (*f)(uintptr_t, uintptr_t, void*),
void *data) __attribute__((always_inline));
static inline void hash_map_for_each(struct hash_map *map,
void (*f)(uintptr_t, uintptr_t, void*),
void *data) {
for (size_t i = 0; i < map->size; i++)
if (!hash_map_slot_is_empty(map, i))
f(hash_map_slot_entry(map, i)->k, hash_map_slot_entry(map, i)->v, data);
}
struct address_map {
struct hash_map hash_map;
};
static void address_map_init(struct address_map *map) {
hash_map_init(&map->hash_map, 8);
}
static void address_map_destroy(struct address_map *map) {
hash_map_destroy(&map->hash_map);
}
static void address_map_clear(struct address_map *map) {
hash_map_clear(&map->hash_map);
}
static void address_map_add(struct address_map *map, uintptr_t addr, uintptr_t v) {
hash_map_insert(&map->hash_map, hash_address(addr), v);
}
static void address_map_remove(struct address_map *map, uintptr_t addr) {
hash_map_remove(&map->hash_map, hash_address(addr));
}
static int address_map_contains(struct address_map *map, uintptr_t addr) {
return hash_map_contains(&map->hash_map, hash_address(addr));
}
static uintptr_t address_map_lookup(struct address_map *map, uintptr_t addr,
uintptr_t default_) {
return hash_map_lookup(&map->hash_map, hash_address(addr), default_);
}
struct address_map_for_each_data {
void (*f)(uintptr_t, uintptr_t, void *);
void *data;
};
static void address_map_do_for_each(uintptr_t k, uintptr_t v, void *data) {
struct address_map_for_each_data *for_each_data = data;
for_each_data->f(unhash_address(k), v, for_each_data->data);
}
static inline void address_map_for_each (struct address_map *map,
void (*f)(uintptr_t, uintptr_t, void*),
void *data) __attribute__((always_inline));
static inline void address_map_for_each (struct address_map *map,
void (*f)(uintptr_t, uintptr_t, void*),
void *data) {
struct address_map_for_each_data for_each_data = { f, data };
hash_map_for_each(&map->hash_map, address_map_do_for_each, &for_each_data);
}
#endif // ADDRESS_MAP_H