Derive-C
Loading...
Searching...
No Matches
template.h
Go to the documentation of this file.
1
2
3#include <stdbool.h>
4#include <stddef.h>
5#include <stdint.h>
6#include <stdlib.h>
7
11
14
15#if !defined KEY
16 #if !defined __clang_analyzer__
17 #error "KEY must be defined to for a hashmap template"
18 #endif
19typedef struct {
20 int x;
21} key_t;
22 #define KEY key_t
23static void key_delete(key_t* UNUSED(self)) {}
24 #define KEY_DELETE key_delete
25static key_t key_clone(key_t const* self) { return *self; }
26 #define KEY_CLONE key_clone
27static bool key_eq(key_t const* key_1, key_t const* key_2) { return key_1->x == key_2->x; }
28 #define KEY_EQ key_eq
29#endif
30
31#if !defined KEY_HASH
32 #if !defined __clang_analyzer__
33 #error "The hash function for K must be defined"
34 #endif
35static size_t key_hash(key_t const* key);
36 #define KEY_HASH key_hash
37#endif
38
39#if !defined KEY_EQ
40 #define KEY_EQ(key_1, key_2) (*(key_1) == *(key_2))
41#endif
42
43#if !defined KEY_DELETE
44 #define KEY_DELETE(key)
45#endif
46
47#if !defined KEY_CLONE
48 #define KEY_CLONE(value) (*(value))
49#endif
50
51#if !defined VALUE
52 #if !defined __clang_analyzer__
53 #error "VALUE must be defined to for a hashmap template"
54 #endif
55typedef struct {
56 int x;
57} value_t;
58 #define VALUE value_t
59static void value_delete(value_t* UNUSED(self)) {}
60 #define VALUE_DELETE value_delete
61static value_t value_clone(value_t const* self) { return *self; }
62 #define VALUE_CLONE value_clone
63#endif
64
65#if !defined VALUE_DELETE
66 #define VALUE_DELETE(value)
67#endif
68
69#if !defined VALUE_CLONE
70 #define VALUE_CLONE(value) (*(value))
71#endif
72
73#define KEY_ENTRY NS(SELF, key_entry)
74typedef struct {
75 bool present;
78} KEY_ENTRY;
79
80typedef struct {
81 size_t capacity; // INVARIANT: A power of 2
82 size_t items;
83 // Split keys & values in an old hashmap I used, cannot remember why (many collisions better
84 // decomposed), should probably use 1 buffer.
87 ALLOC* alloc;
89} SELF;
90
91static SELF NS(SELF, new_with_capacity_for)(size_t capacity, ALLOC* alloc) {
92 ASSERT(capacity > 0);
93 size_t real_capacity = apply_capacity_policy(capacity);
94 ASSERT(real_capacity > 0);
95 // JUSTIFY: calloc of keys
96 // - A cheap way to get all precense flags as zeroed (os & allocater supported get zeroed page)
97 // - for the values, we do not need this (no precense checks are done on values)
98 KEY_ENTRY* keys = (KEY_ENTRY*)NS(ALLOC, calloc)(alloc, sizeof(KEY_ENTRY), real_capacity);
99 VALUE* values = (VALUE*)NS(ALLOC, malloc)(alloc, sizeof(VALUE) * real_capacity);
100 ASSERT(keys && values);
101 return (SELF){
102 .capacity = real_capacity,
103 .items = 0,
104 .keys = keys,
105 .values = values,
106 .alloc = alloc,
107 };
108}
109
110static SELF NS(SELF, new)(ALLOC* alloc) {
112}
113
114static SELF NS(SELF, clone)(SELF const* self) {
115 DEBUG_ASSERT(self);
116
117 // JUSTIFY: Naive copy
118 // - We could resize (potentially a smaller map) and rehash
119 // - Not confident it would be any better than just a copy.
120 // JUSTIFY: Individually copy keys
121 // - Many entries are zeroed, no need to copy uninit data
122
123 KEY_ENTRY* keys = (KEY_ENTRY*)NS(ALLOC, calloc)(self->alloc, sizeof(KEY_ENTRY), self->capacity);
124 VALUE* values = (VALUE*)NS(ALLOC, malloc)(self->alloc, sizeof(VALUE) * self->capacity);
125 ASSERT(keys && values);
126
127 for (size_t i = 0; i < self->capacity; i++) {
128 if (self->keys[i].present) {
129 KEY_ENTRY const* old_entry = &self->keys[i];
130 keys[i] = (KEY_ENTRY){
131 .present = true,
132 .distance_from_desired = old_entry->distance_from_desired,
133 .key = KEY_CLONE(&old_entry->key),
134 };
135 values[i] = VALUE_CLONE(&self->values[i]);
136 }
137 }
138
139 return (SELF){
140 .capacity = self->capacity,
141 .items = self->items,
142 .keys = keys,
143 .values = values,
144 .alloc = self->alloc,
145 };
146}
147
148static void NS(SELF, delete)(SELF* self);
149
150static VALUE* NS(SELF, insert)(SELF* self, KEY key, VALUE value);
151
152static void NS(SELF, extend_capacity_for)(SELF* self, size_t expected_items) {
153 DEBUG_ASSERT(self);
154 size_t target_capacity = apply_capacity_policy(expected_items);
155 if (target_capacity > self->capacity) {
156 SELF new_map = NS(SELF, new_with_capacity_for)(expected_items, self->alloc);
157 for (size_t index = 0; index < self->capacity; index++) {
158 KEY_ENTRY* entry = &self->keys[index];
159 if (entry->present) {
160 NS(SELF, insert)(&new_map, entry->key, self->values[index]);
161 }
162 }
163 NS(ALLOC, free)(self->alloc, (void*)self->keys);
164 NS(ALLOC, free)(self->alloc, (void*)self->values);
165 *self = new_map;
166 }
167}
168
169static VALUE* NS(SELF, try_insert)(SELF* self, KEY key, VALUE value) {
170 DEBUG_ASSERT(self);
171 if (apply_capacity_policy(self->items) > self->capacity / 2) {
172 NS(SELF, extend_capacity_for)(self, self->items * 2);
173 }
174
175 uint16_t distance_from_desired = 0;
176 size_t hash = KEY_HASH(&key);
177 size_t index = modulus_power_of_2_capacity(hash, self->capacity);
178 VALUE* placed_entry = NULL;
179 for (;;) {
180 KEY_ENTRY* entry = &self->keys[index];
181 DEBUG_ASSERT(distance_from_desired < self->capacity);
182
183 if (entry->present) {
184 if (KEY_EQ(&entry->key, &key)) {
185 return NULL;
186 }
187
188 if (entry->distance_from_desired < distance_from_desired) {
189 KEY switch_key = entry->key;
190 uint16_t switch_distance_from_desired = entry->distance_from_desired;
191 VALUE switch_value = self->values[index];
192
193 entry->key = key;
194 entry->distance_from_desired = distance_from_desired;
195 self->values[index] = value;
196
197 key = switch_key;
198 distance_from_desired = switch_distance_from_desired;
199 value = switch_value;
200
201 if (!placed_entry) {
202 placed_entry = &self->values[index];
203 }
204 }
205
206 distance_from_desired++;
207 index = modulus_power_of_2_capacity(index + 1, self->capacity);
208 } else {
209 entry->present = true;
210 entry->distance_from_desired = distance_from_desired;
211 entry->key = key;
212 self->values[index] = value;
213 if (!placed_entry) {
214 placed_entry = &self->values[index];
215 }
216 self->items++;
217 return placed_entry;
218 }
219 }
220}
221
222static VALUE* NS(SELF, insert)(SELF* self, KEY key, VALUE value) {
223 VALUE* value_ptr = NS(SELF, try_insert)(self, key, value);
224 ASSERT(value_ptr);
225 return value_ptr;
226}
227
228static VALUE* NS(SELF, try_write)(SELF* self, KEY key) {
229 DEBUG_ASSERT(self);
230 size_t hash = KEY_HASH(&key);
231 size_t index = modulus_power_of_2_capacity(hash, self->capacity);
232
233 for (;;) {
234 KEY_ENTRY* entry = &self->keys[index];
235 if (entry->present) {
236 if (KEY_EQ(&entry->key, &key)) {
237 return &self->values[index];
238 }
239 index = modulus_power_of_2_capacity(index + 1, self->capacity);
240 } else {
241 return NULL;
242 }
243 }
244}
245
246static VALUE* NS(SELF, write)(SELF* self, KEY key) {
247 VALUE* value = NS(SELF, try_write)(self, key);
248 ASSERT(value);
249 return value;
250}
251
252static VALUE const* NS(SELF, try_read)(SELF const* self, KEY key) {
253 DEBUG_ASSERT(self);
254 size_t hash = KEY_HASH(&key);
255 size_t index = modulus_power_of_2_capacity(hash, self->capacity);
256
257 for (;;) {
258 KEY_ENTRY* entry = &self->keys[index];
259 if (entry->present) {
260 if (KEY_EQ(&entry->key, &key)) {
261 return &self->values[index];
262 }
263 index = modulus_power_of_2_capacity(index + 1, self->capacity);
264 } else {
265 return NULL;
266 }
267 }
268}
269
270static VALUE const* NS(SELF, read)(SELF const* self, KEY key) {
271 VALUE const* value = NS(SELF, try_read)(self, key);
272 ASSERT(value);
273 return value;
274}
275
276static bool NS(SELF, try_remove)(SELF* self, KEY key, VALUE* destination) {
277 DEBUG_ASSERT(self);
278 size_t hash = KEY_HASH(&key);
279 size_t index = modulus_power_of_2_capacity(hash, self->capacity);
280
281 for (;;) {
282 KEY_ENTRY* entry = &self->keys[index];
283 if (entry->present) {
284 if (KEY_EQ(&entry->key, &key)) {
285 self->items--;
286
287 *destination = self->values[index];
288 KEY_DELETE(&entry->key);
289
290 // NOTE: For robin hood hashing, we need probe chains to be unbroken
291 // - Need to find the entries that might use this chain (
292 // distance_to_desired > 0 until the next not-present or
293 // distance_to_desired=0 slot)
294 // hence we shift entries the left (being careful with modulus index)
295
296 size_t free_index = index;
297 KEY_ENTRY* free_entry = entry;
298
299 size_t check_index = modulus_power_of_2_capacity(free_index + 1, self->capacity);
300 KEY_ENTRY* check_entry = &self->keys[check_index];
301
302 while (check_entry->present && (check_entry->distance_from_desired > 0)) {
303 free_entry->key = check_entry->key;
304 free_entry->distance_from_desired = check_entry->distance_from_desired - 1;
305 self->values[free_index] = self->values[check_index];
306
307 free_index = check_index;
308 free_entry = check_entry;
309
310 check_index = modulus_power_of_2_capacity(free_index + 1, self->capacity);
311 check_entry = &self->keys[check_index];
312 }
313
314 // JUSTIFY: Only setting free entry to false
315 // - We remove, then shift down an index
316 // - The removed entry already has the flag set
317 // - the free entry was the last one removed/moved down an index, so it
318 // should be false.
319
320 free_entry->present = false;
321
322 return true;
323 }
324 index = modulus_power_of_2_capacity(index + 1, self->capacity);
325 } else {
326 return false;
327 }
328 }
329}
330
331static VALUE NS(SELF, remove)(SELF* self, KEY key) {
332 VALUE value;
333 ASSERT(NS(SELF, try_remove)(self, key, &value));
334 return value;
335}
336
337static void NS(SELF, delete_entry)(SELF* self, KEY key) {
338 VALUE value = NS(SELF, remove)(self, key);
339 VALUE_DELETE(&value);
340}
341
342static size_t NS(SELF, size)(SELF const* self) {
343 DEBUG_ASSERT(self);
344 return self->items;
345}
346
347#define KV_PAIR NS(SELF, kv)
348
349typedef struct {
350 KEY const* key;
352} KV_PAIR;
353
354#define ITER NS(SELF, iter)
355
356typedef struct {
357 SELF* map;
358 size_t index;
359 KV_PAIR curr;
360} ITER;
361
362static KV_PAIR const* NS(ITER, next)(ITER* iter) {
363 DEBUG_ASSERT(iter);
364 if (iter->index < iter->map->capacity) {
365 iter->curr = (KV_PAIR){.key = &iter->map->keys[iter->index].key,
366 .value = &iter->map->values[iter->index]};
367 iter->index++;
368 while (iter->index < iter->map->capacity && !iter->map->keys[iter->index].present) {
369 iter->index++;
370 }
371 return &iter->curr;
372 }
373 return NULL;
374}
375
376static bool NS(ITER, empty)(ITER const* iter) {
377 DEBUG_ASSERT(iter);
378 return iter->index >= iter->map->capacity;
379}
380
381static ITER NS(SELF, get_iter)(SELF* self) {
382 DEBUG_ASSERT(self);
383 size_t first_index = 0;
384 while (first_index < self->capacity && !self->keys[first_index].present) {
385 first_index++;
386 }
387 return (ITER){
388 .map = self,
389 .index = first_index,
390 .curr = (KV_PAIR){.key = NULL, .value = NULL},
391 };
392}
393
394static void NS(SELF, delete)(SELF* self) {
395 DEBUG_ASSERT(self);
396 ITER iter = NS(SELF, get_iter)(self);
397
398 for (size_t i = 0; i < self->capacity; i++) {
399 if (self->keys[i].present) {
400 KEY_DELETE(&self->keys[i].key);
401 VALUE_DELETE(&self->values[i]);
402 }
403 }
404
405 NS(ALLOC, free)(self->alloc, (void*)self->keys);
406 NS(ALLOC, free)(self->alloc, (void*)self->values);
407}
408
409#undef ITER
410#undef KV_PAIR
411
412#define KV_PAIR_CONST NS(SELF, kv_const)
413
414typedef struct {
415 KEY const* key;
416 VALUE const* value;
418
419#define ITER_CONST NS(SELF, iter_const)
420
421typedef struct {
422 SELF const* map;
423 size_t index;
424 KV_PAIR_CONST curr;
425} ITER_CONST;
426
427static KV_PAIR_CONST const* NS(ITER_CONST, next)(ITER_CONST* iter) {
428 DEBUG_ASSERT(iter);
429 if (iter->index < iter->map->capacity) {
430 iter->curr = (KV_PAIR_CONST){.key = &iter->map->keys[iter->index].key,
431 .value = &iter->map->values[iter->index]};
432 iter->index++;
433 while (iter->index < iter->map->capacity && !iter->map->keys[iter->index].present) {
434 iter->index++;
435 }
436 return &iter->curr;
437 }
438 return NULL;
439}
440
441static bool NS(ITER_CONST, empty)(ITER_CONST const* iter) {
442 DEBUG_ASSERT(iter);
443 return iter->index >= iter->map->capacity;
444}
445
446static ITER_CONST NS(SELF, get_iter_const)(SELF const* self) {
447 DEBUG_ASSERT(self);
448 size_t first_index = 0;
449 while (first_index < self->capacity && !self->keys[first_index].present) {
450 first_index++;
451 }
452 return (ITER_CONST){
453 .map = self,
454 .index = first_index,
455 .curr = (KV_PAIR_CONST){.key = NULL, .value = NULL},
456 };
457}
458
459#undef ITER_CONST
460#undef KV_PAIR_CONST
461
462#undef KEY_ENTRY
463
464#undef KEY
465#undef KEY_HASH
466#undef KEY_EQ
467#undef KEY_DELETE
468#undef KEY_CLONE
469#undef VALUE
470#undef VALUE_DELETE
471#undef VALUE_CLONE
472
static void free(SELF *self, void *ptr)
Definition template.h:56
static void * malloc(SELF *self, size_t size)
Definition template.h:23
static void * calloc(SELF *self, size_t count, size_t size)
Definition template.h:34
#define ALLOC
Definition template.h:56
static size_t const INITIAL_CAPACITY
Definition utils.h:14
static size_t apply_capacity_policy(size_t capacity)
Definition utils.h:8
#define UNUSED(ident)
Definition helpers.h:16
#define NS(pre, post)
Definition helpers.h:6
static size_t modulus_power_of_2_capacity(size_t index, size_t capacity)
Definition helpers.h:48
#define ASSERT(expr,...)
Definition panic.h:15
#define DEBUG_ASSERT(expr)
Definition panic.h:34
#define SELF
Definition def.h:51
bool present
Definition template.h:75
KEY key
Definition template.h:77
uint16_t distance_from_desired
Definition template.h:76
VALUE const * value
Definition template.h:416
KEY const * key
Definition template.h:415
KEY const * key
Definition template.h:350
VALUE * value
Definition template.h:351
gdb_marker derive_c_hashmap
Definition template.h:88
size_t items
Definition template.h:82
VALUE * values
Definition template.h:86
ALLOC * alloc
Definition template.h:63
KEY_ENTRY * keys
Definition template.h:85
A simple open-addressed hashmap using robin-hood hashing.
Definition template.h:19
int x
Definition template.h:20
static INDEX insert(SELF *self, VALUE value)
Definition template.h:129
static ITER_CONST get_iter_const(SELF const *self)
Definition template.h:387
static bool empty(ITER const *iter)
Definition template.h:293
static ITER get_iter(SELF *self)
Definition template.h:318
static SELF new_with_capacity_for(INDEX_TYPE items, ALLOC *alloc)
Definition template.h:113
static INDEX_TYPE size(SELF const *self)
Definition template.h:220
static IV_PAIR const * next(ITER *iter)
Definition template.h:301
static VALUE remove(SELF *self, INDEX index)
Definition template.h:256
static VALUE * try_write(SELF *self, INDEX index)
Definition template.h:159
static bool delete_entry(SELF *self, INDEX index)
Definition template.h:262
static VALUE const * read(SELF const *self, INDEX index)
Definition template.h:189
static VALUE * write(SELF *self, INDEX index)
Definition template.h:171
#define VALUE
Definition template.h:29
static value_t value_clone(value_t const *self)
Definition template.h:32
static void value_delete(value_t *UNUSED(self))
Definition template.h:30
#define VALUE_CLONE
Definition template.h:33
#define VALUE_DELETE
Definition template.h:31
static bool try_remove(SELF *self, INDEX index, VALUE *destination)
Definition template.h:238
static SELF clone(SELF const *self)
Definition template.h:195
static VALUE const * try_read(SELF const *self, INDEX index)
Definition template.h:177
#define KEY_HASH
Definition template.h:36
#define KV_PAIR
Definition template.h:347
#define KEY_DELETE
Definition template.h:24
#define KEY_ENTRY
Definition template.h:73
static key_t key_clone(key_t const *self)
Definition template.h:25
static void key_delete(key_t *UNUSED(self))
Definition template.h:23
#define KEY_CLONE
Definition template.h:26
static VALUE * try_insert(SELF *self, KEY key, VALUE value)
Definition template.h:169
static bool key_eq(key_t const *key_1, key_t const *key_2)
Definition template.h:27
#define KEY
Definition template.h:22
#define KEY_EQ
Definition template.h:28
#define VALUE
Definition template.h:58
static void extend_capacity_for(SELF *self, size_t expected_items)
Definition template.h:152
#define KV_PAIR_CONST
Definition template.h:412
static size_t key_hash(key_t const *key)