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