The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: c290ec76373479abe7503834ba33be837ac0e0ef $")
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
185 fr_hash_entry_t *head, uint32_t reversed, void const *data)
186{
187 fr_hash_entry_t *cur;
188
189 for (cur = head; cur != &ht->null; cur = cur->next) {
190 if (cur->reversed == reversed) {
191 if (ht->cmp) {
192 int cmp = ht->cmp(data, cur->data);
193 if (cmp > 0) break;
194 if (cmp < 0) continue;
195 }
196 return cur;
197 }
198 if (cur->reversed > reversed) break;
199 }
200
201 return NULL;
202}
203
204
205/*
206 * Inserts a new entry into the list, in order.
207 */
210{
211 fr_hash_entry_t **last, *cur;
212
213 last = head;
214
215 for (cur = *head; cur != &ht->null; last = &(cur->next), cur = cur->next) {
216 if (cur->reversed > node->reversed) break;
217
218 if (cur->reversed == node->reversed) {
219 if (ht->cmp) {
220 int8_t cmp = ht->cmp(node->data, cur->data);
221 if (cmp > 0) break;
222 if (cmp < 0) continue;
223 }
224 return false;
225 }
226 }
227
228 node->next = *last;
229 *last = node;
230
231 return true;
232}
233
234
235/*
236 * Delete an entry from the list.
237 */
240{
241 fr_hash_entry_t **last, *cur;
242
243 last = head;
244
245 for (cur = *head; cur != &ht->null; cur = cur->next) {
246 if (cur == node) break;
247 last = &(cur->next);
248 }
249
250 *last = node->next;
251}
252
254{
255 uint32_t i;
256 fr_hash_entry_t *node, *next;
257
258 if (ht->free) {
259 for (i = 0; i < ht->num_buckets; i++) {
260 if (ht->buckets[i]) for (node = ht->buckets[i];
261 node != &ht->null;
262 node = next) {
263 next = node->next;
264 if (!node->data) continue; /* dummy entry */
265
266 ht->free(node->data);
267 }
268 }
269 }
270
271 return 0;
272}
273
274/*
275 * Create the table.
276 *
277 * Memory usage in bytes is (20/3) * number of entries.
278 */
280 char const *type,
281 fr_hash_t hash_func,
282 fr_cmp_t cmp_func,
283 fr_free_t free_func)
284{
285 fr_hash_table_t *ht;
286
287 ht = talloc(ctx, fr_hash_table_t);
288 if (!ht) return NULL;
289 talloc_set_destructor(ht, _fr_hash_table_free);
290
291 *ht = (fr_hash_table_t){
292 .type = type,
293 .free = free_func,
294 .hash = hash_func,
295 .cmp = cmp_func,
296 .num_buckets = FR_HASH_NUM_BUCKETS,
297 .mask = FR_HASH_NUM_BUCKETS - 1,
298
299 /*
300 * Have a default load factor of 2.5. In practice this
301 * means that the average load will hit 3 before the
302 * table grows.
303 */
304 .next_grow = (FR_HASH_NUM_BUCKETS << 1) + (FR_HASH_NUM_BUCKETS >> 1),
305 .buckets = talloc_zero_array(ht, fr_hash_entry_t *, FR_HASH_NUM_BUCKETS)
306 };
307 if (unlikely(!ht->buckets)) {
308 talloc_free(ht);
309 return NULL;
310 }
311
312 ht->null.reversed = ~0;
313 ht->null.key = ~0;
314 ht->null.next = &ht->null;
315 ht->buckets[0] = &ht->null;
316
317 return ht;
318}
319
320
321/*
322 * If the current bucket is uninitialized, initialize it
323 * by recursively copying information from the parent.
324 *
325 * We may have a situation where entry E is a parent to 2 other
326 * entries E' and E". If we split E into E and E', then the
327 * nodes meant for E" end up in E or E', either of which is
328 * wrong. To solve that problem, we walk down the whole chain,
329 * inserting the elements into the correct place.
330 */
332{
333 uint32_t parent_entry;
334 fr_hash_entry_t **last, *cur;
335 uint32_t this;
336
337 parent_entry = parent_of(entry);
338
339 /* parent_entry == entry if and only if entry == 0 */
340
341 if (!ht->buckets[parent_entry]) {
342 fr_hash_table_fixup(ht, parent_entry);
343 }
344
345 /*
346 * Keep walking down cur, trying to find entries that
347 * don't belong here any more. There may be multiple
348 * ones, so we can't have a naive algorithm...
349 */
350 last = &ht->buckets[parent_entry];
351 this = parent_entry;
352
353 for (cur = *last; cur != &ht->null; cur = cur->next) {
354 uint32_t real_entry;
355
356 real_entry = cur->key & ht->mask;
357 if (real_entry != this) { /* ht->buckets[real_entry] == NULL */
358 *last = &ht->null;
359 ht->buckets[real_entry] = cur;
360 this = real_entry;
361 }
362
363 last = &(cur->next);
364 }
365
366 /*
367 * We may NOT have initialized this bucket, so do it now.
368 */
369 if (!ht->buckets[entry]) ht->buckets[entry] = &ht->null;
370}
371
372/*
373 * This should be a power of two. Changing it to 4 doesn't seem
374 * to make any difference.
375 */
376#define GROW_FACTOR (2)
377
378/*
379 * Grow the hash table.
380 */
382{
383 fr_hash_entry_t **buckets;
384 size_t existing = talloc_get_size(ht->buckets);
385
386 buckets = talloc_realloc(ht, ht->buckets, fr_hash_entry_t *, GROW_FACTOR * ht->num_buckets);
387 if (!buckets) return;
388
389 memset(((uint8_t *)buckets) + existing, 0, talloc_get_size(buckets) - existing);
390
391 ht->buckets = buckets;
393 ht->next_grow *= GROW_FACTOR;
394 ht->mask = ht->num_buckets - 1;
395#ifdef TESTING
396 grow = 1;
397 fprintf(stderr, "GROW TO %d\n", ht->num_buckets);
398#endif
399}
400
401/*
402 * Internal find a node routine.
403 */
404static inline CC_HINT(always_inline) fr_hash_entry_t *hash_table_find(fr_hash_table_t *ht,
405 uint32_t key, void const *data)
406{
407 uint32_t entry;
408 uint32_t reversed;
409
410 entry = key & ht->mask;
411 reversed = reverse(key);
412
413 if (!ht->buckets[entry]) fr_hash_table_fixup(ht, entry);
414
415 return list_find(ht, ht->buckets[entry], reversed, data);
416}
417
418/** Find data in a hash table
419 *
420 * @param[in] ht to find data in.
421 * @param[in] data to find. Will be passed to the
422 * hashing function.
423 * @return
424 * - The user data we found.
425 * - NULL if we couldn't find any matching data.
426 */
427CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
429{
430 fr_hash_entry_t *node;
431
432 node = hash_table_find(ht, ht->hash(data), data);
433 if (!node) return NULL;
434
435 return UNCONST(void *, node->data);
436}
437
438/** Hash table lookup with pre-computed key
439 *
440 * @param[in] ht to find data in.
441 * @param[in] key the precomputed key.
442 * @param[in] data for list matching.
443 * @return
444 * - The user data we found.
445 * - NULL if we couldn't find any matching data.
446 */
448{
449 fr_hash_entry_t *node;
450
451 node = hash_table_find(ht, key, data);
452 if (!node) return NULL;
453
454 return UNCONST(void *, node->data);
455}
456
457/** Insert data into a hash table
458 *
459 * @param[in] ht to insert data into.
460 * @param[in] data to insert. Will be passed to the
461 * hashing function.
462 * @return
463 * - true if data was inserted.
464 * - false if data already existed and was not inserted.
465 */
466CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
468{
469 uint32_t key;
470 uint32_t entry;
471 uint32_t reversed;
472 fr_hash_entry_t *node;
473
474#ifndef TALLOC_GET_TYPE_ABORT_NOOP
475 if (ht->type) (void)_talloc_get_type_abort(data, ht->type, __location__);
476#endif
477
478 key = ht->hash(data);
479 entry = key & ht->mask;
480 reversed = reverse(key);
481
482 if (!ht->buckets[entry]) fr_hash_table_fixup(ht, entry);
483
484 /*
485 * If we try to do our own memory allocation here, the
486 * speedup is only ~15% or so, which isn't worth it.
487 */
488 node = talloc_zero(ht, fr_hash_entry_t);
489 if (unlikely(!node)) return false;
490
491 node->next = &ht->null;
492 node->reversed = reversed;
493 node->key = key;
494 node->data = UNCONST(void *, data);
495
496 /* already in the table, can't insert it */
497 if (!list_insert(ht, &ht->buckets[entry], node)) {
498 talloc_free(node);
499 return false;
500 }
501
502 /*
503 * Check the load factor, and grow the table if
504 * necessary.
505 */
506 ht->num_elements++;
507 if (ht->num_elements >= ht->next_grow) fr_hash_table_grow(ht);
508
509 return true;
510}
511
512/** Replace old data with new data, OR insert if there is no old
513 *
514 * @param[out] old data that was replaced. If this argument
515 * is not NULL, then the old data will not
516 * be freed, even if a free function is
517 * configured.
518 * @param[in] ht to insert data into.
519 * @param[in] data to replace. Will be passed to the
520 * hashing function.
521 * @return
522 * - 1 if data was replaced.
523 * - 0 if data was inserted.
524 * - -1 if we failed to replace data
525 */
526CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
527int fr_hash_table_replace(void **old, fr_hash_table_t *ht, void const *data)
528{
529 fr_hash_entry_t *node;
530
531 node = hash_table_find(ht, ht->hash(data), data);
532 if (!node) {
533 if (old) *old = NULL;
534 return fr_hash_table_insert(ht, data) ? 1 : -1;
535 }
536
537 if (old) {
538 *old = node->data;
539 } else if (ht->free) {
540 ht->free(node->data);
541 }
542
543 node->data = UNCONST(void *, data);
544
545 return 0;
546}
547
548/** Remove an entry from the hash table, without freeing the data
549 *
550 * @param[in] ht to remove data from.
551 * @param[in] data to remove. Will be passed to the
552 * hashing function.
553 * @return
554 * - The user data we removed.
555 * - NULL if we couldn't find any matching data.
556 */
557CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
559{
560 uint32_t key;
561 uint32_t entry;
562 uint32_t reversed;
563 void *old;
564 fr_hash_entry_t *node;
565
566 key = ht->hash(data);
567 entry = key & ht->mask;
568 reversed = reverse(key);
569
570 if (!ht->buckets[entry]) fr_hash_table_fixup(ht, entry);
571
572 node = list_find(ht, ht->buckets[entry], reversed, data);
573 if (!node) return NULL;
574
575 list_delete(ht, &ht->buckets[entry], node);
576 ht->num_elements--;
577
578 old = node->data;
579 talloc_free(node);
580
581 return old;
582}
583
584/** Remove and free data (if a free function was specified)
585 *
586 * @param[in] ht to remove data from.
587 * @param[in] data to remove/free.
588 * @return
589 * - true if we removed data.
590 * - false if we couldn't find any matching data.
591 */
592CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
594{
595 void *old;
596
597 old = fr_hash_table_remove(ht, data);
598 if (!old) return false;
599
600 if (ht->free) ht->free(old);
601
602 return true;
603}
604
605/*
606 * Count number of elements
607 */
608CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
610{
611 return ht->num_elements;
612}
613
614/** Iterate over entries in a hash table
615 *
616 * @note If the hash table is modified the iterator should be considered invalidated.
617 *
618 * @param[in] ht to iterate over.
619 * @param[in] iter Pointer to an iterator struct, used to maintain
620 * state between calls.
621 * @return
622 * - User data.
623 * - NULL if at the end of the list.
624 */
626{
627 fr_hash_entry_t *node;
628 uint32_t i;
629
630 /*
631 * Return the next element in the bucket
632 */
633 if (iter->node != &ht->null) {
634 node = iter->node;
635 iter->node = node->next;
636
637 return node->data;
638 }
639
640 if (iter->bucket == 0) return NULL;
641
642 /*
643 * We might have to go through multiple empty
644 * buckets to find one that contains something
645 * we should return
646 */
647 i = iter->bucket - 1;
648 for (;;) {
649 if (!ht->buckets[i]) fr_hash_table_fixup(ht, i);
650
651 node = ht->buckets[i];
652 if (node == &ht->null) {
653 if (i == 0) break;
654 i--;
655 continue; /* This bucket was empty too... */
656 }
657
658 iter->node = node->next; /* Store the next one to examine */
659 iter->bucket = i;
660 return node->data;
661 }
662 iter->bucket = i;
663
664 return NULL;
665}
666
667/** Initialise an iterator
668 *
669 * @note If the hash table is modified the iterator should be considered invalidated.
670 *
671 * @param[in] ht to iterate over.
672 * @param[out] iter to initialise.
673 * @return
674 * - The first entry in the hash table.
675 * - NULL if the hash table is empty.
676 */
678{
679 iter->bucket = ht->num_buckets;
680 iter->node = &ht->null;
681
682 return fr_hash_table_iter_next(ht, iter);
683}
684
685/** Copy all entries out of a hash table into an array
686 *
687 * @param[in] ctx to allocate array in.
688 * @param[in] out array of hash table entries.
689 * @param[in] ht to flatter.
690 * @return
691 * - 0 on success.
692 * - -1 on failure.
693 */
694int fr_hash_table_flatten(TALLOC_CTX *ctx, void **out[], fr_hash_table_t *ht)
695{
696 uint64_t num = fr_hash_table_num_elements(ht), i;
697 fr_hash_iter_t iter;
698 void *item, **list;
699
700 if (unlikely(!(list = talloc_array(ctx, void *, num)))) return -1;
701
702 for (item = fr_hash_table_iter_init(ht, &iter), i = 0;
703 item;
704 item = fr_hash_table_iter_next(ht, &iter), i++) list[i] = item;
705
706 *out = list;
707
708 return 0;
709}
710
711/** Ensure all buckets are filled
712 *
713 * This must be called if the table will be read by multiple threads without
714 * synchronisation. Synchronisation is still required for updates.
715 *
716 * @param[in] ht to fill.
717 */
719{
720 int i;
721
722 for (i = ht->num_buckets - 1; i >= 0; i--) if (!ht->buckets[i]) fr_hash_table_fixup(ht, i);
723}
724
725#ifdef TESTING
726/*
727 * Show what the hash table is doing.
728 */
729int fr_hash_table_info(fr_hash_table_t *ht)
730{
731 int i, a, collisions, uninitialized;
732 int array[256];
733
734 if (!ht) return 0;
735
736 uninitialized = collisions = 0;
737 memset(array, 0, sizeof(array));
738
739 for (i = 0; i < ht->num_buckets; i++) {
740 uint32_t key;
741 int load;
742 fr_hash_entry_t *node, *next;
743
744 /*
745 * If we haven't inserted or looked up an entry
746 * in a bucket, it's uninitialized.
747 */
748 if (!ht->buckets[i]) {
749 uninitialized++;
750 continue;
751 }
752
753 load = 0;
754 key = ~0;
755 for (node = ht->buckets[i]; node != &ht->null; node = next) {
756 if (node->reversed == key) {
757 collisions++;
758 } else {
759 key = node->reversed;
760 }
761 next = node->next;
762 load++;
763 }
764
765 if (load > 255) load = 255;
766 array[load]++;
767 }
768
769 printf("HASH TABLE %p\tbuckets: %d\t(%d uninitialized)\n", ht,
770 ht->num_buckets, uninitialized);
771 printf("\tnum entries %d\thash collisions %d\n",
772 ht->num_elements, collisions);
773
774 a = 0;
775 for (i = 1; i < 256; i++) {
776 if (!array[i]) continue;
777 printf("%d\t%d\n", i, array[i]);
778
779 /*
780 * Since the entries are ordered, the lookup cost
781 * for any one element in a chain is (on average)
782 * the cost of walking half of the chain.
783 */
784 if (i > 1) {
785 a += array[i] * i;
786 }
787 }
788 a /= 2;
789 a += array[1];
790
791 printf("\texpected lookup cost = %d/%d or %f\n\n",
792 ht->num_elements, a,
793 (float) ht->num_elements / (float) a);
794
795 return 0;
796}
797#endif
798
799
800#define FNV_MAGIC_INIT (0x811c9dc5)
801#define FNV_MAGIC_PRIME (0x01000193)
802
803/*
804 * A fast hash function. For details, see:
805 *
806 * http://www.isthe.com/chongo/tech/comp/fnv/
807 *
808 * Which also includes public domain source. We've re-written
809 * it here for our purposes.
810 */
811uint32_t fr_hash(void const *data, size_t size)
812{
813 uint8_t const *p = data;
814 uint8_t const *q = p + size;
816
817 /*
818 * FNV-1 hash each octet in the buffer
819 */
820 while (p != q) {
821 /*
822 * XOR the 8-bit quantity into the bottom of
823 * the hash.
824 */
825 hash ^= (uint32_t) (*p++);
826
827 /*
828 * Multiple by 32-bit magic FNV prime, mod 2^32
829 */
831#if 0
832 /*
833 * Potential optimization.
834 */
835 hash += (hash<<1) + (hash<<4) + (hash<<7) + (hash<<8) + (hash<<24);
836#endif
837 }
838
839 return hash;
840}
841
842/*
843 * Continue hashing data.
844 */
845uint32_t fr_hash_update(void const *data, size_t size, uint32_t hash)
846{
847 uint8_t const *p = data;
848 uint8_t const *q;
849
850 if (size == 0) return hash; /* Avoid ubsan issues with access NULL pointer */
851
852 q = p + size;
853 while (p < q) {
855 hash ^= (uint32_t) (*p++);
856 }
857
858 return hash;
859}
860
861/*
862 * Hash a C string, so we loop over it once.
863 */
865{
867
868 while (*p) {
869 /* coverity[overflow_const] */
871 hash ^= (uint32_t) (*p++);
872 }
873
874 return hash;
875}
876
877/** Hash a C string, converting all chars to lowercase
878 *
879 */
881{
883
884 while (*p) {
885 /* coverity[overflow_const] */
887 hash ^= (uint32_t) (tolower((uint8_t) *p++));
888 }
889
890 return hash;
891}
892
893/** Check hash table is sane
894 *
895 */
897{
898 fr_hash_iter_t iter;
899 void *ptr;
900
901 (void)talloc_get_type_abort(ht, fr_hash_table_t);
902 (void)talloc_get_type_abort(ht->buckets, fr_hash_entry_t *);
903
904 fr_assert(talloc_array_length(ht->buckets) == ht->num_buckets);
905
906 /*
907 * Check talloc headers on all data
908 */
909 if (ht->type) {
910 for (ptr = fr_hash_table_iter_init(ht, &iter);
911 ptr;
912 ptr = fr_hash_table_iter_next(ht, &iter)) {
913 (void)_talloc_get_type_abort(ptr, ht->type, __location__);
914 }
915 }
916}
917
918#ifdef TESTING
919/*
920 * cc -g -DTESTING -I ../include hash.c -o hash
921 *
922 * ./hash
923 */
924static uint32_t hash_int(void const *data)
925{
926 return fr_hash((int *) data, sizeof(int));
927}
928
929#define MAX 1024*1024
930int main(int argc, char **argv)
931{
932 int i, *p, *q, k;
933 fr_hash_table_t *ht;
934 int *array;
935
936 ht = fr_hash_table_alloc(NULL, hash_int, NULL, NULL);
937 if (!ht) {
938 fprintf(stderr, "Hash create failed\n");
939 fr_exit(1);
940 }
941
942 array = talloc_zero_array(NULL, int, MAX);
943 if (!array) fr_exit(1);
944
945 for (i = 0; i < MAX; i++) {
946 p = array + i;
947 *p = i;
948
949 if (!fr_hash_table_insert(ht, p)) {
950 fprintf(stderr, "Failed insert %08x\n", i);
951 fr_exit(1);
952 }
953#ifdef TEST_INSERT
954 q = fr_hash_table_find(ht, p);
955 if (q != p) {
956 fprintf(stderr, "Bad data %d\n", i);
957 fr_exit(1);
958 }
959#endif
960 }
961
962 fr_hash_table_info(ht);
963
964 /*
965 * Build this to see how lookups result in shortening
966 * of the hash chains.
967 */
968 if (1) {
969 for (i = 0; i < MAX ; i++) {
970 q = fr_hash_table_find(ht, &i);
971 if (!q || *q != i) {
972 fprintf(stderr, "Failed finding %d\n", i);
973 fr_exit(1);
974 }
975
976#if 0
977 if (!fr_hash_table_delete(ht, &i)) {
978 fprintf(stderr, "Failed deleting %d\n", i);
979 fr_exit(1);
980 }
981 q = fr_hash_table_find(ht, &i);
982 if (q) {
983 fprintf(stderr, "Failed to delete %08x\n", i);
984 fr_exit(1);
985 }
986#endif
987 }
988
989 fr_hash_table_info(ht);
990 }
991
992 fr_hash_table_free(ht);
993 talloc_free(array);
994
995 return EXIT_SUCCESS;
996}
997#endif
#define load(_var)
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:167
#define RCSID(id)
Definition build.h:485
#define CC_NO_UBSAN(_sanitize)
Definition build.h:428
#define unlikely(_x)
Definition build.h:383
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition debug.h:220
int main(int argc, char **argv)
Definition dhcpclient.c:531
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:625
void * fr_hash_table_find(fr_hash_table_t *ht, void const *data)
Find data in a hash table.
Definition hash.c:428
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:677
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:880
static int _fr_hash_table_free(fr_hash_table_t *ht)
Definition hash.c:253
uint32_t fr_hash_update(void const *data, size_t size, uint32_t hash)
Definition hash.c:845
static uint8_t parent_byte[256]
Definition hash.c:112
uint32_t fr_hash(void const *data, size_t size)
Definition hash.c:811
bool fr_hash_table_insert(fr_hash_table_t *ht, void const *data)
Insert data into a hash table.
Definition hash.c:467
void * fr_hash_table_find_by_key(fr_hash_table_t *ht, uint32_t key, void const *data)
Hash table lookup with pre-computed key.
Definition hash.c:447
fr_hash_entry_t null
Definition hash.c:62
#define FNV_MAGIC_PRIME
Definition hash.c:801
fr_hash_t hash
Hashing function.
Definition hash.c:57
static fr_hash_entry_t * hash_table_find(fr_hash_table_t *ht, uint32_t key, void const *data)
Definition hash.c:404
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
void * fr_hash_table_remove(fr_hash_table_t *ht, void const *data)
Remove an entry from the hash table, without freeing the data.
Definition hash.c:558
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:694
uint32_t mask
Definition hash.c:54
static void list_delete(fr_hash_table_t *ht, fr_hash_entry_t **head, fr_hash_entry_t *node)
Definition hash.c:238
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
static fr_hash_entry_t * list_find(fr_hash_table_t *ht, fr_hash_entry_t *head, uint32_t reversed, void const *data)
Definition hash.c:184
uint32_t fr_hash_string(char const *p)
Definition hash.c:864
bool 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:593
#define FNV_MAGIC_INIT
Definition hash.c:800
void fr_hash_table_verify(fr_hash_table_t *ht)
Check hash table is sane.
Definition hash.c:896
fr_free_t free
Data free function.
Definition hash.c:56
static bool list_insert(fr_hash_table_t *ht, fr_hash_entry_t **head, fr_hash_entry_t *node)
Definition hash.c:208
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:279
void fr_hash_table_fill(fr_hash_table_t *ht)
Ensure all buckets are filled.
Definition hash.c:718
uint32_t key
Definition hash.c:46
static void fr_hash_table_fixup(fr_hash_table_t *ht, uint32_t entry)
Definition hash.c:331
#define GROW_FACTOR
Definition hash.c:376
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:527
uint32_t fr_hash_table_num_elements(fr_hash_table_t *ht)
Definition hash.c:609
#define FR_HASH_NUM_BUCKETS
Definition hash.c:41
static void fr_hash_table_grow(fr_hash_table_t *ht)
Definition hash.c:381
Definition hash.c:43
struct fr_hash_table_s fr_hash_table_t
Definition hash.h:55
uint32_t(* fr_hash_t)(void const *)
Definition hash.h:36
fr_hash_entry_t * node
Definition hash.h:43
#define fr_hash_table_alloc(_ctx, _hash_node, _cmp_node, _free_node)
Definition hash.h:58
uint32_t bucket
Definition hash.h:42
Stores the state of the current iteration operation.
Definition hash.h:41
talloc_free(reap)
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
Definition lst.c:122
unsigned int uint32_t
unsigned char bool
unsigned char uint8_t
int8_t(* fr_cmp_t)(void const *a, void const *b)
Definition misc.h:38
void(* fr_free_t)(void *)
Definition misc.h:39
#define fr_assert(_expr)
Definition rad_assert.h:38
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:420
static fr_slen_t data
Definition value.h:1293
static size_t char ** out
Definition value.h:1023