1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "array.h"
5 #include "primes.h"
6 #include "hash2.h"
7
8 #define HASH_TABLE_MIN_SIZE 131
9
10 struct hash2_value {
11 struct hash2_value *next;
12 unsigned int key_hash;
13 /* user_data[value_size] */
14 };
15 ARRAY_DEFINE_TYPE(hash2_value, struct hash2_value *);
16
17 struct hash2_table {
18 unsigned int count;
19 unsigned int initial_size;
20 unsigned int hash_table_size;
21 unsigned int value_size;
22
23 pool_t value_pool;
24 struct hash2_value *deleted_values;
25
26 ARRAY_TYPE(hash2_value) hash_table;
27
28 hash2_key_callback_t *key_hash_cb;
29 hash2_cmp_callback_t *key_compare_cb;
30 void *context;
31 };
32
hash2_alloc_table(struct hash2_table * hash,unsigned int size)33 static void hash2_alloc_table(struct hash2_table *hash, unsigned int size)
34 {
35 hash->hash_table_size = size;
36
37 i_array_init(&hash->hash_table, hash->hash_table_size);
38 (void)array_idx_get_space(&hash->hash_table, hash->hash_table_size-1);
39 }
40
41 struct hash2_table *
hash2_create(unsigned int initial_size,unsigned int value_size,hash2_key_callback_t * key_hash_cb,hash2_cmp_callback_t * key_compare_cb,void * context)42 hash2_create(unsigned int initial_size, unsigned int value_size,
43 hash2_key_callback_t *key_hash_cb,
44 hash2_cmp_callback_t *key_compare_cb, void *context)
45 {
46 struct hash2_table *hash;
47
48 hash = i_new(struct hash2_table, 1);
49 hash->initial_size = I_MAX(initial_size, HASH_TABLE_MIN_SIZE);
50 hash->value_size = value_size;
51 hash->key_hash_cb = key_hash_cb;
52 hash->key_compare_cb = key_compare_cb;
53 hash->context = context;
54
55 hash->value_pool = pool_alloconly_create("hash2 value pool", 16384);
56 hash2_alloc_table(hash, hash->initial_size);
57 return hash;
58 }
59
hash2_destroy(struct hash2_table ** _hash)60 void hash2_destroy(struct hash2_table **_hash)
61 {
62 struct hash2_table *hash = *_hash;
63
64 *_hash = NULL;
65 array_free(&hash->hash_table);
66 pool_unref(&hash->value_pool);
67 i_free(hash);
68 }
69
hash2_clear(struct hash2_table * hash)70 void hash2_clear(struct hash2_table *hash)
71 {
72 array_free(&hash->hash_table);
73 hash2_alloc_table(hash, hash->initial_size);
74 p_clear(hash->value_pool);
75 hash->count = 0;
76 hash->deleted_values = NULL;
77 }
78
hash2_resize(struct hash2_table * hash,bool grow)79 static void hash2_resize(struct hash2_table *hash, bool grow)
80 {
81 ARRAY_TYPE(hash2_value) old_hash_table;
82 struct hash2_value *old_hash, *value, **valuep, *next;
83 unsigned int next_size, old_count, i, idx;
84 float nodes_per_list;
85
86 nodes_per_list = (float)hash->count / (float)hash->hash_table_size;
87 if (nodes_per_list > 0.3 && nodes_per_list < 2.0)
88 return;
89
90 next_size = I_MAX(primes_closest(hash->count + 1), hash->initial_size);
91 if (hash->hash_table_size >= next_size &&
92 (grow || next_size == hash->hash_table_size))
93 return;
94
95 old_hash_table = hash->hash_table;
96 hash2_alloc_table(hash, next_size);
97
98 old_count = array_count(&old_hash_table);
99 for (i = 0; i < old_count; i++) {
100 old_hash = array_idx_elem(&old_hash_table, i);
101 for (value = old_hash; value != NULL; value = next) {
102 next = value->next;
103
104 idx = value->key_hash % hash->hash_table_size;
105 valuep = array_idx_modifiable(&hash->hash_table, idx);
106 value->next = *valuep;
107 *valuep = value;
108 }
109 }
110 array_free(&old_hash_table);
111 }
112
hash2_lookup(const struct hash2_table * hash,const void * key)113 void *hash2_lookup(const struct hash2_table *hash, const void *key)
114 {
115 unsigned int key_hash = hash->key_hash_cb(key);
116 struct hash2_value *value;
117 void *user_value;
118
119 value = array_idx_elem(&hash->hash_table,
120 key_hash % hash->hash_table_size);
121 while (value != NULL) {
122 if (value->key_hash == key_hash) {
123 user_value = value + 1;
124 if (hash->key_compare_cb(key, user_value,
125 hash->context))
126 return user_value;
127 }
128 value = value->next;
129 }
130 return NULL;
131 }
132
hash2_iterate(const struct hash2_table * hash,unsigned int key_hash,struct hash2_iter * iter)133 void *hash2_iterate(const struct hash2_table *hash,
134 unsigned int key_hash, struct hash2_iter *iter)
135 {
136 struct hash2_value *value;
137
138 if (iter->value == NULL) {
139 iter->key_hash = key_hash;
140 value = array_idx_elem(&hash->hash_table,
141 key_hash % hash->hash_table_size);
142 iter->next_value = value;
143 }
144 while (iter->next_value != NULL) {
145 if (iter->next_value->key_hash == key_hash) {
146 iter->value = iter->next_value;
147 iter->next_value = iter->next_value->next;
148 return iter->value + 1;
149 }
150 iter->next_value = iter->next_value->next;
151 }
152 return NULL;
153 }
154
hash2_insert(struct hash2_table * hash,const void * key)155 void *hash2_insert(struct hash2_table *hash, const void *key)
156 {
157 return hash2_insert_hash(hash, hash->key_hash_cb(key));
158 }
159
hash2_insert_hash(struct hash2_table * hash,unsigned int key_hash)160 void *hash2_insert_hash(struct hash2_table *hash, unsigned int key_hash)
161 {
162 struct hash2_value *value, **valuep;
163
164 hash2_resize(hash, TRUE);
165
166 if (hash->deleted_values != NULL) {
167 value = hash->deleted_values;
168 hash->deleted_values = value->next;
169 value->next = NULL;
170 memset(value + 1, 0, hash->value_size);
171 } else {
172 value = p_malloc(hash->value_pool,
173 sizeof(*value) + hash->value_size);
174 }
175 value->key_hash = key_hash;
176
177 valuep = array_idx_modifiable(&hash->hash_table,
178 key_hash % hash->hash_table_size);
179 value->next = *valuep;
180 *valuep = value;
181
182 hash->count++;
183 return value + 1;
184 }
185
186 static void
hash2_remove_value_p(struct hash2_table * hash,struct hash2_value ** valuep)187 hash2_remove_value_p(struct hash2_table *hash, struct hash2_value **valuep)
188 {
189 struct hash2_value *deleted_value;
190
191 deleted_value = *valuep;
192 *valuep = deleted_value->next;
193
194 deleted_value->next = hash->deleted_values;
195 hash->deleted_values = deleted_value;
196
197 hash->count--;
198 }
199
hash2_remove(struct hash2_table * hash,const void * key)200 void hash2_remove(struct hash2_table *hash, const void *key)
201 {
202 unsigned int key_hash = hash->key_hash_cb(key);
203 struct hash2_value **valuep;
204
205 valuep = array_idx_modifiable(&hash->hash_table,
206 key_hash % hash->hash_table_size);
207 while (*valuep != NULL) {
208 if ((*valuep)->key_hash == key_hash &&
209 hash->key_compare_cb(key, (*valuep) + 1, hash->context)) {
210 hash2_remove_value_p(hash, valuep);
211 hash2_resize(hash, FALSE);
212 return;
213 }
214 valuep = &(*valuep)->next;
215 }
216 i_panic("hash2_remove(): key not found");
217 }
218
hash2_remove_iter(struct hash2_table * hash,struct hash2_iter * iter)219 void hash2_remove_iter(struct hash2_table *hash, struct hash2_iter *iter)
220 {
221 struct hash2_value **valuep, *next;
222
223 valuep = array_idx_modifiable(&hash->hash_table,
224 iter->key_hash % hash->hash_table_size);
225 while (*valuep != NULL) {
226 if (*valuep == iter->value) {
227 next = (*valuep)->next;
228 /* don't allow resizing, otherwise iterating would
229 break completely */
230 hash2_remove_value_p(hash, valuep);
231 iter->next_value = next;
232 return;
233 }
234 valuep = &(*valuep)->next;
235 }
236 i_panic("hash2_remove_value(): key/value not found");
237 }
238
hash2_count(const struct hash2_table * hash)239 unsigned int hash2_count(const struct hash2_table *hash)
240 {
241 return hash->count;
242 }
243