Derive-C
Loading...
Searching...
No Matches
template.h File Reference
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include "utils.h"
#include <derive-c/core/debug/gdb_marker.h>
#include <derive-c/core/debug/memory_tracker.h>
#include <derive-c/core/debug/mutation_tracker.h>
#include <derive-c/core/prelude.h>
#include <derive-c/core/alloc/def.h>
#include <derive-c/core/self/def.h>
#include <derive-c/core/alloc/undef.h>
#include <derive-c/container/map/trait.h>
#include <derive-c/core/self/undef.h>
Include dependency graph for template.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  value_t
struct  KEY_ENTRY
struct  SELF
 An allocator that prints to stdout when it allocates or frees memory. More...
struct  KV_PAIR
struct  KV_PAIR_CONST

Macros

#define KEY   map_key_t
 A simple open-addressed hashmap using robin-hood hashing.
#define KEY_HASH   key_hash
#define KEY_EQ   NS(SELF, key_eq_default)
#define KEY_DELETE   NS(SELF, key_delete_default)
#define KEY_CLONE   NS(SELF, key_clone_default)
#define VALUE   value_t
#define VALUE_DELETE   NS(SELF, value_delete_default)
#define VALUE_CLONE   NS(SELF, value_clone_default)
#define KEY_ENTRY   NS(SELF, key_entry)
#define INVARIANT_CHECK(self)
#define KV_PAIR   NS(SELF, kv)
#define KV_PAIR_CONST   NS(SELF, kv_const)

Typedefs

typedef int KEY
typedef KEY key_t
typedef KV_PAIR const * item

Functions

static size_t KEY_HASH (KEY const *key)
static bool KEY_EQ (KEY const *key_1, KEY const *key_2)
static void KEY_DELETE (KEY *key)
static KEY KEY_CLONE (KEY const *key)
static void VALUE_DELETE (VALUE *value)
static VALUE VALUE_CLONE (VALUE const *value)
static SELF new_with_capacity_for (size_t capacity, ALLOC *alloc)
static SELF new (ALLOC *alloc)
static SELF clone (SELF const *self)
static void delete (SELF *self)
static VALUEinsert (SELF *self, KEY key, VALUE value)
static void extend_capacity_for (SELF *self, size_t expected_items)
static VALUEtry_insert (SELF *self, KEY key, VALUE value)
static VALUEtry_write (SELF *self, KEY key)
static VALUEwrite (SELF *self, KEY key)
static VALUE const * try_read (SELF const *self, KEY key)
static VALUE const * read (SELF const *self, KEY key)
static bool try_remove (SELF *self, KEY key, VALUE *destination)
static VALUE remove (SELF *self, KEY key)
static void delete_entry (SELF *self, KEY key)
static size_t size (SELF const *self)
static bool empty_item (KV_PAIR const *const *item)
static KV_PAIR const * next (ITER *iter)
static bool empty (ITER const *iter)
static ITER get_iter (SELF *self)
static bool empty_item (KV_PAIR_CONST const *const *item)
static KV_PAIR_CONST const * next (ITER_CONST *iter)
static bool empty (ITER_CONST const *iter)
static ITER_CONST get_iter_const (SELF const *self)
 TRAIT_MAP (SELF)

Macro Definition Documentation

◆ INVARIANT_CHECK

#define INVARIANT_CHECK ( self)
Value:
ASSUME(self); \
ASSUME(is_power_of_2((self)->capacity)); \
ASSUME((self)->keys); \
ASSUME((self)->values); \
ASSUME((self)->alloc);
#define ASSUME(expr,...)
Definition macros.h:62
static bool INLINE CONST is_power_of_2(size_t x)
Definition math.h:23

Definition at line 91 of file template.h.

91#define INVARIANT_CHECK(self) \
92 ASSUME(self); \
93 ASSUME(is_power_of_2((self)->capacity)); \
94 ASSUME((self)->keys); \
95 ASSUME((self)->values); \
96 ASSUME((self)->alloc);

◆ KEY

#define KEY   map_key_t

A simple open-addressed hashmap using robin-hood hashing.

Definition at line 22 of file template.h.

◆ KEY_CLONE

#define KEY_CLONE   NS(SELF, key_clone_default)

Definition at line 46 of file template.h.

◆ KEY_DELETE

#define KEY_DELETE   NS(SELF, key_delete_default)

Definition at line 41 of file template.h.

◆ KEY_ENTRY

#define KEY_ENTRY   NS(SELF, key_entry)

Definition at line 74 of file template.h.

◆ KEY_EQ

#define KEY_EQ   NS(SELF, key_eq_default)

Definition at line 36 of file template.h.

◆ KEY_HASH

#define KEY_HASH   key_hash

Definition at line 31 of file template.h.

◆ KV_PAIR

#define KV_PAIR   NS(SELF, kv)

Definition at line 386 of file template.h.

◆ KV_PAIR_CONST

#define KV_PAIR_CONST   NS(SELF, kv_const)

Definition at line 457 of file template.h.

◆ VALUE

#define VALUE   value_t

Definition at line 57 of file template.h.

◆ VALUE_CLONE

#define VALUE_CLONE   NS(SELF, value_clone_default)

Definition at line 66 of file template.h.

◆ VALUE_DELETE

#define VALUE_DELETE   NS(SELF, value_delete_default)

Definition at line 61 of file template.h.

Typedef Documentation

◆ item

typedef KV_PAIR_CONST const* item

Definition at line 394 of file template.h.

◆ KEY

typedef int KEY

Definition at line 23 of file template.h.

◆ key_t

typedef KEY key_t

Definition at line 70 of file template.h.

Function Documentation

◆ clone()

SELF clone ( SELF const * self)
static

Definition at line 132 of file template.h.

132 {
133 INVARIANT_CHECK(self);
134
135 // JUSTIFY: Naive copy
136 // - We could resize (potentially a smaller map) and rehash
137 // - Not confident it would be any better than just a copy.
138 // JUSTIFY: Individually copy keys
139 // - Many entries are zeroed, no need to copy uninit data
140
141 KEY_ENTRY* keys = (KEY_ENTRY*)NS(ALLOC, calloc)(self->alloc, sizeof(KEY_ENTRY), self->capacity);
142 VALUE* values = (VALUE*)NS(ALLOC, malloc)(self->alloc, sizeof(VALUE) * self->capacity);
143 ASSERT(keys && values);
144
145 for (size_t i = 0; i < self->capacity; i++) {
146 if (self->keys[i].present) {
147 KEY_ENTRY const* old_entry = &self->keys[i];
148 keys[i] = (KEY_ENTRY){
149 .present = true,
150 .distance_from_desired = old_entry->distance_from_desired,
151 .key = KEY_CLONE(&old_entry->key),
152 };
153 values[i] = VALUE_CLONE(&self->values[i]);
154 } else {
156 sizeof(VALUE));
157 }
158 }
159
160 return (SELF){
161 .capacity = self->capacity,
162 .items = self->items,
163 .keys = keys,
164 .values = values,
165 .alloc = self->alloc,
166 .derive_c_hashmap = gdb_marker_new(),
167 .iterator_invalidation_tracker = mutation_tracker_new(),
168 };
169}
static void * malloc(SELF *self, size_t size)
Definition template.h:25
static void * calloc(SELF *self, size_t count, size_t size)
Definition template.h:36
#define ALLOC
Definition template.h:59
#define INVARIANT_CHECK(self)
Definition template.h:142
#define VALUE
Definition template.h:33
#define VALUE_CLONE
Definition template.h:37
#define KEY_ENTRY
Definition template.h:74
#define KEY_CLONE
Definition template.h:46
static gdb_marker gdb_marker_new()
Definition gdb_marker.h:7
#define ASSERT(expr,...)
Definition macros.h:42
@ MEMORY_TRACKER_LVL_CONTAINER
static void memory_tracker_set(memory_tracker_level level, memory_tracker_capability cap, const volatile void *addr, size_t size)
@ MEMORY_TRACKER_CAP_NONE
static mutation_tracker mutation_tracker_new()
#define NS(pre, post)
Definition namespace.h:4
#define SELF
Definition def.h:52
KEY key
Definition template.h:78
uint16_t distance_from_desired
Definition template.h:77
Here is the call graph for this function:

◆ delete()

void delete ( SELF * self)
static

Definition at line 440 of file template.h.

440 {
441 ASSUME(self);
442
443 for (size_t i = 0; i < self->capacity; i++) {
444 if (self->keys[i].present) {
445 KEY_DELETE(&self->keys[i].key);
446 VALUE_DELETE(&self->values[i]);
447 }
448 }
449
450 NS(ALLOC, free)(self->alloc, (void*)self->keys);
451 NS(ALLOC, free)(self->alloc, (void*)self->values);
452}
static void free(SELF *self, void *ptr)
Definition template.h:58
#define VALUE_DELETE
Definition template.h:35
#define KEY_DELETE
Definition template.h:41
bool present
Definition template.h:76
size_t capacity
Definition template.h:124
VALUE * values
Definition template.h:85
ALLOC * alloc
Definition template.h:66
KEY_ENTRY * keys
Definition template.h:84
Here is the call graph for this function:

◆ delete_entry()

void delete_entry ( SELF * self,
KEY key )
static

Definition at line 376 of file template.h.

376 {
377 VALUE value = NS(SELF, remove)(self, key);
378 VALUE_DELETE(&value);
379}
static VALUE remove(SELF *self, INDEX index)
Definition template.h:313
Here is the call graph for this function:

◆ empty() [1/2]

bool empty ( ITER const * iter)
static

Definition at line 420 of file template.h.

420 {
421 ASSUME(iter);
422 mutation_version_check(&iter->version);
423 return iter->index >= iter->map->capacity;
424}
static void mutation_version_check(mutation_version const *self)
Here is the call graph for this function:

◆ empty() [2/2]

bool empty ( ITER_CONST const * iter)
static

Definition at line 491 of file template.h.

491 {
492 ASSUME(iter);
493 mutation_version_check(&iter->version);
494 return iter->index >= iter->map->capacity;
495}
Here is the call graph for this function:

◆ empty_item() [1/2]

bool empty_item ( KV_PAIR const *const * item)
static

Definition at line 396 of file template.h.

396{ return *item == NULL; }
IV_PAIR const * item
Definition template.h:350
Here is the call graph for this function:

◆ empty_item() [2/2]

bool empty_item ( KV_PAIR_CONST const *const * item)
static

Definition at line 467 of file template.h.

467{ return *item == NULL; }
Here is the call graph for this function:

◆ extend_capacity_for()

void extend_capacity_for ( SELF * self,
size_t expected_items )
static

Definition at line 175 of file template.h.

175 {
176 INVARIANT_CHECK(self);
177
179 size_t const target_capacity = apply_capacity_policy(expected_items);
180 if (target_capacity > self->capacity) {
181 SELF new_map = NS(SELF, new_with_capacity_for)(expected_items, self->alloc);
182 for (size_t index = 0; index < self->capacity; index++) {
183 KEY_ENTRY* entry = &self->keys[index];
184 if (entry->present) {
185 NS(SELF, insert)(&new_map, entry->key, self->values[index]);
186 }
187 }
188 NS(ALLOC, free)(self->alloc, (void*)self->keys);
189 NS(ALLOC, free)(self->alloc, (void*)self->values);
190
191 INVARIANT_CHECK(&new_map);
192 *self = new_map;
193 }
194}
static INDEX insert(SELF *self, VALUE value)
Definition template.h:171
static SELF new_with_capacity_for(INDEX_TYPE items, ALLOC *alloc)
Definition template.h:148
static size_t apply_capacity_policy(size_t capacity)
Definition utils.h:7
static void mutation_tracker_mutate(mutation_tracker *self)
mutation_tracker iterator_invalidation_tracker
Definition template.h:139
Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_iter()

ITER get_iter ( SELF * self)
static

Definition at line 426 of file template.h.

426 {
427 ASSUME(self);
428 size_t first_index = 0;
429 while (first_index < self->capacity && !self->keys[first_index].present) {
430 first_index++;
431 }
432 return (ITER){
433 .map = self,
434 .index = first_index,
435 .curr = (KV_PAIR){.key = NULL, .value = NULL},
437 };
438}
#define KV_PAIR
Definition template.h:386
static mutation_version mutation_tracker_get(mutation_tracker const *self)
Here is the call graph for this function:

◆ get_iter_const()

ITER_CONST get_iter_const ( SELF const * self)
static

Definition at line 497 of file template.h.

497 {
498 ASSUME(self);
499 size_t first_index = 0;
500 while (first_index < self->capacity && !self->keys[first_index].present) {
501 first_index++;
502 }
503 return (ITER_CONST){
504 .map = self,
505 .index = first_index,
506 .curr = (KV_PAIR_CONST){.key = NULL, .value = NULL},
507 .version = mutation_tracker_get(&self->iterator_invalidation_tracker),
508 };
509}
#define KV_PAIR_CONST
Definition template.h:457
Here is the call graph for this function:

◆ insert()

VALUE * insert ( SELF * self,
KEY key,
VALUE value )
static

Definition at line 258 of file template.h.

258 {
259 VALUE* value_ptr = NS(SELF, try_insert)(self, key, value);
260 ASSERT(value_ptr);
261 return value_ptr;
262}
static VALUE * try_insert(SELF *self, KEY key, VALUE value)
Definition template.h:196
Here is the call graph for this function:

◆ KEY_CLONE()

KEY KEY_CLONE ( KEY const * key)
static

Definition at line 47 of file template.h.

47{ return *key; }

◆ KEY_DELETE()

void KEY_DELETE ( KEY * key)
static

Definition at line 42 of file template.h.

42{ (void)key; }

◆ KEY_EQ()

bool KEY_EQ ( KEY const * key_1,
KEY const * key_2 )
static

Definition at line 37 of file template.h.

37{ return *key_1 == *key_2; }

◆ KEY_HASH()

size_t KEY_HASH ( KEY const * key)
static

◆ new()

SELF new ( ALLOC * alloc)
static

Definition at line 128 of file template.h.

128 {
130}
static size_t const INITIAL_CAPACITY
Definition utils.h:13
Here is the call graph for this function:

◆ new_with_capacity_for()

SELF new_with_capacity_for ( size_t capacity,
ALLOC * alloc )
static

Definition at line 98 of file template.h.

98 {
99 ASSERT(capacity > 0);
100 size_t const real_capacity = apply_capacity_policy(capacity);
101 ASSERT(real_capacity > 0);
102 // JUSTIFY: calloc of keys
103 // - A cheap way to get all precense flags as zeroed (os & allocater supported get zeroed page)
104 // - for the values, we do not need this (no precense checks are done on values)
105 KEY_ENTRY* keys = (KEY_ENTRY*)NS(ALLOC, calloc)(alloc, sizeof(KEY_ENTRY), real_capacity);
106 VALUE* values = (VALUE*)NS(ALLOC, malloc)(alloc, sizeof(VALUE) * real_capacity);
107 ASSERT(keys && values);
108
109 // JUSTIFY: no access for values & but keys are fine
110 // - Keys are calloced/zeroed as we use this for item lookup, therefore it is valid to read
111 // them.
112 // - Values are only accessed when the corresponding key is present, so we can mark them as
113 // deleted.
115 sizeof(VALUE) * real_capacity);
116
117 return (SELF){
118 .capacity = real_capacity,
119 .items = 0,
120 .keys = keys,
121 .values = values,
122 .alloc = alloc,
123 .derive_c_hashmap = gdb_marker_new(),
124 .iterator_invalidation_tracker = mutation_tracker_new(),
125 };
126}
Here is the call graph for this function:

◆ next() [1/2]

KV_PAIR const * next ( ITER * iter)
static

Definition at line 405 of file template.h.

405 {
406 ASSUME(iter);
407 mutation_version_check(&iter->version);
408 if (iter->index < iter->map->capacity) {
409 iter->curr = (KV_PAIR){.key = &iter->map->keys[iter->index].key,
410 .value = &iter->map->values[iter->index]};
411 iter->index++;
412 while (iter->index < iter->map->capacity && !iter->map->keys[iter->index].present) {
413 iter->index++;
414 }
415 return &iter->curr;
416 }
417 return NULL;
418}
Here is the call graph for this function:

◆ next() [2/2]

KV_PAIR_CONST const * next ( ITER_CONST * iter)
static

Definition at line 476 of file template.h.

476 {
477 ASSUME(iter);
478 mutation_version_check(&iter->version);
479 if (iter->index < iter->map->capacity) {
480 iter->curr = (KV_PAIR_CONST){.key = &iter->map->keys[iter->index].key,
481 .value = &iter->map->values[iter->index]};
482 iter->index++;
483 while (iter->index < iter->map->capacity && !iter->map->keys[iter->index].present) {
484 iter->index++;
485 }
486 return &iter->curr;
487 }
488 return NULL;
489}
Here is the call graph for this function:

◆ read()

VALUE const * read ( SELF const * self,
KEY key )
static

Definition at line 306 of file template.h.

306 {
307 VALUE const* value = NS(SELF, try_read)(self, key);
308 ASSERT(value);
309 return value;
310}
static VALUE const * try_read(SELF const *self, INDEX index)
Definition template.h:228
Here is the call graph for this function:

◆ remove()

VALUE remove ( SELF * self,
KEY key )
static

Definition at line 370 of file template.h.

370 {
371 VALUE value;
372 ASSERT(NS(SELF, try_remove)(self, key, &value));
373 return value;
374}
static bool try_remove(SELF *self, INDEX index, VALUE *destination)
Definition template.h:292
Here is the call graph for this function:

◆ size()

size_t size ( SELF const * self)
static

Definition at line 381 of file template.h.

381 {
382 INVARIANT_CHECK(self);
383 return self->items;
384}
Here is the call graph for this function:

◆ TRAIT_MAP()

TRAIT_MAP ( SELF )

◆ try_insert()

VALUE * try_insert ( SELF * self,
KEY key,
VALUE value )
static

Definition at line 196 of file template.h.

196 {
197 INVARIANT_CHECK(self);
199 if (apply_capacity_policy(self->items) > self->capacity / 2) {
200 NS(SELF, extend_capacity_for)(self, self->items * 2);
201 }
202
203 uint16_t distance_from_desired = 0;
204 size_t const hash = KEY_HASH(&key);
205 size_t index = modulus_power_of_2_capacity(hash, self->capacity);
206
207 VALUE* inserted_to_entry = NULL;
208
209 for (;;) {
210 KEY_ENTRY* entry = &self->keys[index];
211 ASSUME(distance_from_desired < self->capacity);
212
213 if (entry->present) {
214 if (KEY_EQ(&entry->key, &key)) {
215 return NULL;
216 }
217
218 if (entry->distance_from_desired < distance_from_desired) {
219 KEY const switch_key = entry->key;
220 uint16_t const switch_distance_from_desired = entry->distance_from_desired;
221 VALUE const switch_value = self->values[index];
222
223 entry->key = key;
224 entry->distance_from_desired = distance_from_desired;
225 self->values[index] = value;
226
227 key = switch_key;
228 distance_from_desired = switch_distance_from_desired;
229 value = switch_value;
230
231 if (!inserted_to_entry) {
232 inserted_to_entry = &self->values[index];
233 }
234 }
235
236 distance_from_desired++;
237 index = modulus_power_of_2_capacity(index + 1, self->capacity);
238 } else {
239 entry->present = true;
240 entry->distance_from_desired = distance_from_desired;
241 entry->key = key;
242
244 &self->values[index], sizeof(VALUE));
245
246 self->values[index] = value;
247
248 if (!inserted_to_entry) {
249 inserted_to_entry = &self->values[index];
250 }
251
252 self->items++;
253 return inserted_to_entry;
254 }
255 }
256}
#define KEY_HASH
Definition template.h:31
#define KEY
A simple open-addressed hashmap using robin-hood hashing.
Definition template.h:22
#define KEY_EQ
Definition template.h:36
static void extend_capacity_for(SELF *self, size_t expected_items)
Definition template.h:175
static size_t INLINE CONST modulus_power_of_2_capacity(size_t index, size_t capacity)
Definition math.h:25
@ MEMORY_TRACKER_CAP_WRITE
size_t items
Definition template.h:83
Here is the call graph for this function:
Here is the caller graph for this function:

◆ try_read()

VALUE const * try_read ( SELF const * self,
KEY key )
static

Definition at line 288 of file template.h.

288 {
289 INVARIANT_CHECK(self);
290 size_t const hash = KEY_HASH(&key);
291 size_t index = modulus_power_of_2_capacity(hash, self->capacity);
292
293 for (;;) {
294 KEY_ENTRY* entry = &self->keys[index];
295 if (entry->present) {
296 if (KEY_EQ(&entry->key, &key)) {
297 return &self->values[index];
298 }
299 index = modulus_power_of_2_capacity(index + 1, self->capacity);
300 } else {
301 return NULL;
302 }
303 }
304}
Here is the call graph for this function:

◆ try_remove()

bool try_remove ( SELF * self,
KEY key,
VALUE * destination )
static

Definition at line 312 of file template.h.

312 {
313 INVARIANT_CHECK(self);
315 size_t const hash = KEY_HASH(&key);
316 size_t index = modulus_power_of_2_capacity(hash, self->capacity);
317
318 for (;;) {
319 KEY_ENTRY* entry = &self->keys[index];
320 if (entry->present) {
321 if (KEY_EQ(&entry->key, &key)) {
322 self->items--;
323
324 *destination = self->values[index];
325 KEY_DELETE(&entry->key);
326
327 // NOTE: For robin hood hashing, we need probe chains to be unbroken
328 // - Need to find the entries that might use this chain (
329 // distance_to_desired > 0 until the next not-present or
330 // distance_to_desired=0 slot)
331 // hence we shift entries the left (being careful with modulus index)
332
333 size_t free_index = index;
334 KEY_ENTRY* free_entry = entry;
335
336 size_t check_index = modulus_power_of_2_capacity(free_index + 1, self->capacity);
337 KEY_ENTRY* check_entry = &self->keys[check_index];
338
339 while (check_entry->present && (check_entry->distance_from_desired > 0)) {
340 free_entry->key = check_entry->key;
341 free_entry->distance_from_desired = check_entry->distance_from_desired - 1;
342 self->values[free_index] = self->values[check_index];
343
344 free_index = check_index;
345 free_entry = check_entry;
346
347 check_index = modulus_power_of_2_capacity(free_index + 1, self->capacity);
348 check_entry = &self->keys[check_index];
349 }
350
351 // JUSTIFY: Only setting free entry to false
352 // - We remove, then shift down an index
353 // - The removed entry already has the flag set
354 // - the free entry was the last one removed/moved down an index, so it
355 // should be false.
356
357 free_entry->present = false;
359 &self->values[free_index], sizeof(VALUE));
360
361 return true;
362 }
363 index = modulus_power_of_2_capacity(index + 1, self->capacity);
364 } else {
365 return false;
366 }
367 }
368}
Here is the call graph for this function:

◆ try_write()

VALUE * try_write ( SELF * self,
KEY key )
static

Definition at line 264 of file template.h.

264 {
265 INVARIANT_CHECK(self);
266 size_t const hash = KEY_HASH(&key);
267 size_t index = modulus_power_of_2_capacity(hash, self->capacity);
268
269 for (;;) {
270 KEY_ENTRY* entry = &self->keys[index];
271 if (entry->present) {
272 if (KEY_EQ(&entry->key, &key)) {
273 return &self->values[index];
274 }
275 index = modulus_power_of_2_capacity(index + 1, self->capacity);
276 } else {
277 return NULL;
278 }
279 }
280}
Here is the call graph for this function:

◆ VALUE_CLONE()

VALUE VALUE_CLONE ( VALUE const * value)
static

Definition at line 67 of file template.h.

67{ return *value; }

◆ VALUE_DELETE()

void VALUE_DELETE ( VALUE * value)
static

Definition at line 62 of file template.h.

62{ (void)value; }

◆ write()

VALUE * write ( SELF * self,
KEY key )
static

Definition at line 282 of file template.h.

282 {
283 VALUE* value = NS(SELF, try_write)(self, key);
284 ASSERT(value);
285 return value;
286}
static VALUE * try_write(SELF *self, INDEX index)
Definition template.h:210
Here is the call graph for this function: