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