/src/strongswan/src/libstrongswan/collections/hashtable.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2008-2020 Tobias Brunner |
3 | | * |
4 | | * Copyright (C) secunet Security Networks AG |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or modify it |
7 | | * under the terms of the GNU General Public License as published by the |
8 | | * Free Software Foundation; either version 2 of the License, or (at your |
9 | | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, but |
12 | | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
13 | | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | | * for more details. |
15 | | */ |
16 | | |
17 | | #include "hashtable.h" |
18 | | #include "hashtable_profiler.h" |
19 | | |
20 | | #include <utils/chunk.h> |
21 | | #include <utils/debug.h> |
22 | | |
23 | | /** The minimum size of the hash table (MUST be a power of 2) */ |
24 | | #define MIN_SIZE 8 |
25 | | /** The maximum size of the hash table (MUST be a power of 2) */ |
26 | 2 | #define MAX_SIZE (1 << 30) |
27 | | |
28 | | /** Determine the capacity/maximum load of the table (higher values cause |
29 | | * more collisions, lower values increase the memory overhead) */ |
30 | 73.8k | #define CAPACITY(size) (size / 3 * 2) |
31 | | /** Factor for the new table size based on the number of items when resizing, |
32 | | * with the above load factor this results in doubling the size when growing */ |
33 | 2 | #define RESIZE_FACTOR 3 |
34 | | |
35 | | /** |
36 | | * A note about these parameters: |
37 | | * |
38 | | * The maximum number of items that can be stored in this implementation |
39 | | * is MAX_COUNT = CAPACITY(MAX_SIZE). |
40 | | * Since we use u_int throughout, MAX_COUNT * RESIZE_FACTOR must not overflow |
41 | | * this type. |
42 | | */ |
43 | | #if (UINT_MAX / RESIZE_FACTOR < CAPACITY(MAX_SIZE)) |
44 | | #error Hahstable parameters invalid! |
45 | | #endif |
46 | | |
47 | | typedef struct pair_t pair_t; |
48 | | |
49 | | /** |
50 | | * This pair holds a pointer to the key and value it represents. |
51 | | */ |
52 | | struct pair_t { |
53 | | |
54 | | /** |
55 | | * Key of a hash table item. |
56 | | */ |
57 | | const void *key; |
58 | | |
59 | | /** |
60 | | * Value of a hash table item. |
61 | | */ |
62 | | void *value; |
63 | | |
64 | | /** |
65 | | * Cached hash (used in case of a resize). |
66 | | */ |
67 | | u_int hash; |
68 | | }; |
69 | | |
70 | | typedef struct private_hashtable_t private_hashtable_t; |
71 | | |
72 | | /** |
73 | | * Private data of a hashtable_t object. |
74 | | * |
75 | | */ |
76 | | struct private_hashtable_t { |
77 | | |
78 | | /** |
79 | | * Public part of hash table. |
80 | | */ |
81 | | hashtable_t public; |
82 | | |
83 | | /** |
84 | | * The number of items in the hash table. |
85 | | */ |
86 | | u_int count; |
87 | | |
88 | | /** |
89 | | * The current size of the hash table (always a power of 2). |
90 | | */ |
91 | | u_int size; |
92 | | |
93 | | /** |
94 | | * The current mask to calculate the row index (size - 1). |
95 | | */ |
96 | | u_int mask; |
97 | | |
98 | | /** |
99 | | * All items in the order they were inserted (removed items are marked by |
100 | | * setting the key to NULL until resized). |
101 | | */ |
102 | | pair_t *items; |
103 | | |
104 | | /** |
105 | | * Number of available slots in the array above and the table in general, |
106 | | * is set to CAPACITY(size) when the hash table is initialized. |
107 | | */ |
108 | | u_int capacity; |
109 | | |
110 | | /** |
111 | | * Number of used slots in the array above. |
112 | | */ |
113 | | u_int items_count; |
114 | | |
115 | | /** |
116 | | * Hash table with indices into the array above. The type depends on the |
117 | | * current capacity. |
118 | | */ |
119 | | void *table; |
120 | | |
121 | | /** |
122 | | * The hashing function. |
123 | | */ |
124 | | hashtable_hash_t hash; |
125 | | |
126 | | /** |
127 | | * The equality function. |
128 | | */ |
129 | | hashtable_equals_t equals; |
130 | | |
131 | | /** |
132 | | * Profiling data |
133 | | */ |
134 | | hashtable_profile_t profile; |
135 | | }; |
136 | | |
137 | | typedef struct private_enumerator_t private_enumerator_t; |
138 | | |
139 | | /** |
140 | | * Hash table enumerator implementation |
141 | | */ |
142 | | struct private_enumerator_t { |
143 | | |
144 | | /** |
145 | | * Implements enumerator interface |
146 | | */ |
147 | | enumerator_t enumerator; |
148 | | |
149 | | /** |
150 | | * Associated hash table |
151 | | */ |
152 | | private_hashtable_t *table; |
153 | | |
154 | | /** |
155 | | * Current index |
156 | | */ |
157 | | u_int index; |
158 | | }; |
159 | | |
160 | | /* |
161 | | * Described in header |
162 | | */ |
163 | | u_int hashtable_hash_ptr(const void *key) |
164 | 60 | { |
165 | 60 | return chunk_hash(chunk_from_thing(key)); |
166 | 60 | } |
167 | | |
168 | | /* |
169 | | * Described in header |
170 | | */ |
171 | | u_int hashtable_hash_str(const void *key) |
172 | 28.1k | { |
173 | 28.1k | return chunk_hash(chunk_from_str((char*)key)); |
174 | 28.1k | } |
175 | | |
176 | | /* |
177 | | * Described in header |
178 | | */ |
179 | | bool hashtable_equals_ptr(const void *key, const void *other_key) |
180 | 30 | { |
181 | 30 | return key == other_key; |
182 | 30 | } |
183 | | |
184 | | /* |
185 | | * Described in header |
186 | | */ |
187 | | bool hashtable_equals_str(const void *key, const void *other_key) |
188 | 21.0k | { |
189 | 21.0k | return streq(key, other_key); |
190 | 21.0k | } |
191 | | |
192 | | /** |
193 | | * Returns the index stored in the given bucket. If the bucket is empty, |
194 | | * 0 is returned. |
195 | | */ |
196 | | static inline u_int get_index(private_hashtable_t *this, u_int row) |
197 | 28.3k | { |
198 | 28.3k | if (this->capacity <= 0xff) |
199 | 28.3k | { |
200 | 28.3k | return ((uint8_t*)this->table)[row]; |
201 | 28.3k | } |
202 | 0 | else if (this->capacity <= 0xffff) |
203 | 0 | { |
204 | 0 | return ((uint16_t*)this->table)[row]; |
205 | 0 | } |
206 | 0 | return ((u_int*)this->table)[row]; |
207 | 28.3k | } |
208 | | |
209 | | /** |
210 | | * Set the index stored in the given bucket. Set to 0 to clear a bucket. |
211 | | */ |
212 | | static inline void set_index(private_hashtable_t *this, u_int row, u_int index) |
213 | 7.16k | { |
214 | 7.16k | if (this->capacity <= 0xff) |
215 | 7.16k | { |
216 | 7.16k | ((uint8_t*)this->table)[row] = index; |
217 | 7.16k | } |
218 | 0 | else if (this->capacity <= 0xffff) |
219 | 0 | { |
220 | 0 | ((uint16_t*)this->table)[row] = index; |
221 | 0 | } |
222 | 0 | else |
223 | 0 | { |
224 | 0 | ((u_int*)this->table)[row] = index; |
225 | 0 | } |
226 | 7.16k | } |
227 | | |
228 | | /** |
229 | | * This function returns the next-highest power of two for the given number. |
230 | | * The algorithm works by setting all bits on the right-hand side of the most |
231 | | * significant 1 to 1 and then increments the whole number so it rolls over |
232 | | * to the nearest power of two. Note: returns 0 for n == 0 |
233 | | * |
234 | | * Also used by hashlist_t. |
235 | | */ |
236 | | u_int hashtable_get_nearest_powerof2(u_int n) |
237 | 80.8k | { |
238 | 80.8k | u_int i; |
239 | | |
240 | 80.8k | --n; |
241 | 485k | for (i = 1; i < sizeof(u_int) * 8; i <<= 1) |
242 | 404k | { |
243 | 404k | n |= n >> i; |
244 | 404k | } |
245 | 80.8k | return ++n; |
246 | 80.8k | } |
247 | | |
248 | | /** |
249 | | * Init hash table to the given size |
250 | | */ |
251 | | static void init_hashtable(private_hashtable_t *this, u_int size) |
252 | 73.8k | { |
253 | 73.8k | u_int index_size = sizeof(u_int); |
254 | | |
255 | 73.8k | this->size = max(MIN_SIZE, min(size, MAX_SIZE)); |
256 | 73.8k | this->size = hashtable_get_nearest_powerof2(this->size); |
257 | 73.8k | this->mask = this->size - 1; |
258 | 73.8k | profile_size(&this->profile, this->size); |
259 | | |
260 | 73.8k | this->capacity = CAPACITY(this->size); |
261 | 73.8k | this->items = calloc(this->capacity, sizeof(pair_t)); |
262 | 73.8k | this->items_count = 0; |
263 | | |
264 | 73.8k | if (this->capacity <= 0xff) |
265 | 73.8k | { |
266 | 73.8k | index_size = sizeof(uint8_t); |
267 | 73.8k | } |
268 | 0 | else if (this->capacity <= 0xffff) |
269 | 0 | { |
270 | 0 | index_size = sizeof(uint16_t); |
271 | 0 | } |
272 | 73.8k | this->table = calloc(this->size, index_size); |
273 | 73.8k | } |
274 | | |
275 | | /** |
276 | | * Calculate the next bucket using quadratic probing (the sequence is h(k) + 1, |
277 | | * h(k) + 3, h(k) + 6, h(k) + 10, ...). |
278 | | */ |
279 | | static inline u_int get_next(private_hashtable_t *this, u_int row, u_int *p) |
280 | 63 | { |
281 | 63 | *p += 1; |
282 | 63 | return (row + *p) & this->mask; |
283 | 63 | } |
284 | | |
285 | | /** |
286 | | * Find the pair with the given key, optionally returns the hash and first empty |
287 | | * or previously used row if the key is not found. |
288 | | */ |
289 | | static inline pair_t *find_key(private_hashtable_t *this, const void *key, |
290 | | u_int *out_hash, u_int *out_row) |
291 | 40.3k | { |
292 | 40.3k | pair_t *pair; |
293 | 40.3k | u_int hash, row, p = 0, removed = 0, index; |
294 | 40.3k | bool found_removed = FALSE; |
295 | | |
296 | 40.3k | if (!this->count && !out_hash && !out_row) |
297 | 12.1k | { |
298 | 12.1k | return NULL; |
299 | 12.1k | } |
300 | | |
301 | 28.2k | lookup_start(); |
302 | | |
303 | 28.2k | hash = this->hash(key); |
304 | 28.2k | row = hash & this->mask; |
305 | 28.2k | index = get_index(this, row); |
306 | 28.2k | while (index) |
307 | 21.1k | { |
308 | 21.1k | lookup_probing(); |
309 | 21.1k | pair = &this->items[index-1]; |
310 | | |
311 | 21.1k | if (!pair->key) |
312 | 0 | { |
313 | 0 | if (!found_removed && out_row) |
314 | 0 | { |
315 | 0 | removed = row; |
316 | 0 | found_removed = TRUE; |
317 | 0 | } |
318 | 0 | } |
319 | 21.1k | else if (pair->hash == hash && this->equals(key, pair->key)) |
320 | 21.1k | { |
321 | 21.1k | lookup_success(&this->profile); |
322 | 21.1k | return pair; |
323 | 21.1k | } |
324 | 45 | row = get_next(this, row, &p); |
325 | 45 | index = get_index(this, row); |
326 | 45 | } |
327 | 7.12k | if (out_hash) |
328 | 7.12k | { |
329 | 7.12k | *out_hash = hash; |
330 | 7.12k | } |
331 | 7.12k | if (out_row) |
332 | 7.12k | { |
333 | 7.12k | *out_row = found_removed ? removed : row; |
334 | 7.12k | } |
335 | 7.12k | lookup_failure(&this->profile); |
336 | 7.12k | return NULL; |
337 | 28.2k | } |
338 | | |
339 | | /** |
340 | | * Helper to insert a new item into the table and items array, |
341 | | * returns its new index into the latter. |
342 | | */ |
343 | | static inline u_int insert_item(private_hashtable_t *this, u_int row) |
344 | 7.16k | { |
345 | 7.16k | u_int index = this->items_count++; |
346 | | |
347 | | /* we use 0 to mark unused buckets, so increase the index */ |
348 | 7.16k | set_index(this, row, index + 1); |
349 | 7.16k | return index; |
350 | 7.16k | } |
351 | | |
352 | | /** |
353 | | * Resize the hash table to the given size and rehash all the elements, |
354 | | * size may be smaller or even the same (e.g. if it's necessary to clear |
355 | | * previously used buckets). |
356 | | */ |
357 | | static bool rehash(private_hashtable_t *this, u_int size) |
358 | 2 | { |
359 | 2 | pair_t *old_items, *pair; |
360 | 2 | u_int old_count, i, p, row, index; |
361 | | |
362 | 2 | if (size > MAX_SIZE) |
363 | 0 | { |
364 | 0 | return FALSE; |
365 | 0 | } |
366 | | |
367 | 2 | old_items = this->items; |
368 | 2 | old_count = this->items_count; |
369 | 2 | free(this->table); |
370 | 2 | init_hashtable(this, size); |
371 | | |
372 | | /* no need to do anything if the table is empty and we are just cleaning |
373 | | * up previously used items */ |
374 | 2 | if (this->count) |
375 | 2 | { |
376 | 42 | for (i = 0; i < old_count; i++) |
377 | 40 | { |
378 | 40 | pair = &old_items[i]; |
379 | | |
380 | 40 | if (pair->key) |
381 | 40 | { |
382 | 40 | row = pair->hash & this->mask; |
383 | 40 | index = get_index(this, row); |
384 | 58 | for (p = 0; index;) |
385 | 18 | { |
386 | 18 | row = get_next(this, row, &p); |
387 | 18 | index = get_index(this, row); |
388 | 18 | } |
389 | 40 | index = insert_item(this, row); |
390 | 40 | this->items[index] = *pair; |
391 | 40 | } |
392 | 40 | } |
393 | 2 | } |
394 | 2 | free(old_items); |
395 | 2 | return TRUE; |
396 | 2 | } |
397 | | |
398 | | METHOD(hashtable_t, put, void*, |
399 | | private_hashtable_t *this, const void *key, void *value) |
400 | 7.12k | { |
401 | 7.12k | void *old_value = NULL; |
402 | 7.12k | pair_t *pair; |
403 | 7.12k | u_int index, hash = 0, row = 0; |
404 | | |
405 | 7.12k | if (this->items_count >= this->capacity && |
406 | 7.12k | !rehash(this, this->count * RESIZE_FACTOR)) |
407 | 0 | { |
408 | 0 | DBG1(DBG_LIB, "!!! FAILED TO RESIZE HASHTABLE TO %u !!!", |
409 | 0 | this->count * RESIZE_FACTOR); |
410 | 0 | return NULL; |
411 | 0 | } |
412 | 7.12k | pair = find_key(this, key, &hash, &row); |
413 | 7.12k | if (pair) |
414 | 0 | { |
415 | 0 | old_value = pair->value; |
416 | 0 | pair->value = value; |
417 | 0 | pair->key = key; |
418 | 0 | return old_value; |
419 | 0 | } |
420 | 7.12k | index = insert_item(this, row); |
421 | 7.12k | this->items[index] = (pair_t){ |
422 | 7.12k | .hash = hash, |
423 | 7.12k | .key = key, |
424 | 7.12k | .value = value, |
425 | 7.12k | }; |
426 | 7.12k | this->count++; |
427 | 7.12k | profile_count(&this->profile, this->count); |
428 | 7.12k | return NULL; |
429 | 7.12k | } |
430 | | |
431 | | METHOD(hashtable_t, get, void*, |
432 | | private_hashtable_t *this, const void *key) |
433 | 21.1k | { |
434 | 21.1k | pair_t *pair = find_key(this, key, NULL, NULL); |
435 | 21.1k | return pair ? pair->value : NULL; |
436 | 21.1k | } |
437 | | |
438 | | /** |
439 | | * Remove the given item from the table, returns the currently stored value. |
440 | | */ |
441 | | static void *remove_internal(private_hashtable_t *this, pair_t *pair) |
442 | 12.0k | { |
443 | 12.0k | void *value = NULL; |
444 | | |
445 | 12.0k | if (pair) |
446 | 30 | { /* this does not decrease the item count as we keep the previously |
447 | | * used items until the table is rehashed/resized */ |
448 | 30 | value = pair->value; |
449 | 30 | pair->key = NULL; |
450 | 30 | this->count--; |
451 | 30 | } |
452 | 12.0k | return value; |
453 | 12.0k | } |
454 | | |
455 | | METHOD(hashtable_t, remove_, void*, |
456 | | private_hashtable_t *this, const void *key) |
457 | 12.0k | { |
458 | 12.0k | pair_t *pair = find_key(this, key, NULL, NULL); |
459 | 12.0k | return remove_internal(this, pair); |
460 | 12.0k | } |
461 | | |
462 | | METHOD(hashtable_t, remove_at, void, |
463 | | private_hashtable_t *this, private_enumerator_t *enumerator) |
464 | 0 | { |
465 | 0 | if (enumerator->table == this && enumerator->index) |
466 | 0 | { /* the index is already advanced by one */ |
467 | 0 | u_int index = enumerator->index - 1; |
468 | 0 | remove_internal(this, &this->items[index]); |
469 | 0 | } |
470 | 0 | } |
471 | | |
472 | | METHOD(hashtable_t, get_count, u_int, |
473 | | private_hashtable_t *this) |
474 | 0 | { |
475 | 0 | return this->count; |
476 | 0 | } |
477 | | |
478 | | METHOD(enumerator_t, enumerate, bool, |
479 | | private_enumerator_t *this, va_list args) |
480 | 3.51k | { |
481 | 3.51k | const void **key; |
482 | 3.51k | void **value; |
483 | 3.51k | pair_t *pair; |
484 | | |
485 | 3.51k | VA_ARGS_VGET(args, key, value); |
486 | | |
487 | 3.51k | while (this->index < this->table->items_count) |
488 | 0 | { |
489 | 0 | pair = &this->table->items[this->index++]; |
490 | 0 | if (pair->key) |
491 | 0 | { |
492 | 0 | if (key) |
493 | 0 | { |
494 | 0 | *key = pair->key; |
495 | 0 | } |
496 | 0 | if (value) |
497 | 0 | { |
498 | 0 | *value = pair->value; |
499 | 0 | } |
500 | 0 | return TRUE; |
501 | 0 | } |
502 | 0 | } |
503 | 3.51k | return FALSE; |
504 | 3.51k | } |
505 | | |
506 | | METHOD(hashtable_t, create_enumerator, enumerator_t*, |
507 | | private_hashtable_t *this) |
508 | 3.51k | { |
509 | 3.51k | private_enumerator_t *enumerator; |
510 | | |
511 | 3.51k | INIT(enumerator, |
512 | 3.51k | .enumerator = { |
513 | 3.51k | .enumerate = enumerator_enumerate_default, |
514 | 3.51k | .venumerate = _enumerate, |
515 | 3.51k | .destroy = (void*)free, |
516 | 3.51k | }, |
517 | 3.51k | .table = this, |
518 | 3.51k | ); |
519 | 3.51k | return &enumerator->enumerator; |
520 | 3.51k | } |
521 | | |
522 | | static void destroy_internal(private_hashtable_t *this, |
523 | | void (*fn)(void*,const void*)) |
524 | 73.8k | { |
525 | 73.8k | pair_t *pair; |
526 | 73.8k | u_int i; |
527 | | |
528 | 73.8k | profiler_cleanup(&this->profile, this->count, this->size); |
529 | | |
530 | 73.8k | if (fn) |
531 | 3.51k | { |
532 | 10.5k | for (i = 0; i < this->items_count; i++) |
533 | 7.03k | { |
534 | 7.03k | pair = &this->items[i]; |
535 | 7.03k | if (pair->key) |
536 | 7.03k | { |
537 | 7.03k | fn(pair->value, pair->key); |
538 | 7.03k | } |
539 | 7.03k | } |
540 | 3.51k | } |
541 | 73.8k | free(this->items); |
542 | 73.8k | free(this->table); |
543 | 73.8k | free(this); |
544 | 73.8k | } |
545 | | |
546 | | METHOD(hashtable_t, destroy, void, |
547 | | private_hashtable_t *this) |
548 | 70.3k | { |
549 | 70.3k | destroy_internal(this, NULL); |
550 | 70.3k | } |
551 | | |
552 | | METHOD(hashtable_t, destroy_function, void, |
553 | | private_hashtable_t *this, void (*fn)(void*,const void*)) |
554 | 3.51k | { |
555 | 3.51k | destroy_internal(this, fn); |
556 | 3.51k | } |
557 | | |
558 | | /* |
559 | | * Described in header. |
560 | | */ |
561 | | hashtable_t *hashtable_create(hashtable_hash_t hash, hashtable_equals_t equals, |
562 | | u_int size) |
563 | 73.8k | { |
564 | 73.8k | private_hashtable_t *this; |
565 | | |
566 | 73.8k | INIT(this, |
567 | 73.8k | .public = { |
568 | 73.8k | .put = _put, |
569 | 73.8k | .get = _get, |
570 | 73.8k | .remove = _remove_, |
571 | 73.8k | .remove_at = (void*)_remove_at, |
572 | 73.8k | .get_count = _get_count, |
573 | 73.8k | .create_enumerator = _create_enumerator, |
574 | 73.8k | .destroy = _destroy, |
575 | 73.8k | .destroy_function = _destroy_function, |
576 | 73.8k | }, |
577 | 73.8k | .hash = hash, |
578 | 73.8k | .equals = equals, |
579 | 73.8k | ); |
580 | | |
581 | 73.8k | init_hashtable(this, size); |
582 | | |
583 | 73.8k | profiler_init(&this->profile, 2); |
584 | | |
585 | 73.8k | return &this->public; |
586 | 73.8k | } |