AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
hashmap.h
Go to the documentation of this file.
1#ifndef _DS_HASHMAP_H
2#define _DS_HASHMAP_H
3
4#include <core/system.h>
5
6struct hashmap;
7struct hashmap_node;
8
9#include <core/string.h>
10#include <aquila/errno.h>
11#include <ds/queue.h>
12
13/*
14 * Hash tables: entries found by a hash of their key. Each bucket is a
15 * queue of nodes, and a node points to its entry. The caller computes
16 * the hash (hashmap_digest()), and gives the table a function that says
17 * whether an entry is the one for a key. The table doubles its buckets
18 * when it holds more than HASHMAP_LOAD entries per bucket on average.
19 */
20
21/* A key's hash. */
23
24/* How many buckets a new table has, unless the caller says. */
25#define HASHMAP_DEFAULT 20
26
27/* With more entries than this per bucket on average, the table doubles
28 * its buckets. */
29#define HASHMAP_LOAD 2
30
32struct hashmap {
33 size_t count;
34
35 size_t buckets_nr;
36 struct queue *buckets;
37
38 /* says whether entry `a` is the one for key `b` */
39 int (*eq)(void *a, void *b);
40};
41
45 void *entry;
46 struct qnode *qnode;
47};
48
49/* Loops over each node of a hash table, as a queue node `n` whose value
50 * is the hashmap_node. */
51#define hashmap_for(n, h) \
52 for (size_t __i__ = 0; __i__ < (h)->buckets_nr; ++__i__) \
53 queue_for ((n), &(h)->buckets[__i__])
54
57static inline struct hashmap *hashmap_new(size_t n, int (*eq)(void *, void *))
58{
59 if (!n) n = HASHMAP_DEFAULT;
60
61 struct hashmap *hashmap;
62
63 hashmap = kmalloc(sizeof(struct hashmap), &M_HASHMAP, 0);
64 if (!hashmap) return NULL;
65
66
67 memset(hashmap, 0, sizeof(struct hashmap));
68
69 hashmap->eq = eq;
70 hashmap->buckets = kmalloc(n * sizeof(struct queue), &M_QUEUE, 0);
71
72 if (!hashmap->buckets) {
74 return NULL;
75 }
76
77 memset(hashmap->buckets, 0, n * sizeof(struct queue));
78
80
81 return hashmap;
82}
83
85static inline hash_t hashmap_digest(const void *_id, size_t size)
86{
87 /* FNV-1a, where every byte changes the whole hash: page offsets, all
88 * multiples of 4096, would mostly collide in a sum of the bytes */
89 const unsigned char *id = (const unsigned char *) _id;
90
91#if ARCH_BITS==64
92 hash_t hash = 14695981039346656037ULL;
93 const hash_t prime = 1099511628211ULL;
94#else
95 hash_t hash = 2166136261U;
96 const hash_t prime = 16777619U;
97#endif
98
99 while (size--) {
100 hash ^= *id++;
101 hash *= prime;
102 }
103
104 return hash;
105}
106
110static inline void hashmap_grow(struct hashmap *hashmap)
111{
112 size_t nr = hashmap->buckets_nr * 2;
113 struct queue *buckets = kmalloc(nr * sizeof(struct queue), &M_QUEUE, M_ZERO);
114
115 if (!buckets)
116 return;
117
118 for (size_t i = 0; i < hashmap->buckets_nr; ++i) {
119 struct qnode *qnode = hashmap->buckets[i].head;
120
121 while (qnode) {
122 struct qnode *next = qnode->next;
123 struct hashmap_node *node = (struct hashmap_node *) qnode->value;
124 struct queue *queue = &buckets[node->hash % nr];
125
126 qnode->prev = queue->tail;
127 qnode->next = NULL;
128
129 if (queue->tail)
130 queue->tail->next = qnode;
131 else
132 queue->head = qnode;
133
134 queue->tail = qnode;
135 queue->count++;
136
137 qnode = next;
138 }
139 }
140
143 hashmap->buckets_nr = nr;
144}
145
148static inline int hashmap_insert(struct hashmap *hashmap, hash_t hash, void *entry)
149{
151 return -EINVAL;
152
153 struct hashmap_node *node;
154
155 node = kmalloc(sizeof(struct hashmap_node), &M_HASHMAP_NODE, 0);
156
157 if (!node) return -ENOMEM;
158
159 node->hash = hash;
160 node->entry = entry;
161
162 size_t idx = hash % hashmap->buckets_nr;
163
164 node->qnode = enqueue(&hashmap->buckets[idx], node);
165
166 if (!node->qnode) {
167 kfree(node);
168 return -ENOMEM;
169 }
170
171 hashmap->count++;
172
175
176 return 0;
177}
178
181static inline struct hashmap_node *hashmap_lookup(struct hashmap *hashmap, hash_t hash, void *key)
182{
184 return NULL;
185
186 size_t idx = hash % hashmap->buckets_nr;
187 struct queue *queue = &hashmap->buckets[idx];
188
189 queue_for (node, queue) {
190 struct hashmap_node *hnode = (struct hashmap_node *) node->value;
191 if (hnode && hnode->hash == hash && hashmap->eq(hnode->entry, key))
192 return hnode;
193 }
194
195 return NULL;
196}
197
200static inline int hashmap_replace(struct hashmap *hashmap, hash_t hash, void *key, void *entry)
201{
203 return -EINVAL;
204
205 size_t idx = hash % hashmap->buckets_nr;
206 struct queue *queue = &hashmap->buckets[idx];
207
208 queue_for (node, queue) {
209 struct hashmap_node *hnode = (struct hashmap_node *) node->value;
210 if (hnode && hnode->hash == hash && hashmap->eq(hnode->entry, key)) {
211 hnode->entry = entry;
212 return 0;
213 }
214 }
215
217}
218
220static inline void hashmap_node_remove(struct hashmap *hashmap, struct hashmap_node *node)
221{
222 if (!hashmap || !hashmap->buckets ||
223 !hashmap->buckets_nr || !node || !node->qnode)
224 return;
225
226 size_t idx = node->hash % hashmap->buckets_nr;
227 struct queue *queue = &hashmap->buckets[idx];
228
229 queue_node_remove(queue, node->qnode);
230 kfree(node);
231 hashmap->count--;
232}
233
235static inline void hashmap_free(struct hashmap *hashmap)
236{
237 for (size_t i = 0; i < hashmap->buckets_nr; ++i) {
238 struct queue *queue = &hashmap->buckets[i];
239
240 struct hashmap_node *node;
241 while ((node = dequeue(queue))) {
242 kfree(node);
243 }
244 }
245
247 kfree(hashmap);
248}
249
250#endif /* ! _DS_HASHMAP_H */
static struct blk * buckets[CACHE_BUCKETS]
Definition blkcache.c:66
static int hashmap_replace(struct hashmap *hashmap, hash_t hash, void *key, void *entry)
Replaces the entry for a key, or adds it if there's none.
Definition hashmap.h:200
static hash_t hashmap_digest(const void *_id, size_t size)
Returns the hash of a key of size bytes.
Definition hashmap.h:85
#define HASHMAP_LOAD
Definition hashmap.h:29
uintptr_t hash_t
Definition hashmap.h:22
static int hashmap_insert(struct hashmap *hashmap, hash_t hash, void *entry)
Adds an entry to a hash table.
Definition hashmap.h:148
static void hashmap_free(struct hashmap *hashmap)
Frees a hash table and its nodes (not their entries).
Definition hashmap.h:235
static void hashmap_node_remove(struct hashmap *hashmap, struct hashmap_node *node)
Removes a node from a hash table, and frees it (not its entry).
Definition hashmap.h:220
static struct hashmap_node * hashmap_lookup(struct hashmap *hashmap, hash_t hash, void *key)
Finds the node of the entry for a key, or returns NULL if there's none.
Definition hashmap.h:181
#define HASHMAP_DEFAULT
Definition hashmap.h:25
static void hashmap_grow(struct hashmap *hashmap)
Doubles a table's buckets, and moves each entry to its new bucket.
Definition hashmap.h:110
static struct hashmap * hashmap_new(size_t n, int(*eq)(void *, void *))
Makes a new, empty hash table of n buckets (0: HASHMAP_DEFAULT).
Definition hashmap.h:57
void * kmalloc(size_t size, struct malloc_type *type, int flags)
Allocates memory for one of the kernel's structures.
Definition kvmem.c:123
#define M_ZERO
Definition kvmem.h:26
void kfree(void *)
Frees an object that kmalloc() gave.
Definition kvmem.c:146
void * memset(void *dst, int c, size_t n)
Sets n bytes to c.
Definition libc.c:23
#define queue_for(n, q)
Definition queue.h:37
#define NULL
Definition stddef.h:27
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
A hash table's node: an entry, with its key's hash.
Definition hashmap.h:43
hash_t hash
Definition hashmap.h:44
void * entry
Definition hashmap.h:45
struct qnode * qnode
Definition hashmap.h:46
A hash table.
Definition hashmap.h:32
int(*) eq(void *a, void *b)
Definition hashmap.h:39
size_t count
Definition hashmap.h:33
size_t buckets_nr
Definition hashmap.h:35
struct queue * buckets
Definition hashmap.h:36
A queue's node, holding one entry.
Definition queue.h:19
void * value
Definition queue.h:20
struct qnode * next
Definition queue.h:22
struct qnode * prev
Definition queue.h:21
A queue.
Definition queue.h:29
struct qnode * tail
Definition queue.h:32
size_t count
Definition queue.h:30
struct qnode * head
Definition queue.h:31