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