Derive-C
Loading...
Searching...
No Matches
template.h File Reference
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <derive-c/core/helpers.h>
#include <derive-c/core/panic.h>
#include <derive-c/structures/hashmap/utils.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/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  key_t
 A simple open-addressed hashmap using robin-hood hashing. More...
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   key_t
#define KEY_DELETE   key_delete
#define KEY_CLONE   key_clone
#define KEY_EQ   key_eq
#define KEY_HASH   key_hash
#define VALUE   value_t
#define VALUE_DELETE   value_delete
#define VALUE_CLONE   value_clone
#define KEY_ENTRY   NS(SELF, key_entry)
#define KV_PAIR   NS(SELF, kv)
#define KV_PAIR_CONST   NS(SELF, kv_const)

Functions

static void key_delete (key_t *UNUSED(self))
static key_t key_clone (key_t const *self)
static bool key_eq (key_t const *key_1, key_t const *key_2)
static size_t key_hash (key_t const *key)
static void value_delete (value_t *UNUSED(self))
static value_t value_clone (value_t const *self)
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 KV_PAIR const * next (ITER *iter)
static bool empty (ITER const *iter)
static ITER get_iter (SELF *self)
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)

Macro Definition Documentation

◆ KEY

#define KEY   key_t

Definition at line 22 of file template.h.

◆ KEY_CLONE

#define KEY_CLONE   key_clone

Definition at line 26 of file template.h.

◆ KEY_DELETE

#define KEY_DELETE   key_delete

Definition at line 24 of file template.h.

◆ KEY_ENTRY

#define KEY_ENTRY   NS(SELF, key_entry)

Definition at line 73 of file template.h.

◆ KEY_EQ

#define KEY_EQ   key_eq

Definition at line 28 of file template.h.

◆ KEY_HASH

#define KEY_HASH   key_hash

Definition at line 36 of file template.h.

◆ KV_PAIR

#define KV_PAIR   NS(SELF, kv)

Definition at line 347 of file template.h.

◆ KV_PAIR_CONST

#define KV_PAIR_CONST   NS(SELF, kv_const)

Definition at line 412 of file template.h.

◆ VALUE

#define VALUE   value_t

Definition at line 58 of file template.h.

◆ VALUE_CLONE

#define VALUE_CLONE   value_clone

Definition at line 62 of file template.h.

◆ VALUE_DELETE

#define VALUE_DELETE   value_delete

Definition at line 60 of file template.h.

Function Documentation

◆ clone()

SELF clone ( SELF const * self)
static

Definition at line 114 of file template.h.

114 {
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}
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
#define NS(pre, post)
Definition helpers.h:6
#define ASSERT(expr,...)
Definition panic.h:15
#define DEBUG_ASSERT(expr)
Definition panic.h:34
#define SELF
Definition def.h:51
KEY key
Definition template.h:77
uint16_t distance_from_desired
Definition template.h:76
#define VALUE
Definition template.h:29
#define VALUE_CLONE
Definition template.h:33
#define KEY_ENTRY
Definition template.h:73
#define KEY_CLONE
Definition template.h:26
Here is the call graph for this function:

◆ delete()

void delete ( SELF * self)
static

Definition at line 394 of file template.h.

394 {
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}
static void free(SELF *self, void *ptr)
Definition template.h:56
bool present
Definition template.h:75
size_t capacity
Definition template.h:97
VALUE * values
Definition template.h:86
ALLOC * alloc
Definition template.h:63
KEY_ENTRY * keys
Definition template.h:85
static ITER get_iter(SELF *self)
Definition template.h:318
#define VALUE_DELETE
Definition template.h:31
#define KEY_DELETE
Definition template.h:24
Here is the call graph for this function:

◆ delete_entry()

void delete_entry ( SELF * self,
KEY key )
static

Definition at line 337 of file template.h.

337 {
338 VALUE value = NS(SELF, remove)(self, key);
339 VALUE_DELETE(&value);
340}
static VALUE remove(SELF *self, INDEX index)
Definition template.h:256
Here is the call graph for this function:

◆ empty() [1/2]

bool empty ( ITER const * iter)
static

Definition at line 376 of file template.h.

376 {
377 DEBUG_ASSERT(iter);
378 return iter->index >= iter->map->capacity;
379}
Here is the call graph for this function:

◆ empty() [2/2]

bool empty ( ITER_CONST const * iter)
static

Definition at line 441 of file template.h.

441 {
442 DEBUG_ASSERT(iter);
443 return iter->index >= iter->map->capacity;
444}
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 152 of file template.h.

152 {
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}
static size_t apply_capacity_policy(size_t capacity)
Definition utils.h:8
static INDEX insert(SELF *self, VALUE value)
Definition template.h:129
static SELF new_with_capacity_for(INDEX_TYPE items, ALLOC *alloc)
Definition template.h:113
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 381 of file template.h.

381 {
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}
#define KV_PAIR
Definition template.h:347
Here is the call graph for this function:

◆ get_iter_const()

ITER_CONST get_iter_const ( SELF const * self)
static

Definition at line 446 of file template.h.

446 {
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}
#define KV_PAIR_CONST
Definition template.h:412
Here is the call graph for this function:

◆ insert()

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

Definition at line 222 of file template.h.

222 {
223 VALUE* value_ptr = NS(SELF, try_insert)(self, key, value);
224 ASSERT(value_ptr);
225 return value_ptr;
226}
static VALUE * try_insert(SELF *self, KEY key, VALUE value)
Definition template.h:169
Here is the call graph for this function:

◆ key_clone()

key_t key_clone ( key_t const * self)
static

Definition at line 25 of file template.h.

25{ return *self; }

◆ key_delete()

void key_delete ( key_t * UNUSEDself)
static

Definition at line 23 of file template.h.

23{}

◆ key_eq()

bool key_eq ( key_t const * key_1,
key_t const * key_2 )
static

Definition at line 27 of file template.h.

27{ return key_1->x == key_2->x; }

◆ key_hash()

size_t key_hash ( key_t const * key)
static

◆ new()

SELF new ( ALLOC * alloc)
static

Definition at line 110 of file template.h.

110 {
112}
static size_t const INITIAL_CAPACITY
Definition utils.h:14
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 91 of file template.h.

91 {
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}
Here is the call graph for this function:

◆ next() [1/2]

KV_PAIR const * next ( ITER * iter)
static

Definition at line 362 of file template.h.

362 {
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}
Here is the call graph for this function:

◆ next() [2/2]

KV_PAIR_CONST const * next ( ITER_CONST * iter)
static

Definition at line 427 of file template.h.

427 {
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}
Here is the call graph for this function:

◆ read()

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

Definition at line 270 of file template.h.

270 {
271 VALUE const* value = NS(SELF, try_read)(self, key);
272 ASSERT(value);
273 return value;
274}
static VALUE const * try_read(SELF const *self, INDEX index)
Definition template.h:177
Here is the call graph for this function:

◆ remove()

VALUE remove ( SELF * self,
KEY key )
static

Definition at line 331 of file template.h.

331 {
332 VALUE value;
333 ASSERT(NS(SELF, try_remove)(self, key, &value));
334 return value;
335}
static bool try_remove(SELF *self, INDEX index, VALUE *destination)
Definition template.h:238
Here is the call graph for this function:

◆ size()

size_t size ( SELF const * self)
static

Definition at line 342 of file template.h.

342 {
343 DEBUG_ASSERT(self);
344 return self->items;
345}
Here is the call graph for this function:

◆ try_insert()

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

Definition at line 169 of file template.h.

169 {
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}
static size_t modulus_power_of_2_capacity(size_t index, size_t capacity)
Definition helpers.h:48
size_t items
Definition template.h:82
#define KEY_HASH
Definition template.h:36
#define KEY
Definition template.h:22
#define KEY_EQ
Definition template.h:28
static void extend_capacity_for(SELF *self, size_t expected_items)
Definition template.h:152
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 252 of file template.h.

252 {
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}
Here is the call graph for this function:

◆ try_remove()

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

Definition at line 276 of file template.h.

276 {
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}
Here is the call graph for this function:

◆ try_write()

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

Definition at line 228 of file template.h.

228 {
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}
Here is the call graph for this function:

◆ value_clone()

value_t value_clone ( value_t const * self)
static

Definition at line 61 of file template.h.

61{ return *self; }

◆ value_delete()

void value_delete ( value_t * UNUSEDself)
static

Definition at line 59 of file template.h.

59{}

◆ write()

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

Definition at line 246 of file template.h.

246 {
247 VALUE* value = NS(SELF, try_write)(self, key);
248 ASSERT(value);
249 return value;
250}
static VALUE * try_write(SELF *self, INDEX index)
Definition template.h:159
Here is the call graph for this function: