The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
hash.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Resizable hash tables
18 *
19 * The weird "reverse" function is based on an idea from
20 * "Split-Ordered Lists - Lock-free Resizable Hash Tables", with
21 * modifications so that they're not lock-free. :(
22 *
23 * However, the split-order idea allows a fast & easy splitting of the
24 * hash bucket chain when the hash table is resized. Without it, we'd
25 * have to check & update the pointers for every node in the buck chain,
26 * rather than being able to move 1/2 of the entries in the chain with
27 * one update.
28 *
29 * @file src/lib/util/hash.c
30 *
31 * @copyright 2005,2006 The FreeRADIUS server project
32 */
33RCSID("$Id: dbccb0582a6ab40f1a5f03ea10888b1cd641f9c9 $")
34
35#include <freeradius-devel/util/hash.h>
36
37/*
38 * A reasonable number of buckets to start off with.
39 * Should be a power of two.
40 */
41#define FR_HASH_NUM_BUCKETS (64)
42
49
51 uint32_t num_elements; //!< Number of elements in the hash table.
52 uint32_t num_buckets; //!< Number of buckets (how long the array is) - power of 2 */
55
56 fr_free_t free; //!< Data free function.
57 fr_hash_t hash; //!< Hashing function.
58 fr_cmp_t cmp; //!< Comparison function.
59
60 char const *type; //!< Talloc type to check elements against.
61
63 fr_hash_entry_t **buckets; //!< Array of hash buckets.
64};
65
66#ifdef TESTING
67static int grow = 0;
68#endif
69
70/*
71 * perl -e 'foreach $i (0..255) {$r = 0; foreach $j (0 .. 7 ) { if (($i & ( 1<< $j)) != 0) { $r |= (1 << (7 - $j));}} print $r, ", ";if (($i & 7) == 7) {print "\n";}}'
72 */
73static const uint8_t reversed_byte[256] = {
74 0, 128, 64, 192, 32, 160, 96, 224,
75 16, 144, 80, 208, 48, 176, 112, 240,
76 8, 136, 72, 200, 40, 168, 104, 232,
77 24, 152, 88, 216, 56, 184, 120, 248,
78 4, 132, 68, 196, 36, 164, 100, 228,
79 20, 148, 84, 212, 52, 180, 116, 244,
80 12, 140, 76, 204, 44, 172, 108, 236,
81 28, 156, 92, 220, 60, 188, 124, 252,
82 2, 130, 66, 194, 34, 162, 98, 226,
83 18, 146, 82, 210, 50, 178, 114, 242,
84 10, 138, 74, 202, 42, 170, 106, 234,
85 26, 154, 90, 218, 58, 186, 122, 250,
86 6, 134, 70, 198, 38, 166, 102, 230,
87 22, 150, 86, 214, 54, 182, 118, 246,
88 14, 142, 78, 206, 46, 174, 110, 238,
89 30, 158, 94, 222, 62, 190, 126, 254,
90 1, 129, 65, 193, 33, 161, 97, 225,
91 17, 145, 81, 209, 49, 177, 113, 241,
92 9, 137, 73, 201, 41, 169, 105, 233,
93 25, 153, 89, 217, 57, 185, 121, 249,
94 5, 133, 69, 197, 37, 165, 101, 229,
95 21, 149, 85, 213, 53, 181, 117, 245,
96 13, 141, 77, 205, 45, 173, 109, 237,
97 29, 157, 93, 221, 61, 189, 125, 253,
98 3, 131, 67, 195, 35, 163, 99, 227,
99 19, 147, 83, 211, 51, 179, 115, 243,
100 11, 139, 75, 203, 43, 171, 107, 235,
101 27, 155, 91, 219, 59, 187, 123, 251,
102 7, 135, 71, 199, 39, 167, 103, 231,
103 23, 151, 87, 215, 55, 183, 119, 247,
104 15, 143, 79, 207, 47, 175, 111, 239,
105 31, 159, 95, 223, 63, 191, 127, 255
106};
107
108
109/*
110 * perl -e 'foreach $i (0..255) {$r = 0;foreach $j (0 .. 7) { $r = $i & (1 << (7 - $j)); last if ($r)} print $i & ~($r), ", ";if (($i & 7) == 7) {print "\n";}}'
111 */
112static uint8_t parent_byte[256] = {
113 0, 0, 0, 1, 0, 1, 2, 3,
114 0, 1, 2, 3, 4, 5, 6, 7,
115 0, 1, 2, 3, 4, 5, 6, 7,
116 8, 9, 10, 11, 12, 13, 14, 15,
117 0, 1, 2, 3, 4, 5, 6, 7,
118 8, 9, 10, 11, 12, 13, 14, 15,
119 16, 17, 18, 19, 20, 21, 22, 23,
120 24, 25, 26, 27, 28, 29, 30, 31,
121 0, 1, 2, 3, 4, 5, 6, 7,
122 8, 9, 10, 11, 12, 13, 14, 15,
123 16, 17, 18, 19, 20, 21, 22, 23,
124 24, 25, 26, 27, 28, 29, 30, 31,
125 32, 33, 34, 35, 36, 37, 38, 39,
126 40, 41, 42, 43, 44, 45, 46, 47,
127 48, 49, 50, 51, 52, 53, 54, 55,
128 56, 57, 58, 59, 60, 61, 62, 63,
129 0, 1, 2, 3, 4, 5, 6, 7,
130 8, 9, 10, 11, 12, 13, 14, 15,
131 16, 17, 18, 19, 20, 21, 22, 23,
132 24, 25, 26, 27, 28, 29, 30, 31,
133 32, 33, 34, 35, 36, 37, 38, 39,
134 40, 41, 42, 43, 44, 45, 46, 47,
135 48, 49, 50, 51, 52, 53, 54, 55,
136 56, 57, 58, 59, 60, 61, 62, 63,
137 64, 65, 66, 67, 68, 69, 70, 71,
138 72, 73, 74, 75, 76, 77, 78, 79,
139 80, 81, 82, 83, 84, 85, 86, 87,
140 88, 89, 90, 91, 92, 93, 94, 95,
141 96, 97, 98, 99, 100, 101, 102, 103,
142 104, 105, 106, 107, 108, 109, 110, 111,
143 112, 113, 114, 115, 116, 117, 118, 119,
144 120, 121, 122, 123, 124, 125, 126, 127
145};
146
147
148/*
149 * Reverse a key.
150 */
152{
153 /*
154 * Cast to uint32_t is required because the
155 * default type of of the expression is an
156 * int and ubsan correctly complains that
157 * the result of 0xff << 24 won't fit in a
158 * signed 32bit integer.
159 */
160 return (((uint32_t)reversed_byte[key & 0xff] << 24) |
161 ((uint32_t)reversed_byte[(key >> 8) & 0xff] << 16) |
162 ((uint32_t)reversed_byte[(key >> 16) & 0xff] << 8) |
163 ((uint32_t)reversed_byte[(key >> 24) & 0xff]));
164}
165
166/*
167 * Take the parent by discarding the highest bit that is set.
168 */
170{
171 if (key > 0x00ffffff)
172 return (key & 0x00ffffff) | (parent_byte[key >> 24] << 24);
173
174 if (key > 0x0000ffff)
175 return (key & 0x0000ffff) | (parent_byte[key >> 16] << 16);
176
177 if (key > 0x000000ff)
178 return (key & 0x000000ff) | (parent_byte[key >> 8] << 8);
179
180 return parent_byte[key];
181}
182
183
184static CC_NO_UBSAN(undefined)
185int list_find(fr_hash_entry_t **found, fr_hash_table_t *ht,
186 fr_hash_entry_t *head, uint32_t reversed, void const *data)
187{
188 fr_hash_entry_t *cur;
189
190 *found = NULL;
191
192 for (cur = head; cur != &ht->null; cur = cur->next) {
193 if (cur->reversed == reversed) {
194 if (ht->cmp) {
195 fr_cmp_ret_t cmp = ht->cmp(data, cur->data);
196
197 if (unlikely(cmp == CMP_ERR)) return -1;
198 if (cmp == CMP_GT) break;
199 if (cmp == CMP_LT) continue;
200 }
201 *found = cur;
202 return 0;
203 }
204 if (cur->reversed > reversed) break;
205 }
206
207 return 0;
208}
209
210
211/*
212 * Inserts a new entry into the list, in order.
213 */
214static CC_NO_UBSAN(undefined)
215int list_insert(fr_hash_table_t *ht,
217{
218 fr_hash_entry_t **last, *cur;
219
220 last = head;
221
222 for (cur = *head; cur != &ht->null; last = &(cur->next), cur = cur->next) {
223 if (cur->reversed > node->reversed) break;
224
225 if (cur->reversed == node->reversed) {
226 if (ht->cmp) {
227 fr_cmp_ret_t cmp = ht->cmp(node->data, cur->data);
228
229 if (unlikely(cmp == CMP_ERR)) return -1;
230 if (cmp == CMP_GT) break;
231 if (cmp == CMP_LT) continue;
232 }
233 return 1;
234 }
235 }
236
237 node->next = *last;
238 *last = node;
239
240 return 0;
241}
242
243
244/*
245 * Delete an entry from the list.
246 */
249{
250 fr_hash_entry_t **last, *cur;
251
252 last = head;
253
254 for (cur = *head; cur != &ht->null; cur = cur->next) {
255 if (cur == node) {
256 *last = node->next;
257 return;
258 }
259 last = &(cur->next);
260 }
261
262 fr_assert(0);
263}
264
266{
267 uint32_t i;
268 fr_hash_entry_t *node, *next;
269
270 if (ht->free) {
271 for (i = 0; i < ht->num_buckets; i++) {
272 if (ht->buckets[i]) for (node = ht->buckets[i];
273 node != &ht->null;
274 node = next) {
275 next = node->next;
276 if (!node->data) continue; /* dummy entry */
277
278 ht->free(node->data);
279 }
280 }
281 }
282
283 return 0;
284}
285
286/*
287 * Create the table.
288 *
289 * Memory usage in bytes is (20/3) * number of entries.
290 */
292 char const *type,
293 fr_hash_t hash_func,
294 fr_cmp_t cmp_func,
295 fr_free_t free_func)
296{
297 fr_hash_table_t *ht;
298
299 ht = talloc(ctx, fr_hash_table_t);
300 if (!ht) return NULL;
301 talloc_set_destructor(ht, _fr_hash_table_free);
302
303 *ht = (fr_hash_table_t){
304 .type = type,
305 .free = free_func,
306 .hash = hash_func,
307 .cmp = cmp_func,
308 .num_buckets = FR_HASH_NUM_BUCKETS,
309 .mask = FR_HASH_NUM_BUCKETS - 1,
310
311 /*
312 * Have a default load factor of 2.5. In practice this
313 * means that the average load will hit 3 before the
314 * table grows.
315 */
316 .next_grow = (FR_HASH_NUM_BUCKETS << 1) + (FR_HASH_NUM_BUCKETS >> 1),
317 .buckets = talloc_zero_array(ht, fr_hash_entry_t *, FR_HASH_NUM_BUCKETS)
318 };
319 if (unlikely(!ht->buckets)) {
320 talloc_free(ht);
321 return NULL;
322 }
323
324 ht->null.reversed = ~0;
325 ht->null.key = ~0;
326 ht->null.next = &ht->null;
327 ht->buckets[0] = &ht->null;
328
329 return ht;
330}
331
332
333/*
334 * If the current bucket is uninitialized, initialize it
335 * by recursively copying information from the parent.
336 *
337 * We may have a situation where entry E is a parent to 2 other
338 * entries E' and E". If we split E into E and E', then the
339 * nodes meant for E" end up in E or E', either of which is
340 * wrong. To solve that problem, we walk down the whole chain,
341 * inserting the elements into the correct place.
342 */
344{
345 uint32_t parent_entry;
346 fr_hash_entry_t **last, *cur;
347 uint32_t this;
348
349 parent_entry = parent_of(entry);
350
351 /* parent_entry == entry if and only if entry == 0 */
352
353 if (!ht->buckets[parent_entry]) {
354 fr_hash_table_fixup(ht, parent_entry);
355 }
356
357 /*
358 * Keep walking down cur, trying to find entries that
359 * don't belong here any more. There may be multiple
360 * ones, so we can't have a naive algorithm...
361 */
362 last = &ht->buckets[parent_entry];
363 this = parent_entry;
364
365 for (cur = *last; cur != &ht->null; cur = cur->next) {
366 uint32_t real_entry;
367
368 real_entry = cur->key & ht->mask;
369 if (real_entry != this) { /* ht->buckets[real_entry] == NULL */
370 *last = &ht->null;
371 ht->buckets[real_entry] = cur;
372 this = real_entry;
373 }
374
375 last = &(cur->next);
376 }
377
378 /*
379 * We may NOT have initialized this bucket, so do it now.
380 */
381 if (!ht->buckets[entry]) ht->buckets[entry] = &ht->null;
382}
383
384/*
385 * This should be a power of two. Changing it to 4 doesn't seem
386 * to make any difference.
387 */
388#define GROW_FACTOR (2)
389
390/*
391 * Set a maximum number of entries, which lets us avoid overflows
392 * on next_grow, GROW_FACTOR, etc.
393 */
394#define TABLE_MAX ((uint32_t) 0x20000000)
395
396/*
397 * Grow the hash table.
398 */
400{
401 fr_hash_entry_t **buckets;
402 size_t existing = talloc_get_size(ht->buckets);
403 size_t expanded;
404
405 /*
406 * Cap the growth, because we need to be able to set a
407 * mask, etc.
408 */
409 if (ht->num_buckets >= TABLE_MAX) return;
410
411 expanded = GROW_FACTOR * ht->num_buckets;
412 if (expanded > TABLE_MAX) expanded = TABLE_MAX;
413
414 buckets = talloc_realloc(ht, ht->buckets, fr_hash_entry_t *, expanded);
415 if (!buckets) return;
416
417 memset(((uint8_t *)buckets) + existing, 0, talloc_get_size(buckets) - existing);
418
419 ht->buckets = buckets;
420 ht->num_buckets = expanded;
421 ht->mask = ht->num_buckets - 1;
422 ht->next_grow *= GROW_FACTOR;
423
424#ifdef TESTING
425 grow = 1;
426 fprintf(stderr, "GROW TO %d\n", ht->num_buckets);
427#endif
428}
429
430/*
431 * Internal find a node routine.
432 */
433static inline CC_HINT(always_inline) int hash_table_find(fr_hash_entry_t **found, fr_hash_table_t *ht,
434 uint32_t key, void const *data)
435{
436 uint32_t entry;
437 uint32_t reversed;
438
439 entry = key & ht->mask;
440 reversed = reverse(key);
441
442 if (!ht->buckets[entry]) fr_hash_table_fixup(ht, entry);
443
444 return list_find(found, ht, ht->buckets[entry], reversed, data);
445}
446
447/** Find data in a hash table
448 *
449 * @param[out] found the matching element, or NULL if no element matched.
450 * @param[in] ht to find data in.
451 * @param[in] data to find. Will be passed to the
452 * hashing function.
453 * @return
454 * - 0 the comparison sequence succeeded, check found for the result.
455 * - -1 the comparator errored, retrieve the error with fr_strerror.
456 */
457CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
458int fr_hash_table_find(void **found, fr_hash_table_t *ht, void const *data)
459{
460 fr_hash_entry_t *node;
461
462 *found = NULL;
463 if (unlikely(hash_table_find(&node, ht, ht->hash(data), data) < 0)) return -1;
464 if (node) *found = UNCONST(void *, node->data);
465
466 return 0;
467}
468
469/** Hash table lookup with pre-computed key
470 *
471 * @param[out] found the matching element, or NULL if no element matched.
472 * @param[in] ht to find data in.
473 * @param[in] key the precomputed key.
474 * @param[in] data for list matching.
475 * @return
476 * - 0 the comparison sequence succeeded, check found for the result.
477 * - -1 the comparator errored, retrieve the error with fr_strerror.
478 */
479int fr_hash_table_find_by_key(void **found, fr_hash_table_t *ht, uint32_t key, void const *data)
480{
481 fr_hash_entry_t *node;
482
483 *found = NULL;
484 if (unlikely(hash_table_find(&node, ht, key, data) < 0)) return -1;
485 if (node) *found = UNCONST(void *, node->data);
486
487 return 0;
488}
489
490/** Insert data into a hash table
491 *
492 * @param[in] ht to insert data into.
493 * @param[in] data to insert. Will be passed to the
494 * hashing function.
495 * @return
496 * - 0 if data was inserted.
497 * - 1 if data already existed and was not inserted.
498 * - -1 on comparator or allocation error, retrieve the error with fr_strerror.
499 */
500CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
502{
503 uint32_t key;
504 uint32_t entry;
505 uint32_t reversed;
506 fr_hash_entry_t *node;
507 int ret;
508
509#ifndef TALLOC_GET_TYPE_ABORT_NOOP
510 if (ht->type) (void)_talloc_get_type_abort(data, ht->type, __location__);
511#endif
512
513 if (ht->num_elements >= TABLE_MAX) return -1;
514
515 key = ht->hash(data);
516 entry = key & ht->mask;
517 reversed = reverse(key);
518
519 if (!ht->buckets[entry]) fr_hash_table_fixup(ht, entry);
520
521 /*
522 * If we try to do our own memory allocation here, the
523 * speedup is only ~15% or so, which isn't worth it.
524 */
525 node = talloc_zero(ht, fr_hash_entry_t);
526 if (unlikely(!node)) return -1;
527
528 node->next = &ht->null;
529 node->reversed = reversed;
530 node->key = key;
531 node->data = UNCONST(void *, data);
532
533 /* already in the table, can't insert it */
534 ret = list_insert(ht, &ht->buckets[entry], node);
535 if (ret != 0) {
536 talloc_free(node);
537 return ret;
538 }
539
540 /*
541 * Check the load factor, and grow the table if
542 * necessary.
543 */
544 ht->num_elements++;
545 if (ht->num_elements >= ht->next_grow) fr_hash_table_grow(ht);
546
547 return 0;
548}
549
550/** Replace old data with new data, OR insert if there is no old
551 *
552 * @param[out] old data that was replaced. If this argument
553 * is not NULL, then the old data will not
554 * be freed, even if a free function is
555 * configured.
556 * @param[in] ht to insert data into.
557 * @param[in] data to replace. Will be passed to the
558 * hashing function.
559 * @return
560 * - 1 if data was replaced.
561 * - 0 if data was inserted.
562 * - -1 if we failed to replace data, retrieve the error with fr_strerror.
563 */
564CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
565int fr_hash_table_replace(void **old, fr_hash_table_t *ht, void const *data)
566{
567 fr_hash_entry_t *node;
568
569 if (unlikely(hash_table_find(&node, ht, ht->hash(data), data) < 0)) return -1;
570 if (!node) {
571 if (old) *old = NULL;
572 return (fr_hash_table_insert(ht, data) == 0) ? 0 : -1;
573 }
574
575 if (old) {
576 *old = node->data;
577 } else if (ht->free) {
578 ht->free(node->data);
579 }
580
581 node->data = UNCONST(void *, data);
582
583 return 1;
584}
585
586/** Remove an entry from the hash table, without freeing the data
587 *
588 * @param[out] removed the data we removed, if any. May be NULL.
589 * @param[in] ht to remove data from.
590 * @param[in] data to remove. Will be passed to the
591 * hashing function.
592 * @return
593 * - 0 if we removed data, removed is populated.
594 * - 1 if we couldn't find any matching data.
595 * - -1 if the comparator errored, retrieve the error with fr_strerror.
596 */
597CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
598int fr_hash_table_remove(void **removed, fr_hash_table_t *ht, void const *data)
599{
600 uint32_t key;
601 uint32_t entry;
602 uint32_t reversed;
603 fr_hash_entry_t *node;
604
605 if (removed) *removed = NULL;
606
607 key = ht->hash(data);
608 entry = key & ht->mask;
609 reversed = reverse(key);
610
611 if (!ht->buckets[entry]) fr_hash_table_fixup(ht, entry);
612
613 if (unlikely(list_find(&node, ht, ht->buckets[entry], reversed, data) < 0)) return -1;
614 if (!node) return 1;
615
616 list_delete(ht, &ht->buckets[entry], node);
617 ht->num_elements--;
618
619 if (removed) *removed = node->data;
620 talloc_free(node);
621
622 return 0;
623}
624
625/** Remove and free data (if a free function was specified)
626 *
627 * @param[in] ht to remove data from.
628 * @param[in] data to remove/free.
629 * @return
630 * - 0 if we removed data.
631 * - 1 if we couldn't find any matching data.
632 * - -1 if the comparator errored, retrieve the error with fr_strerror.
633 */
634CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
636{
637 void *old;
638 int ret;
639
640 ret = fr_hash_table_remove(&old, ht, data);
641 if (ret != 0) return ret;
642
643 if (ht->free) ht->free(old);
644
645 return 0;
646}
647
648/*
649 * Count number of elements
650 */
651CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
653{
654 return ht->num_elements;
655}
656
657/** Iterate over entries in a hash table
658 *
659 * @note If the hash table is modified the iterator should be considered invalidated.
660 *
661 * @param[in] ht to iterate over.
662 * @param[in] iter Pointer to an iterator struct, used to maintain
663 * state between calls.
664 * @return
665 * - User data.
666 * - NULL if at the end of the list.
667 */
669{
670 fr_hash_entry_t *node;
671 uint32_t i;
672
673 /*
674 * Return the next element in the bucket.
675 */
676 if (iter->next != &ht->null) {
677 node = iter->next;
678 iter->next = node->next;
679
680 return node->data;
681 }
682
683 /*
684 * We've wrapped around to bucket 0 again. That means we're done.
685 */
686 if (iter->bucket == 0) return NULL;
687
688 /*
689 * We might have to go through multiple empty
690 * buckets to find one that contains something
691 * we should return
692 */
693 i = iter->bucket - 1;
694 for (;;) {
695 if (!ht->buckets[i]) fr_hash_table_fixup(ht, i);
696
697 node = ht->buckets[i];
698 if (node == &ht->null) {
699 if (i == 0) break;
700 i--;
701 continue; /* This bucket was empty too... */
702 }
703
704 iter->next = node->next; /* Store the next one to examine */
705 iter->bucket = i;
706 return node->data;
707 }
708 iter->bucket = i;
709
710 return NULL;
711}
712
713/** Initialise an iterator
714 *
715 * @note If the hash table is modified the iterator should be considered invalidated.
716 *
717 * @param[in] ht to iterate over.
718 * @param[out] iter to initialise.
719 * @return
720 * - The first entry in the hash table.
721 * - NULL if the hash table is empty.
722 */
724{
725 iter->bucket = ht->num_buckets;
726 iter->next = &ht->null;
727
728 return fr_hash_table_iter_next(ht, iter);
729}
730
731/** Copy all entries out of a hash table into an array
732 *
733 * @param[in] ctx to allocate array in.
734 * @param[in] out array of hash table entries.
735 * @param[in] ht to flatter.
736 * @return
737 * - 0 on success.
738 * - -1 on failure.
739 */
740int fr_hash_table_flatten(TALLOC_CTX *ctx, void **out[], fr_hash_table_t *ht)
741{
742 uint64_t num = fr_hash_table_num_elements(ht), i;
743 fr_hash_iter_t iter;
744 void *item, **list;
745
746 if (unlikely(!(list = talloc_array(ctx, void *, num)))) return -1;
747
748 for (item = fr_hash_table_iter_init(ht, &iter), i = 0;
749 item;
750 item = fr_hash_table_iter_next(ht, &iter), i++) list[i] = item;
751
752 *out = list;
753
754 return 0;
755}
756
757/** Ensure all buckets are filled
758 *
759 * This must be called if the table will be read by multiple threads without
760 * synchronisation. Synchronisation is still required for updates.
761 *
762 * @param[in] ht to fill.
763 */
765{
766 uint32_t i;
767
768 if (!ht->num_buckets) return;
769
770
771 i = ht->num_buckets - 1;
772
773 while (true) {
774 if (!ht->buckets[i]) fr_hash_table_fixup(ht, i);
775 if (!i) break;
776 i--;
777 }
778}
779
780#ifdef TESTING
781/*
782 * Show what the hash table is doing.
783 */
784int fr_hash_table_info(fr_hash_table_t *ht)
785{
786 int i, a, collisions, uninitialized;
787 int array[256];
788
789 if (!ht) return 0;
790
791 uninitialized = collisions = 0;
792 memset(array, 0, sizeof(array));
793
794 for (i = 0; i < ht->num_buckets; i++) {
795 uint32_t key;
796 int load;
797 fr_hash_entry_t *node, *next;
798
799 /*
800 * If we haven't inserted or looked up an entry
801 * in a bucket, it's uninitialized.
802 */
803 if (!ht->buckets[i]) {
804 uninitialized++;
805 continue;
806 }
807
808 load = 0;
809 key = ~0;
810 for (node = ht->buckets[i]; node != &ht->null; node = next) {
811 if (node->reversed == key) {
812 collisions++;
813 } else {
814 key = node->reversed;
815 }
816 next = node->next;
817 load++;
818 }
819
820 if (load > 255) load = 255;
821 array[load]++;
822 }
823
824 printf("HASH TABLE %p\tbuckets: %d\t(%d uninitialized)\n", ht,
825 ht->num_buckets, uninitialized);
826 printf("\tnum entries %d\thash collisions %d\n",
827 ht->num_elements, collisions);
828
829 a = 0;
830 for (i = 1; i < 256; i++) {
831 if (!array[i]) continue;
832 printf("%d\t%d\n", i, array[i]);
833
834 /*
835 * Since the entries are ordered, the lookup cost
836 * for any one element in a chain is (on average)
837 * the cost of walking half of the chain.
838 */
839 if (i > 1) {
840 a += array[i] * i;
841 }
842 }
843 a /= 2;
844 a += array[1];
845
846 printf("\texpected lookup cost = %d/%d or %f\n\n",
847 ht->num_elements, a,
848 (float) ht->num_elements / (float) a);
849
850 return 0;
851}
852#endif
853
854
855#define FNV_MAGIC_INIT (0x811c9dc5)
856#define FNV_MAGIC_PRIME (0x01000193)
857
858/*
859 * A fast hash function. For details, see:
860 *
861 * http://www.isthe.com/chongo/tech/comp/fnv/
862 *
863 * Which also includes public domain source. We've re-written
864 * it here for our purposes.
865 */
866uint32_t fr_hash(void const *data, size_t size)
867{
868 uint8_t const *p = data;
869 uint8_t const *q = p + size;
871
872 /*
873 * FNV-1 hash each octet in the buffer
874 */
875 while (p != q) {
876 /*
877 * XOR the 8-bit quantity into the bottom of
878 * the hash.
879 */
880 hash ^= (uint32_t) (*p++);
881
882 /*
883 * Multiply by 32-bit magic FNV prime, mod 2^32
884 */
886#if 0
887 /*
888 * Potential optimization.
889 */
890 hash += (hash<<1) + (hash<<4) + (hash<<7) + (hash<<8) + (hash<<24);
891#endif
892 }
893
894 return hash;
895}
896
897/*
898 * Continue hashing data.
899 */
900uint32_t fr_hash_update(void const *data, size_t size, uint32_t hash)
901{
902 uint8_t const *p = data;
903 uint8_t const *q;
904
905 if (size == 0) return hash; /* Avoid ubsan issues with access NULL pointer */
906
907 q = p + size;
908 while (p < q) {
909 hash ^= (uint32_t) (*p++);
911 }
912
913 return hash;
914}
915
916/*
917 * Hash a C string, so we loop over it once.
918 */
920{
922
923 while (*p) {
924 hash ^= (uint32_t) (*p++);
925 /* coverity[overflow_const] */
927 }
928
929 return hash;
930}
931
932/** Hash a C string, converting all chars to lowercase
933 *
934 */
936{
938
939 while (*p) {
940 hash ^= (uint32_t) (tolower((uint8_t) *p++));
941 /* coverity[overflow_const] */
943 }
944
945 return hash;
946}
947
948/*
949 * 64-bit variants of the above functions/
950 */
951#undef FNV_MAGIC_INIT
952#undef FNV_MAGIC_PRIME
953#define FNV_MAGIC_INIT ((uint64_t) 0xcbf29ce484222325)
954#define FNV_MAGIC_PRIME ((uint64_t) 0x00000100000001B3)
955
956/*
957 * A 64-bit version of the above hash
958 */
959uint64_t fr_hash64(void const *data, size_t size)
960{
961 uint8_t const *p = data;
962 uint8_t const *q = p + size;
963 uint64_t hash = FNV_MAGIC_INIT;
964
965 /*
966 * FNV-1 hash each octet in the buffer
967 */
968 while (p != q) {
969 /*
970 * XOR the 8-bit quantity into the bottom of
971 * the hash.
972 */
973 hash ^= (uint64_t) (*p++);
974
975 /*
976 * Multiply by 64-bit magic FNV prime, mod 2^64
977 */
979 }
980
981 return hash;
982}
983
984/*
985 * Continue hashing data.
986 */
987uint64_t fr_hash64_update(void const *data, size_t size, uint64_t hash)
988{
989 uint8_t const *p = data;
990 uint8_t const *q;
991
992 if (size == 0) return hash; /* Avoid ubsan issues with access NULL pointer */
993
994 q = p + size;
995 while (p < q) {
996 hash ^= (uint64_t) (*p++);
998 }
999
1000 return hash;
1001}
1002
1003/** Check hash table is sane
1004 *
1005 */
1007{
1008 fr_hash_iter_t iter;
1009 void *ptr;
1010
1011 (void)talloc_get_type_abort(ht, fr_hash_table_t);
1012 (void)talloc_get_type_abort(ht->buckets, fr_hash_entry_t *);
1013
1014 fr_assert(talloc_array_length(ht->buckets) == ht->num_buckets);
1015
1016 /*
1017 * Check talloc headers on all data
1018 */
1019 if (ht->type) {
1020 for (ptr = fr_hash_table_iter_init(ht, &iter);
1021 ptr;
1022 ptr = fr_hash_table_iter_next(ht, &iter)) {
1023 (void)_talloc_get_type_abort(ptr, ht->type, __location__);
1024 }
1025 }
1026}
1027
1028#ifdef TESTING
1029/*
1030 * cc -g -DTESTING -I ../include hash.c -o hash
1031 *
1032 * ./hash
1033 */
1034static uint32_t hash_int(void const *data)
1035{
1036 return fr_hash((int *) data, sizeof(int));
1037}
1038
1039#define MAX 1024*1024
1040int main(int argc, char **argv)
1041{
1042 int i, *p, *q, k;
1043 fr_hash_table_t *ht;
1044 int *array;
1045
1046 ht = fr_hash_table_alloc(NULL, hash_int, NULL, NULL);
1047 if (!ht) {
1048 fprintf(stderr, "Hash create failed\n");
1049 fr_exit(1);
1050 }
1051
1052 array = talloc_zero_array(NULL, int, MAX);
1053 if (!array) fr_exit(1);
1054
1055 for (i = 0; i < MAX; i++) {
1056 p = array + i;
1057 *p = i;
1058
1059 if (fr_hash_table_insert(ht, p) != 0) {
1060 fprintf(stderr, "Failed insert %08x\n", i);
1061 fr_exit(1);
1062 }
1063#ifdef TEST_INSERT
1064 (void) fr_hash_table_find((void **)&q, ht, p);
1065 if (q != p) {
1066 fprintf(stderr, "Bad data %d\n", i);
1067 fr_exit(1);
1068 }
1069#endif
1070 }
1071
1072 fr_hash_table_info(ht);
1073
1074 /*
1075 * Build this to see how lookups result in shortening
1076 * of the hash chains.
1077 */
1078 if (1) {
1079 for (i = 0; i < MAX ; i++) {
1080 (void) fr_hash_table_find((void **)&q, ht, &i);
1081 if (!q || *q != i) {
1082 fprintf(stderr, "Failed finding %d\n", i);
1083 fr_exit(1);
1084 }
1085
1086#if 0
1087 if (fr_hash_table_delete(ht, &i) != 0) {
1088 fprintf(stderr, "Failed deleting %d\n", i);
1089 fr_exit(1);
1090 }
1091 (void) fr_hash_table_find((void **)&q, ht, &i);
1092 if (q) {
1093 fprintf(stderr, "Failed to delete %08x\n", i);
1094 fr_exit(1);
1095 }
1096#endif
1097 }
1098
1099 fr_hash_table_info(ht);
1100 }
1101
1102 fr_hash_table_free(ht);
1103 talloc_free(array);
1104
1105 return EXIT_SUCCESS;
1106}
1107#endif
#define load(_var)
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:186
#define RCSID(id)
Definition build.h:560
#define CC_NO_UBSAN(_sanitize)
Definition build.h:503
#define unlikely(_x)
Definition build.h:455
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition debug.h:261
int main(int argc, char **argv)
Definition dhcpclient.c:530
void * fr_hash_table_iter_next(fr_hash_table_t *ht, fr_hash_iter_t *iter)
Iterate over entries in a hash table.
Definition hash.c:668
int fr_hash_table_find(void **found, fr_hash_table_t *ht, void const *data)
Find data in a hash table.
Definition hash.c:458
fr_hash_entry_t ** buckets
Array of hash buckets.
Definition hash.c:63
void * fr_hash_table_iter_init(fr_hash_table_t *ht, fr_hash_iter_t *iter)
Initialise an iterator.
Definition hash.c:723
char const * type
Talloc type to check elements against.
Definition hash.c:60
uint32_t next_grow
Definition hash.c:53
uint32_t fr_hash_case_string(char const *p)
Hash a C string, converting all chars to lowercase.
Definition hash.c:935
#define TABLE_MAX
Definition hash.c:394
static int _fr_hash_table_free(fr_hash_table_t *ht)
Definition hash.c:265
uint32_t fr_hash_update(void const *data, size_t size, uint32_t hash)
Definition hash.c:900
static uint8_t parent_byte[256]
Definition hash.c:112
uint32_t fr_hash(void const *data, size_t size)
Definition hash.c:866
uint64_t fr_hash64(void const *data, size_t size)
Definition hash.c:959
fr_hash_entry_t null
Definition hash.c:62
#define FNV_MAGIC_PRIME
Definition hash.c:856
fr_hash_t hash
Hashing function.
Definition hash.c:57
fr_hash_entry_t * next
Definition hash.c:44
static uint32_t parent_of(uint32_t key)
Definition hash.c:169
fr_cmp_t cmp
Comparison function.
Definition hash.c:58
void * data
Definition hash.c:47
static const uint8_t reversed_byte[256]
Definition hash.c:73
uint32_t num_buckets
Number of buckets (how long the array is) - power of 2 *‍/.
Definition hash.c:52
static int hash_table_find(fr_hash_entry_t **found, fr_hash_table_t *ht, uint32_t key, void const *data)
Definition hash.c:433
int fr_hash_table_flatten(TALLOC_CTX *ctx, void **out[], fr_hash_table_t *ht)
Copy all entries out of a hash table into an array.
Definition hash.c:740
uint32_t mask
Definition hash.c:54
int fr_hash_table_delete(fr_hash_table_t *ht, void const *data)
Remove and free data (if a free function was specified)
Definition hash.c:635
static void list_delete(fr_hash_table_t *ht, fr_hash_entry_t **head, fr_hash_entry_t *node)
Definition hash.c:247
static uint32_t reverse(uint32_t key)
Definition hash.c:151
uint32_t num_elements
Number of elements in the hash table.
Definition hash.c:51
uint32_t reversed
Definition hash.c:45
uint32_t fr_hash_string(char const *p)
Definition hash.c:919
#define FNV_MAGIC_INIT
Definition hash.c:855
int fr_hash_table_find_by_key(void **found, fr_hash_table_t *ht, uint32_t key, void const *data)
Hash table lookup with pre-computed key.
Definition hash.c:479
void fr_hash_table_verify(fr_hash_table_t *ht)
Check hash table is sane.
Definition hash.c:1006
fr_free_t free
Data free function.
Definition hash.c:56
fr_hash_table_t * _fr_hash_table_alloc(TALLOC_CTX *ctx, char const *type, fr_hash_t hash_func, fr_cmp_t cmp_func, fr_free_t free_func)
Definition hash.c:291
void fr_hash_table_fill(fr_hash_table_t *ht)
Ensure all buckets are filled.
Definition hash.c:764
uint32_t key
Definition hash.c:46
static void fr_hash_table_fixup(fr_hash_table_t *ht, uint32_t entry)
Definition hash.c:343
#define GROW_FACTOR
Definition hash.c:388
uint64_t fr_hash64_update(void const *data, size_t size, uint64_t hash)
Definition hash.c:987
int fr_hash_table_replace(void **old, fr_hash_table_t *ht, void const *data)
Replace old data with new data, OR insert if there is no old.
Definition hash.c:565
uint32_t fr_hash_table_num_elements(fr_hash_table_t *ht)
Definition hash.c:652
int fr_hash_table_remove(void **removed, fr_hash_table_t *ht, void const *data)
Remove an entry from the hash table, without freeing the data.
Definition hash.c:598
#define FR_HASH_NUM_BUCKETS
Definition hash.c:41
int fr_hash_table_insert(fr_hash_table_t *ht, void const *data)
Insert data into a hash table.
Definition hash.c:501
static void fr_hash_table_grow(fr_hash_table_t *ht)
Definition hash.c:399
Definition hash.c:43
struct fr_hash_table_s fr_hash_table_t
Definition hash.h:58
fr_hash_entry_t * next
the next node which we will return
Definition hash.h:43
uint32_t(* fr_hash_t)(void const *)
Definition hash.h:36
#define fr_hash_table_alloc(_ctx, _hash_node, _cmp_node, _free_node)
Definition hash.h:61
uint32_t bucket
the current buck that we are examining
Definition hash.h:42
Stores the state of the current iteration operation.
Definition hash.h:41
talloc_free(hp)
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
Definition lst.c:121
unsigned int uint32_t
unsigned char uint8_t
void(* fr_free_t)(void *)
Definition misc.h:58
fr_cmp_ret_t(* fr_cmp_t)(void const *a, void const *b)
Definition misc.h:57
fr_cmp_ret_t
Result of an ordering comparison.
Definition misc.h:50
@ CMP_GT
a > b
Definition misc.h:54
@ CMP_LT
a < b
Definition misc.h:52
@ CMP_ERR
comparison failed
Definition misc.h:51
#define fr_assert(_expr)
Definition rad_assert.h:37
static unsigned int hash(char const *username, unsigned int tablesize)
Definition rlm_passwd.c:132
fr_aka_sim_id_type_t type
static fr_slen_t head
Definition xlat.h:421
static fr_slen_t data
Definition value.h:1340
static size_t char ** out
Definition value.h:1030