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