libpulsar
A modular compiler for the pulsar programming language
Loading...
Searching...
No Matches
hashmap.c
Go to the documentation of this file.
1// Copyright (C) 2023 Ethan Uppal. All rights reserved.
2
3#include <stdio.h>
4#include <stdlib.h> // malloc, free
5#include "hashmap.h"
6#include "util/abort.h"
7#include "util/mtrack.h"
8
9#define PS_HASH_MAP_INITIAL_CAPACITY 16
10
12void ps_hash_map_assert_inv(const struct ps_hash_map* map) {
13 usize calc_count = 0;
14 for (usize i = 0; i < map->length; i++) {
15 const struct ps_hash_map_entry* entry = &map->entries[i];
16 if (entry->key == NULL) {
17 continue;
18 }
19
20 const usize hash_idx = map->hasher(entry->key) % map->length;
21
22 usize local_count = 1;
23 while (entry->next) {
24 local_count++;
25 entry = entry->next;
26 if (entry) {
27 ps_assert(entry->key != NULL,
28 "hash map: only the first entry can have a NULL key");
29 ps_assert(map->hasher(entry->key) % map->length == hash_idx,
30 "hash map: all entries in a bucket should have the same "
31 "hash index");
32 }
33 }
34 calc_count += local_count;
35 }
36
37 ps_assert(map->count == calc_count, "hash map: invalid count");
38}
39
48
50 free_fn_t free_fn, u64 capacity) {
51 struct ps_hash_map* map = malloc(sizeof(*map)
52 + sizeof(*map->entries) * capacity);
53 CHECK(if (!map) {
55 return NULL;
56 })
57
58 map->is_equal = is_equal;
59 map->hasher = hasher;
60 map->free_fn = free_fn;
61
62 map->length = capacity;
63 map->count = 0;
64
65 for (usize i = 0; i < map->length; i++) {
66 map->entries[i].key = NULL;
67 map->entries[i].value = NULL;
68 map->entries[i].next = NULL;
69 }
70
72 return map;
73}
74
78void ps_hash_map_free(struct ps_hash_map* map) {
79 for (usize i = 0; i < map->length; i++) {
80 struct ps_hash_map_entry* entry = &map->entries[i];
81 while (entry) {
82 struct ps_hash_map_entry* next = entry->next;
83 if (map->free_fn) {
84 map->free_fn(entry->value);
85 }
86 entry = next;
87 free(entry); // should not free first entry
88 }
89 }
90
91 free(map);
92}
93
94void ps_hash_map_redistribute(struct ps_hash_map** map_ptr) {
95 struct ps_hash_map* map = *map_ptr;
96 struct ps_hash_map* new_map = _ps_hash_map_new(map->is_equal, map->hasher,
97 map->free_fn, map->length * 2);
98 if (!new_map) {
100 }
101
102 for (usize i = 0; i < map->length; i++) {
103 struct ps_hash_map_entry* entry = &map->entries[i];
104 while (entry && entry->key) {
105 ps_hash_map_insert(&new_map, entry->key, entry->value);
106 entry = entry->next;
107 }
108 }
109 *map_ptr = new_map;
110
111 // free old map pointer
112 free(map);
113}
114
119void ps_hash_map_insert(struct ps_hash_map** map_ptr, void* key, void* value) {
120 if (!key) {
121 return;
122 }
123
124 struct ps_hash_map* map = *map_ptr;
125 CHECK({ ps_hash_map_assert_inv(map); });
126
127 // compute hash index
128 hash_t hash = map->hasher(key);
129 usize index = hash % map->length;
130 struct ps_hash_map_entry* entry = &map->entries[index];
131
132 // try to find the bucket associated with the key
133 if (entry->key) {
134 while (entry) {
135 if (entry->key != NULL && map->is_equal(entry->key, key)) {
136 break;
137 }
138 if (entry->next == NULL) {
139 // add new bucket if doesn't exist
140 struct ps_hash_map_entry* new_entry =
141 malloc(sizeof(*new_entry));
142 CHECK(if (!new_entry) {
143 PS_NO_MEMORY();
144 return;
145 })
146 new_entry->next = NULL;
147 entry->next = new_entry;
148 map->count++;
149 entry = new_entry;
150 break;
151 }
152 entry = entry->next;
153 }
154 } else {
155 // we are setting the first entry
156 map->count++;
157 }
158
159 // insert the key-value pair
160 entry->key = key;
161 entry->value = value;
162 entry->next = NULL;
163
164 // if load factor > 1, redistribute
165 // can be inefficient if just malloc'd for new entry?
166 if (map->count > map->length) {
167 *map_ptr = map;
169 }
170
171 CHECK({ ps_hash_map_assert_inv(*map_ptr); });
172}
173
174void* ps_hash_map_get(struct ps_hash_map* map, void* key) {
175 CHECK({ ps_hash_map_assert_inv(map); });
176
177 // compute hash index
178 hash_t hash = map->hasher(key);
179 usize index = hash % map->length;
180 struct ps_hash_map_entry* entry = &map->entries[index];
181
182 // simply walks the linked list
183 while (entry) {
184 if (entry->key && map->is_equal(entry->key, key)) {
185 return entry->value;
186 }
187 entry = entry->next;
188 }
189
190 return NULL;
191}
192
193void ps_hash_map_remove(struct ps_hash_map* map, void* key) {
194 CHECK({ ps_hash_map_assert_inv(map); });
195
196 // compute hash index
197 hash_t hash = map->hasher(key);
198 usize index = hash % map->length;
199 struct ps_hash_map_entry* entry = &map->entries[index];
200 struct ps_hash_map_entry* prev = NULL;
201
202 while (entry) {
203 // find the entry
204 if (entry->key && map->is_equal(entry->key, key)) {
205 // we found the entry
206 if (map->free_fn) {
207 map->free_fn(entry->value);
208 }
209
210 if (prev == NULL) {
211 // if it's the first node we have to see if there's a tail
212 if (entry->next) {
213 // if so, we move that forward
214 entry->key = entry->next->key;
215 entry->value = entry->next->value;
216 auto_t afterward = entry->next->next;
217 free(entry->next);
218 entry->next = afterward;
219 } else {
220 // if not, just set key to NULL
221 entry->key = NULL;
222 entry->value = NULL;
223 }
224 } else {
225 // otherwise remove the node
226 prev->next = entry->next;
227 free(entry);
228 }
229 map->count--;
230 return;
231 }
232
233 prev = entry;
234 entry = entry->next;
235 }
236
237 CHECK({ ps_hash_map_assert_inv(map); });
238}
239
240bool ps_hash_map_contains(struct ps_hash_map* map, void* key) {
241 return ps_hash_map_get(map, key) != NULL;
242}
243
245 return map->count;
246}
Defines assertion and abortion functionality.
#define CHECK(...)
All code inside CHECK will be run when PS_DEBUG is defined.
Definition abort.h:22
#define PS_NO_MEMORY()
Aborts the program due to exhaustion of virtual memory.
Definition abort.h:65
#define ps_assert(cond, msg)
Asserts the given condition cond, aborting via ps_abort with the given msg otherwise.
Definition abort.h:53
#define u64
Definition def.h:48
#define auto_t
Definition def.h:53
#define usize
Definition def.h:50
u64(* hasher_t)(void *)
Function that hashes an object.
Definition hash.h:15
bool(* is_equal_t)(void *, void *)
Function that compares whether two objects are equal.
Definition hash.h:18
u64 hash_t
Numeric type for hashes.
Definition hash.h:12
void * ps_hash_map_get(struct ps_hash_map *map, void *key)
Gets the value associated with the given key.
Definition hashmap.c:174
void ps_hash_map_redistribute(struct ps_hash_map **map_ptr)
Definition hashmap.c:94
struct ps_hash_map * _ps_hash_map_new(is_equal_t is_equal, hasher_t hasher, free_fn_t free_fn, u64 capacity)
Definition hashmap.c:49
struct ps_hash_map * ps_hash_map_new(is_equal_t is_equal, hasher_t hasher, free_fn_t free_fn)
Initializes a new hash map with the given comparison and memory functions.
Definition hashmap.c:43
u64 ps_hash_map_count(const struct ps_hash_map *map)
Gets the number of key-value pairs in the hash map.
Definition hashmap.c:244
void ps_hash_map_free(struct ps_hash_map *map)
Destroys all data owned by the hash map.
Definition hashmap.c:78
void ps_hash_map_insert(struct ps_hash_map **map_ptr, void *key, void *value)
Sets the object associated with the given key in the hash map, overwriting any existing value for tha...
Definition hashmap.c:119
bool ps_hash_map_contains(struct ps_hash_map *map, void *key)
Checks if the hash map contains the given key-value pair.
Definition hashmap.c:240
void ps_hash_map_remove(struct ps_hash_map *map, void *key)
Removes the key-value pair associated with the given key.
Definition hashmap.c:193
void ps_hash_map_assert_inv(const struct ps_hash_map *map)
Asserts the invariants of the data structure implementation.
Definition hashmap.c:12
#define PS_HASH_MAP_INITIAL_CAPACITY
Definition hashmap.c:9
Hash map data structure.
void(* free_fn_t)(void *ptr)
Function that destroys an object and its owned resources.
Definition hashmap.h:13
Enables malloc tracking functionality.
void * key
Definition hashmap.h:26
void * value
Definition hashmap.h:27
struct ps_hash_map_entry * next
Definition hashmap.h:28
A hash map.
Definition hashmap.h:18
u64 length
Number of first-node entries.
Definition hashmap.h:19
is_equal_t is_equal
Definition hashmap.h:22
hasher_t hasher
Definition hashmap.h:23
u64 count
Number of total entries: load factor = count / length.
Definition hashmap.h:20
struct ps_hash_map::ps_hash_map_entry entries[]
free_fn_t free_fn
Definition hashmap.h:24