Subastra
Loading...
Searching...
No Matches
map.h
Go to the documentation of this file.
1#ifndef __H__MAP__
2#define __H__MAP__
3
4#include <string.h>
5
6#include "defs.h"
7#include "memory.h"
8
12#define MAP_LOAD_FACTOR 75
16#define MAP_MAX_CAPACITY (1 << 30)
17
18typedef u32 map_key_t;
19
20typedef map_key_t (*hash_function_f)(void *, sz);
21
27
45
48static map_bucket_t *map__get_bucket_at(map_t *map, sz idx) {
49 sz block_size = sizeof(map_bucket_t) + map->entry_size;
50 return (map_bucket_t *)(((char *)map->buckets) + idx * block_size);
51}
52
56 sz entry_size, sz capacity) {
57 map->allocator = allocator;
58 map->entry_size = entry_size;
59 map->size = 0;
60 map->capacity = capacity;
61
62 sz block_size = sizeof(map_bucket_t) + entry_size;
63 sz size = map->capacity * block_size;
64 map->buckets = allocator_alloc(allocator, size);
65
66 for (sz i = 0; i < map->capacity; i++) {
67 map_bucket_t *b = map__get_bucket_at(map, i);
68 b->next = 0;
69 b->has_data = false;
70 }
71}
72
76static void map_init(map_t *map, allocator_t allocator, sz entry_size) {
77 map_init_with_capacity(map, allocator, entry_size, MAP_LOAD_FACTOR / 25 + 1);
78}
79
83#define map_init_ty(ty, map, allocator) map_init(map, allocator, sizeof(ty))
84
89static void map_cleanup(map_t *map) {
90 for (sz i = 0; i < map->capacity; i++) {
91 map_bucket_t *b = map__get_bucket_at(map, i)->next;
92 while (b) {
93 map_bucket_t *next = b->next;
94 allocator_free(map->allocator, b);
95 b = next;
96 }
97 }
98
99 allocator_free(map->allocator, map->buckets);
100}
101
103static bool map_should_grow(const map_t *map) {
104 return map->capacity != MAP_MAX_CAPACITY &&
105 (100 * map->size) / map->capacity >= MAP_LOAD_FACTOR;
106}
107
112 sz capacity = map->capacity;
113 while (capacity * MAP_LOAD_FACTOR / 100 <= map->size) {
114 capacity <<= 1;
115 if (capacity >= MAP_MAX_CAPACITY)
116 return MAP_MAX_CAPACITY;
117 }
118
119 return capacity;
120}
121
122static void map_insert(map_t *map, map_key_t key, void *data, sz size);
123
127static void map_grow(map_t *map) {
128 if (!map_should_grow(map))
129 return;
130
131 sz new_capacity = map_calculate_next_capacity(map);
132
133 map_t new_map;
134 map_init_with_capacity(&new_map, map->allocator, map->entry_size, new_capacity);
135
136 for (sz i = 0; i < map->capacity; i++) {
137 map_bucket_t *bucket = map__get_bucket_at(map, i);
138 if (!bucket->has_data)
139 return;
140
141 for (map_bucket_t *current = bucket; current; current = current->next)
142 map_insert(&new_map, current->key, current + 1, map->entry_size);
143 }
144
145 map_cleanup(map);
146 *map = new_map;
147}
148
153static void map_insert(map_t *map, map_key_t key, void *data, sz size) {
154 ASSERT(size == map->entry_size,
155 "The size of an inserted entry (%d) must match the entry size defined "
156 "at compile time (%d)",
157 (int)size, (int)map->entry_size);
158
159 if (map_should_grow(map))
160 map_grow(map);
161
162 sz idx = key % map->capacity;
163 map_bucket_t *head = map__get_bucket_at(map, idx);
164
165 if (!head->has_data) {
166 head->has_data = true;
167 head->key = key;
168 head->next = 0;
169 memcpy(head + 1, data, size);
170 map->size++;
171 return;
172 }
173
174 map_bucket_t *current = head;
175 while (true) {
176 if (current->has_data && current->key == key) {
177 memcpy(current + 1, data, size);
178 return;
179 }
180
181 if (!current->next)
182 break;
183 current = current->next;
184 }
185
186 map_bucket_t *new_node = (map_bucket_t *)allocator_alloc(
188 new_node->has_data = true;
189 new_node->key = key;
190 new_node->next = 0;
191 memcpy(new_node + 1, data, size);
192 current->next = new_node;
193 map->size++;
194}
195
196#define map_insert_ty(ty, map, key, data) map_insert(map, key, data, sizeof(ty))
197
201static void *map_get(map_t *map, map_key_t key) {
202 sz idx = key % map->capacity;
203
204 map_bucket_t *b = map__get_bucket_at(map, idx);
205 if (!b->has_data)
206 return 0;
207
208 for (; b; b = b->next) {
209 if (b->key == key)
210 return b + 1;
211 }
212
213 return 0;
214}
215
218#define map_get_ty(ty, map, key) (ty *)map_get(map, key)
219
220#endif
#define ASSERT(expr, msg,...)
Definition defs.h:35
size_t sz
Definition defs.h:51
uint32_t u32
Definition defs.h:45
static map_t map
Definition map.c:6
static void map_insert(map_t *map, map_key_t key, void *data, sz size)
map_key_t(* hash_function_f)(void *, sz)
Definition map.h:20
u32 map_key_t
Definition map.h:18
static sz map_calculate_next_capacity(const map_t *map)
Calculates the next capacity for the map. May return the same capacity if the MAP_LOAD_FACTOR is not ...
Definition map.h:111
static bool map_should_grow(const map_t *map)
Calculates if the map reached its MAP_LOAD_FACTOR. O(1)
Definition map.h:103
A generic allocator type passed by value. Contains a fallback allocator and a set of function pointer...
Definition memory.h:30
Definition map.h:22
bool has_data
Definition map.h:23
map_key_t key
Definition map.h:24
struct map_bucket_t * next
Definition map.h:25
Definition map.h:28
allocator_t allocator
The allocator used for all memory management, used for allocating both the main and the secondary sto...
Definition map.h:32
static void map_init(map_t *map, allocator_t allocator, sz entry_size)
Initializes the map with a fixed size and some initial capacity. O(1)
Definition map.h:76
static void map_init_with_capacity(map_t *map, allocator_t allocator, sz entry_size, sz capacity)
Initialize the map with a specific capacity and entry size. O(1)
Definition map.h:55
static void map_insert(map_t *map, map_key_t key, void *data, sz size)
Insert an element of the size size into the map. Does an additional check to verify that the size of ...
Definition map.h:153
sz size
The amount of elements currently in the map.
Definition map.h:38
sz capacity
The amount of buckets available.
Definition map.h:40
void * buckets
The raw byte storage used for the buckets.
Definition map.h:43
static void * map_get(map_t *map, map_key_t key)
Retrieves the element of the map at key map_key. Returns NULL if no element found....
Definition map.h:201
static void map_cleanup(map_t *map)
Cleanup the memory used by the map itself. Importantly, does not clean the internal types stored insi...
Definition map.h:89
static void map_grow(map_t *map)
Tries to grow the map. Doubles the size and reallocates all buckets. Will not grow if the MAP_LOAD_FA...
Definition map.h:127
sz entry_size
The size of a single entry in bytes. Serves as a guardrail for runtime checking.
Definition map.h:36