summaryrefslogtreecommitdiff
path: root/plugins/static/hashtable.h
blob: 670eac6589707768964b60b8ce79349dd19d9d97 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#pragma once

#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "memory.h"


inline size_t string_hash_func(const char *key) {
	size_t h = 0;
	for (; *key; key++) h = ((h << 9) | (h >> (64 - 9))) ^ *key;
	return h;
}

// API:
//  table prefix_make()
//  void prefix_destroy(table)
//  value* prefix_find(table*, const char *key)
//  void prefix_insert(table*, const char *key, value)
//  bool prefix_erase(table*, const char *key)
#define HASHTABLE_DEFINE(prefix_, value_t_) \
	struct prefix_ ## internal_table_entry { \
		char *key; \
		value_t_ value; \
	}; \
	\
	struct prefix_ ## table { \
		size_t size, load; /* size is always a power of 2 */ \
		struct prefix_ ## internal_table_entry *values; \
	}; \
	\
	struct prefix_ ## table prefix_ ## make(void) { \
		struct prefix_ ## table ht; \
		ht.size = 8; \
		ht.load = 0; \
		ht.values = malloc(ht.size, struct prefix_ ## internal_table_entry); \
		for (size_t i = 0; i < ht.size; i++) ht.values[i].key = NULL; \
		return ht; \
	} \
	\
	void prefix_ ## destroy(struct prefix_ ## table ht) { \
		for (size_t i = 0; i < ht.size; i++) free(ht.values[i].key); \
		free(ht.values); \
	} \
	\
	size_t prefix_ ## internal_probe_index(const struct prefix_ ## table *ht, const char *key) { \
		size_t h = string_hash_func(key) % ht->size; \
		for (size_t i = 0; i < ht->size; i++) { \
			if (!ht->values[h].key || strcmp(ht->values[h].key, key) == 0) return h; \
			h = (h + i) % ht->size;  /* quadratic probing with index h + 1/2 i + 1/2 i^2 */ \
		} \
		assert(false);  /* cannot happen unless the table is full, which we don't allow */ \
	} \
	\
	void prefix_ ## internal_grow_table(struct prefix_ ## table *ht); \
	\
	/* Returns pointer to value in table */ \
	value_t_* prefix_ ## find(struct prefix_ ## table *ht, const char *key) { \
		size_t idx = prefix_ ## internal_probe_index(ht, key); \
		if (ht->values[idx].key) return &ht->values[idx].value; \
		return NULL; \
	} \
	\
	/* Overwrites the existing value if already present */ \
	void prefix_ ## insert_nostrdup(struct prefix_ ## table *ht, char *key, value_t_ value) { \
		fprintf(stderr, "HT insert nostrdup load=%zu size=%zu\n", ht->load, ht->size); \
		if (ht->load * 4 >= ht->size * 3) prefix_ ## internal_grow_table(ht); \
		size_t idx = prefix_ ## internal_probe_index(ht, key); \
		if (!ht->values[idx].key) { \
			ht->values[idx].key = key; \
			ht->load++; \
		} \
		ht->values[idx].value = value; \
	} \
	\
	/* Overwrites the existing value if already present */ \
	void prefix_ ## insert(struct prefix_ ## table *ht, const char *key, value_t_ value) { \
		fprintf(stderr, "HT insert load=%zu size=%zu\n", ht->load, ht->size); \
		if (ht->load * 4 >= ht->size * 3) prefix_ ## internal_grow_table(ht); \
		size_t idx = prefix_ ## internal_probe_index(ht, key); \
		if (!ht->values[idx].key) { \
			ht->values[idx].key = strdup(key); \
			ht->load++; \
		} \
		ht->values[idx].value = value; \
	} \
	\
	/* Returns whether the element was indeed present */ \
	bool prefix_ ## erase(struct prefix_ ## table *ht, const char *key) { \
		size_t idx = prefix_ ## internal_probe_index(ht, key); \
		if (!ht->values[idx].key) return false; \
		free(ht->values[idx].key); \
		ht->values[idx].key = NULL; \
		ht->load--; \
		fprintf(stderr, "HT erase, afterwards load=%zu size=%zu\n", ht->load, ht->size); \
		return true; \
	} \
	\
	void prefix_ ## internal_grow_table(struct prefix_ ## table *ht) { \
		fprintf(stderr, "HT grow load=%zu size=%zu\n", ht->load, ht->size); \
		assert(ht->size * 2 != 0); \
		struct prefix_ ## table ht2; \
		ht2.size = ht->size * 2; \
		ht2.load = 0; \
		ht2.values = malloc(ht2.size, struct prefix_ ## internal_table_entry); \
		for (size_t i = 0; i < ht2.size; i++) ht2.values[i].key = NULL; \
		for (size_t i = 0; i < ht->size; i++) { \
			if (ht->values[i].key) prefix_ ## insert_nostrdup(&ht2, ht->values[i].key, ht->values[i].value); \
		} \
		free(ht->values); \
		*ht = ht2; \
	}