Concord - C Discord API library
A Discord API wrapper library written in C
oa_hash.h
Go to the documentation of this file.
1#ifndef OA_HASH_H
2#define OA_HASH_H
3
4#ifdef __cplusplus
5extern "C" {
6#endif /* __cplusplus */
7
8#ifdef OA_HASH_STATIC
9#define OA_HASH_API static
10#else
11#define OA_HASH_API extern
12#endif /* OA_HASH_STATIC */
13
14#include <stddef.h>
15
21};
22
26 struct {
27 const char *buf;
28 size_t length;
29 } key;
30 void *value;
31};
32
33#define __OA_HASH_ATTRS_const \
34 const size_t length; \
35 const size_t capacity; \
36 const struct oa_hash_entry *buckets
37#define __OA_HASH_ATTRS_mut \
38 size_t length; \
39 size_t capacity; \
40 struct oa_hash_entry *buckets
42#define OA_HASH_ATTRS(_qualifier) __OA_HASH_ATTRS_##_qualifier
43
45struct oa_hash {
47};
48
56OA_HASH_API void oa_hash_init(struct oa_hash *ht,
57 struct oa_hash_entry buckets[],
58 const size_t capacity);
59
65OA_HASH_API void oa_hash_cleanup(struct oa_hash *ht);
66
76 const struct oa_hash *ht, const char key[], const size_t len);
77
86OA_HASH_API void *oa_hash_get(const struct oa_hash *ht,
87 const char key[],
88 const size_t len);
89
100OA_HASH_API const struct oa_hash_entry *oa_hash_set_entry(struct oa_hash *ht,
101 const char key[],
102 const size_t len,
103 void *value);
104
115OA_HASH_API void *oa_hash_set(struct oa_hash *ht,
116 const char key[],
117 const size_t len,
118 void *value);
119
128OA_HASH_API int oa_hash_remove(struct oa_hash *ht,
129 const char key[],
130 const size_t len);
131
141 struct oa_hash *ht,
142 struct oa_hash_entry new_buckets[],
143 const size_t new_capacity);
144
145#ifndef OA_HASH_HEADER
146
147#include <string.h>
148#include <stdint.h>
149
150OA_HASH_API void
152 struct oa_hash_entry buckets[],
153 const size_t capacity)
154{
155 memset(buckets, 0, sizeof(struct oa_hash_entry) * capacity);
156 ht->buckets = buckets;
157 ht->length = 0;
158 ht->capacity = capacity;
159}
160
161OA_HASH_API void
163{
164 if (!ht) return;
165
166 ht->length = 0;
167 ht->capacity = 0;
168 ht->buckets = NULL;
169}
170
171static size_t
172_oa_hash_genhash(const char key[], size_t len, const size_t capacity)
173{
174 const unsigned char *str = (const unsigned char *)key;
175 unsigned long hash = 5381; /* DJB2 initial value */
176
177 if (!key || !capacity) return 0;
178
179 while (len--) {
180 hash = ((hash & 0x7fffffff) << 5) + hash + *str++;
181 }
182 return hash % capacity;
183}
184
185OA_HASH_API const struct oa_hash_entry *
186oa_hash_get_entry(const struct oa_hash *ht, const char key[], const size_t len)
187{
188 const size_t start_slot = _oa_hash_genhash(key, len, ht->capacity);
189 size_t slot = start_slot;
190
191 if (!len || !ht->capacity) return NULL;
192
193 do {
194 struct oa_hash_entry *entry = &ht->buckets[slot];
195
196 if (entry->state == OA_HASH_ENTRY_EMPTY) {
197 return NULL;
198 }
199
200 if (entry->state == OA_HASH_ENTRY_OCCUPIED && len == entry->key.length
201 && 0 == memcmp(entry->key.buf, key, len))
202 {
203 return entry;
204 }
205
206 slot = (slot + 1) % ht->capacity;
207 } while (slot != start_slot);
208
209 return NULL;
210}
211
212OA_HASH_API void *
213oa_hash_get(const struct oa_hash *ht, const char key[], const size_t len)
214{
215 const struct oa_hash_entry *entry = oa_hash_get_entry(ht, key, len);
216 return entry ? entry->value : NULL;
217}
218
219OA_HASH_API const struct oa_hash_entry *
221 const char key[],
222 const size_t len,
223 void *value)
224{
225 const size_t start_slot = _oa_hash_genhash(key, len, ht->capacity);
226 size_t slot = start_slot;
227 size_t first_deleted = SIZE_MAX;
228
229 if (!len || !ht->capacity) return NULL;
230
231 do {
232 struct oa_hash_entry *entry = &ht->buckets[slot];
233
234 if (entry->state != OA_HASH_ENTRY_OCCUPIED) {
235 if (first_deleted == SIZE_MAX
236 && entry->state == OA_HASH_ENTRY_DELETED)
237 {
238 first_deleted = slot;
239 }
240 if (entry->state == OA_HASH_ENTRY_EMPTY) {
241 slot = (first_deleted != SIZE_MAX) ? first_deleted : slot;
242 entry = &ht->buckets[slot];
243 entry->key.buf = (char *)key;
244 entry->key.length = len;
245 entry->value = value;
247 ht->length++;
248 return entry;
249 }
250 }
251
252 if (entry->state == OA_HASH_ENTRY_OCCUPIED && len == entry->key.length
253 && 0 == memcmp(entry->key.buf, key, len))
254 {
255 entry->value = value;
256 return entry;
257 }
258
259 slot = (slot + 1) % ht->capacity;
260 } while (slot != start_slot);
261
262 return NULL;
263}
264
265OA_HASH_API void *
267 const char key[],
268 const size_t len,
269 void *value)
270{
271 const struct oa_hash_entry *entry = oa_hash_set_entry(ht, key, len, value);
272 return entry ? entry->value : NULL;
273}
274
275OA_HASH_API int
276oa_hash_remove(struct oa_hash *ht, const char key[], const size_t len)
277{
278 const size_t start_slot = _oa_hash_genhash(key, len, ht->capacity);
279 size_t slot = start_slot;
280
281 if (!len || !ht->capacity) return 0;
282
283 do {
284 struct oa_hash_entry *entry = &ht->buckets[slot];
285
286 if (entry->state == OA_HASH_ENTRY_EMPTY) {
287 return 0;
288 }
289
290 if (entry->state == OA_HASH_ENTRY_OCCUPIED && len == entry->key.length
291 && 0 == memcmp(entry->key.buf, key, len))
292 {
294 ht->length--;
295 return 1;
296 }
297
298 slot = (slot + 1) % ht->capacity;
299 } while (slot != start_slot);
300
301 return 0;
302}
303
306 struct oa_hash_entry new_buckets[],
307 const size_t new_capacity)
308{
309 struct oa_hash_entry *old_buckets = ht->buckets;
310 const size_t old_capacity = ht->capacity;
311 const size_t old_length = ht->length;
312 size_t i;
313
314 if (!new_buckets || new_capacity <= old_capacity) return 0;
315
316 memset(new_buckets, 0, sizeof(struct oa_hash_entry) * new_capacity);
317
318 /* temporarily switch to new buckets */
319 ht->buckets = new_buckets;
320 ht->capacity = new_capacity;
321 ht->length = 0;
322
323 for (i = 0; i < old_capacity; ++i) {
324 if (old_buckets[i].state == OA_HASH_ENTRY_OCCUPIED
325 && !oa_hash_set_entry(ht, old_buckets[i].key.buf,
326 old_buckets[i].key.length,
327 old_buckets[i].value))
328 {
329 /* restore original state on failure */
330 ht->buckets = old_buckets;
331 ht->capacity = old_capacity;
332 ht->length = old_length;
333 return NULL;
334 }
335 }
336 return old_buckets;
337}
338
339#endif /* OA_HASH_HEADER */
340
341#ifdef __cplusplus
342}
343#endif /* __cplusplus */
344
345#endif /* OA_HASH_H */
const struct oa_hash_entry * oa_hash_set_entry(struct oa_hash *ht, const char key[], const size_t len, void *value)
Insert or update entry.
Definition: oa_hash.h:220
struct oa_hash_entry * oa_hash_rehash(struct oa_hash *ht, struct oa_hash_entry new_buckets[], const size_t new_capacity)
Rehash table to new buckets array.
Definition: oa_hash.h:305
void * oa_hash_set(struct oa_hash *ht, const char key[], const size_t len, void *value)
Insert or update entry (wrapper around oa_hash_set_entry)
Definition: oa_hash.h:266
void * oa_hash_get(const struct oa_hash *ht, const char key[], const size_t len)
Retrieve value by key (wrapper around oa_hash_get_entry)
Definition: oa_hash.h:213
void oa_hash_cleanup(struct oa_hash *ht)
Clean up hash table entries and struct.
Definition: oa_hash.h:162
oa_hash_entry_state
Hash table entry state.
Definition: oa_hash.h:17
@ OA_HASH_ENTRY_EMPTY
Definition: oa_hash.h:18
@ OA_HASH_ENTRY_OCCUPIED
Definition: oa_hash.h:19
@ OA_HASH_ENTRY_DELETED
Definition: oa_hash.h:20
#define OA_HASH_ATTRS(_qualifier)
can be used to cast to struct oa_hash
Definition: oa_hash.h:42
#define OA_HASH_API
Definition: oa_hash.h:11
int oa_hash_remove(struct oa_hash *ht, const char key[], const size_t len)
Remove entry by key.
Definition: oa_hash.h:276
void oa_hash_init(struct oa_hash *ht, struct oa_hash_entry buckets[], const size_t capacity)
Initialize hash table with given buckets array.
Definition: oa_hash.h:151
const struct oa_hash_entry * oa_hash_get_entry(const struct oa_hash *ht, const char key[], const size_t len)
Retrieve entry by key.
Definition: oa_hash.h:186
Entry holding key-value pair in hash table.
Definition: oa_hash.h:24
void * value
Definition: oa_hash.h:30
size_t length
Definition: oa_hash.h:28
enum oa_hash_entry_state state
Definition: oa_hash.h:25
struct oa_hash_entry::@14 key
const char * buf
Definition: oa_hash.h:27
Open addressing hash table.
Definition: oa_hash.h:45
struct oa_hash_entry * buckets
Definition: oa_hash.h:46
size_t length
Definition: oa_hash.h:46
size_t capacity
Definition: oa_hash.h:46