PatchworkOS  966e257
A non-POSIX operating system.
Loading...
Searching...
No Matches
map.c
Go to the documentation of this file.
1#include <kernel/utils/map.h>
2
3#include <errno.h>
4#include <stdlib.h>
5#include <string.h>
6#include <sys/math.h>
7
9{
10 return n > 0 && (n & (n - 1)) == 0;
11}
12
14{
15 if (n <= 1)
16 {
17 return 2;
18 }
19
20 if ((n > 0 && (n & (n - 1)) == 0))
21 {
22 return n;
23 }
24
25 n--;
26 n |= n >> 1;
27 n |= n >> 2;
28 n |= n >> 4;
29 n |= n >> 8;
30 n |= n >> 16;
31 n |= n >> 32;
32 n++;
33
34 if (n == 0)
35 {
36 return (UINT64_MAX >> 1) + 1;
37 }
38
39 return n;
40}
41
42uint64_t hash_object(const void* object, uint64_t length)
43{
44 uint64_t hash = 0xcbf29ce484222325ULL;
45 const uint64_t prime = 0x100000001b3ULL;
46
47 for (uint64_t i = 0; i < length; ++i)
48 {
49 hash ^= (uint64_t)((uint8_t*)object)[i];
50 hash *= prime;
51 }
52
53 return hash;
54}
55
56bool map_key_is_equal(const map_key_t* a, const map_key_t* b)
57{
58 if (a->hash != b->hash)
59 {
60 return false;
61 }
62
63 if (a->len != b->len)
64 {
65 return false;
66 }
67
68 return memcmp(a->key, b->key, a->len) == 0;
69}
70
72{
73 memset(entry->key.key, 0, sizeof(entry->key.key));
74 entry->key.len = 0;
75 entry->key.hash = 0;
76 entry->index = ERR;
77}
78
79static uint64_t map_find_slot(const map_t* map, const map_key_t* key, bool forInsertion)
80{
81 uint64_t index = (uint64_t)(key->hash & (map->capacity - 1));
82 uint64_t firstTombstone = ERR;
83
84 for (uint64_t i = 0; i < map->capacity; i++)
85 {
86 uint64_t currentIndex = (index + i) & (map->capacity - 1);
87 map_entry_t* entry = map->entries[currentIndex];
88
89 if (entry == NULL)
90 {
91 if (forInsertion && firstTombstone != ERR)
92 {
93 return firstTombstone;
94 }
95 return currentIndex;
96 }
97
98 if (entry == MAP_TOMBSTONE)
99 {
100 if (forInsertion && firstTombstone == ERR)
101 {
102 firstTombstone = currentIndex;
103 }
104 continue;
105 }
106
107 if (map_key_is_equal(&entry->key, key))
108 {
109 return currentIndex;
110 }
111 }
112
113 if (forInsertion && firstTombstone != ERR)
114 {
115 return firstTombstone;
116 }
117
118 return ERR;
119}
120
121static uint64_t map_resize(map_t* map, uint64_t newCapacity)
122{
123 if (!is_power_of_two(newCapacity))
124 {
125 newCapacity = next_power_of_two(newCapacity);
126 }
127
128 if (newCapacity <= map->capacity)
129 {
130 return 0;
131 }
132
133 map_entry_t** newEntries = calloc(newCapacity, sizeof(map_entry_t*));
134 if (newEntries == NULL)
135 {
136 return ERR;
137 }
138
139 map_entry_t** oldEntries = map->entries;
140 uint64_t oldCapacity = map->capacity;
141 uint64_t oldLength = map->length;
142 uint64_t oldTombstones = map->tombstones;
143
144 map->entries = newEntries;
145 map->capacity = newCapacity;
146 map->length = 0;
147 map->tombstones = 0;
148
149 for (uint64_t i = 0; i < oldCapacity; i++)
150 {
151 map_entry_t* entry = oldEntries[i];
152 if (entry != NULL && entry != MAP_TOMBSTONE)
153 {
154 uint64_t newIndex = map_find_slot(map, &entry->key, true);
155 if (newIndex == ERR)
156 {
157 free(newEntries);
158 map->entries = oldEntries;
159 map->capacity = oldCapacity;
160 map->length = oldLength;
161 map->tombstones = oldTombstones;
162 return ERR;
163 }
164
165 entry->index = newIndex;
166 entry->map = map;
167 map->entries[newIndex] = entry;
168 map->length++;
169 }
170 }
171
172 free(oldEntries);
173 return 0;
174}
175
177{
178 map->length = 0;
179 map->capacity = 0;
180 map->tombstones = 0;
181 map->entries = NULL;
182}
183
185{
186 free(map->entries);
187 map->entries = NULL;
188 map->length = 0;
189 map->capacity = 0;
190 map->tombstones = 0;
191}
192
194{
195 uint64_t currentEntries = map->length + map->tombstones;
196 if (currentEntries * 100 >= MAP_MAX_LOAD_PERCENTAGE * map->capacity)
197 {
198 uint64_t newCapacity = (map->capacity == 0) ? MAP_MIN_CAPACITY : (map->capacity * 2);
199 return map_resize(map, newCapacity);
200 }
201 return 0;
202}
203
205{
206 if (entry == NULL)
207 {
208 errno = EINVAL;
209 return ERR;
210 }
211
212 if (map_resize_check(map) == ERR)
213 {
214 errno = ENOMEM;
215 return ERR;
216 }
217
218 uint64_t index = map_find_slot(map, key, true);
219 if (index == ERR)
220 {
221 errno = ENOMEM;
222 return ERR;
223 }
224
225 if (map->entries[index] != NULL && map->entries[index] != MAP_TOMBSTONE &&
226 map_key_is_equal(&map->entries[index]->key, key))
227 {
228 errno = EEXIST;
229 return ERR;
230 }
231
232 if (map->entries[index] == MAP_TOMBSTONE)
233 {
234 map->tombstones--;
235 }
236
237 entry->key = *key;
238 entry->index = index;
239 entry->map = map;
240
241 map->entries[index] = entry;
242 map->length++;
243
244 return 0;
245}
246
248{
249 if (entry == NULL)
250 {
251 errno = EINVAL;
252 return ERR;
253 }
254
255 if (map_resize_check(map) == ERR)
256 {
257 errno = ENOMEM;
258 return ERR;
259 }
260
261 uint64_t index = map_find_slot(map, key, true);
262 if (index == ERR)
263 {
264 errno = ENOMEM;
265 return ERR;
266 }
267
268 if (map->entries[index] == MAP_TOMBSTONE)
269 {
270 map->tombstones--;
271 map->length++;
272 }
273 else if (map->entries[index] == NULL)
274 {
275 map->length++;
276 }
277
278 entry->key = *key;
279 entry->index = index;
280 entry->map = map;
281
282 map->entries[index] = entry;
283
284 return 0;
285}
286
288{
289 uint64_t index = map_find_slot(map, key, false);
290 if (index == ERR)
291 {
292 return NULL;
293 }
294
295 map_entry_t* entry = map->entries[index];
296 if (entry == NULL || entry == MAP_TOMBSTONE)
297 {
298 return NULL;
299 }
300
301 if (map_key_is_equal(&entry->key, key))
302 {
303 return entry;
304 }
305
306 return NULL;
307}
308
309static void map_remove_index(map_t* map, uint64_t index)
310{
311 map_entry_t* entry = map->entries[index];
312 if (entry == NULL || entry == MAP_TOMBSTONE)
313 {
314 return;
315 }
316
317 map->entries[index] = MAP_TOMBSTONE;
318 map->length--;
319 map->tombstones++;
320
321 if (map->capacity > MAP_MIN_CAPACITY && map->length * 100 < MAP_MIN_LOAD_PERCENTAGE * map->capacity)
322 {
323 uint64_t newCapacity = map->capacity / 2;
324 if (newCapacity < MAP_MIN_CAPACITY)
325 {
326 newCapacity = MAP_MIN_CAPACITY;
327 }
328 map_resize(map, newCapacity); // If we fail here, we can just ignore it
329 }
330
331 entry->index = ERR;
332 entry->map = NULL;
333}
334
336{
337 uint64_t index = map_find_slot(map, key, false);
338 if (index == ERR)
339 {
340 return NULL;
341 }
342
343 map_entry_t* entry = map->entries[index];
344 if (entry == NULL || entry == MAP_TOMBSTONE || !map_key_is_equal(&entry->key, key))
345 {
346 return NULL;
347 }
348
349 map_remove_index(map, index);
350 return entry;
351}
352
354{
355 if (entry == NULL || entry->index == ERR)
356 {
357 return;
358 }
359 assert(entry->map == map);
360
361 map_remove_index(map, entry->index);
362}
363
365{
366 uint64_t index = map_find_slot(map, key, false);
367 if (index == ERR)
368 {
369 return;
370 }
371
372 map_entry_t* entry = map->entries[index];
373 if (entry == NULL || entry == MAP_TOMBSTONE || !map_key_is_equal(&entry->key, key))
374 {
375 return;
376 }
377
378 map_remove_index(map, index);
379}
380
382{
383 return map->length;
384}
385
387{
388 return map->capacity;
389}
390
392{
393 return map->length == 0;
394}
395
396bool map_contains(map_t* map, const map_key_t* key)
397{
398 return map_get(map, key) != NULL;
399}
400
402{
403 for (uint64_t i = 0; i < map->capacity; i++)
404 {
405 map_entry_t* entry = map->entries[i];
406 if (entry != NULL && entry != MAP_TOMBSTONE)
407 {
408 entry->index = ERR;
409 entry->map = NULL;
410 }
411 map->entries[i] = NULL;
412 }
413
414 map->length = 0;
415 map->tombstones = 0;
416}
417
419{
420 if (minCapacity < map->length)
421 {
422 return 0;
423 }
424
425 uint64_t newCapacity = next_power_of_two(minCapacity);
426 if (map_resize(map, newCapacity) == ERR)
427 {
428 errno = ENOMEM;
429 return ERR;
430 }
431 return 0;
432}
#define assert(expression)
Definition assert.h:29
uint64_t hash_object(const void *object, uint64_t length)
Hash a object.
Definition map.c:42
void map_init(map_t *map)
Initialize a map.
Definition map.c:176
uint64_t map_capacity(const map_t *map)
Get the capacity of the map.
Definition map.c:386
bool map_contains(map_t *map, const map_key_t *key)
Check if the map contains a key.
Definition map.c:396
void map_clear(map_t *map)
Clear all entries from the map.
Definition map.c:401
void map_deinit(map_t *map)
Deinitialize a map.
Definition map.c:184
#define MAP_MIN_CAPACITY
The minimum capacity of a map.
Definition map.h:26
uint64_t map_replace(map_t *map, const map_key_t *key, map_entry_t *entry)
Replace a key-value pair in the map.
Definition map.c:247
void map_entry_init(map_entry_t *entry)
Initialize a map entry.
Definition map.c:71
#define MAP_MAX_LOAD_PERCENTAGE
The maximum load percentage of a map before it resizes.
Definition map.h:31
#define MAP_TOMBSTONE
The value used to indicate a tombstone (removed entry).
Definition map.h:41
uint64_t map_size(const map_t *map)
Get the number of entries in the map.
Definition map.c:381
uint64_t map_insert(map_t *map, const map_key_t *key, map_entry_t *entry)
Insert a key-value pair into the map.
Definition map.c:204
map_entry_t * map_get_and_remove(map_t *map, const map_key_t *key)
Get and remove a key-value pair from the map.
Definition map.c:335
void map_remove(map_t *map, map_entry_t *entry)
Remove a entry from the map.
Definition map.c:353
void map_remove_key(map_t *map, const map_key_t *key)
Remove a key-value pair from the map by key.
Definition map.c:364
bool map_is_empty(const map_t *map)
Check if the map is empty.
Definition map.c:391
#define MAP_MIN_LOAD_PERCENTAGE
The minimum load percentage of a map before it resizes down.
Definition map.h:36
map_entry_t * map_get(map_t *map, const map_key_t *key)
Get a value from the map by key.
Definition map.c:287
uint64_t map_reserve(map_t *map, uint64_t minCapacity)
Reserve space in the map for at least minCapacity entries.
Definition map.c:418
#define EEXIST
File exists.
Definition errno.h:117
#define EINVAL
Invalid argument.
Definition errno.h:142
#define ENOMEM
Out of memory.
Definition errno.h:92
#define errno
Error number variable.
Definition errno.h:27
#define NULL
Pointer error value.
Definition NULL.h:23
#define ERR
Integer error value.
Definition ERR.h:17
static uint64_t next_power_of_two(uint64_t n)
Definition map.c:13
static uint64_t map_resize(map_t *map, uint64_t newCapacity)
Definition map.c:121
static void map_remove_index(map_t *map, uint64_t index)
Definition map.c:309
static uint64_t map_find_slot(const map_t *map, const map_key_t *key, bool forInsertion)
Definition map.c:79
static bool is_power_of_two(uint64_t n)
Definition map.c:8
bool map_key_is_equal(const map_key_t *a, const map_key_t *b)
Definition map.c:56
static uint64_t map_resize_check(map_t *map)
Definition map.c:193
boot_memory_map_t * map
Definition mem.c:19
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
#define UINT64_MAX
Definition stdint.h:74
__UINT8_TYPE__ uint8_t
Definition stdint.h:11
_PUBLIC void * calloc(size_t nmemb, size_t size)
Definition calloc.c:6
_PUBLIC void free(void *ptr)
Definition free.c:11
_PUBLIC int memcmp(const void *s1, const void *s2, size_t n)
Definition memcmp.c:3
_PUBLIC void * memset(void *s, int c, size_t n)
Definition memset.c:4
uint64_t length
Definition boot_info.h:58
Map entry structure.
Definition map.h:68
uint64_t index
Definition map.h:70
map_t * map
Definition map.h:71
map_key_t key
Definition map.h:69
Map key stucture.
Definition map.h:56
uint8_t key[MAP_KEY_MAX_LENGTH]
Definition map.h:57
uint8_t len
Definition map.h:58
uint64_t hash
Definition map.h:59
Hash map structure.
Definition map.h:89