2 * hash.c Non-thread-safe split-ordered hash table.
4 * The weird "reverse" function is based on an idea from
5 * "Split-Ordered Lists - Lock-free Resizable Hash Tables", with
6 * modifications so that they're not lock-free. :(
8 * However, the split-order idea allows a fast & easy splitting of the
9 * hash bucket chain when the hash table is resized. Without it, we'd
10 * have to check & update the pointers for every node in the buck chain,
11 * rather than being able to move 1/2 of the entries in the chain with
16 * This library is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Lesser General Public
18 * License as published by the Free Software Foundation; either
19 * version 2.1 of the License, or (at your option) any later version.
21 * This library is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Lesser General Public License for more details.
26 * You should have received a copy of the GNU Lesser General Public
27 * License along with this library; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
30 * Copyright 2005 The FreeRADIUS server project
33 static const char rcsid[] = "$Id$";
41 #include "libradius.h"
43 typedef struct lrad_hash_entry_t {
44 struct lrad_hash_entry_t *next;
45 uint32_t key; /* reversed image of the key */
49 struct lrad_hash_table_t {
51 int num_buckets; /* power of 2 */
55 lrad_hash_entry_t **buckets;
64 * 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";}}'
66 static const uint8_t reversed_byte[256] = {
67 0, 128, 64, 192, 32, 160, 96, 224,
68 16, 144, 80, 208, 48, 176, 112, 240,
69 8, 136, 72, 200, 40, 168, 104, 232,
70 24, 152, 88, 216, 56, 184, 120, 248,
71 4, 132, 68, 196, 36, 164, 100, 228,
72 20, 148, 84, 212, 52, 180, 116, 244,
73 12, 140, 76, 204, 44, 172, 108, 236,
74 28, 156, 92, 220, 60, 188, 124, 252,
75 2, 130, 66, 194, 34, 162, 98, 226,
76 18, 146, 82, 210, 50, 178, 114, 242,
77 10, 138, 74, 202, 42, 170, 106, 234,
78 26, 154, 90, 218, 58, 186, 122, 250,
79 6, 134, 70, 198, 38, 166, 102, 230,
80 22, 150, 86, 214, 54, 182, 118, 246,
81 14, 142, 78, 206, 46, 174, 110, 238,
82 30, 158, 94, 222, 62, 190, 126, 254,
83 1, 129, 65, 193, 33, 161, 97, 225,
84 17, 145, 81, 209, 49, 177, 113, 241,
85 9, 137, 73, 201, 41, 169, 105, 233,
86 25, 153, 89, 217, 57, 185, 121, 249,
87 5, 133, 69, 197, 37, 165, 101, 229,
88 21, 149, 85, 213, 53, 181, 117, 245,
89 13, 141, 77, 205, 45, 173, 109, 237,
90 29, 157, 93, 221, 61, 189, 125, 253,
91 3, 131, 67, 195, 35, 163, 99, 227,
92 19, 147, 83, 211, 51, 179, 115, 243,
93 11, 139, 75, 203, 43, 171, 107, 235,
94 27, 155, 91, 219, 59, 187, 123, 251,
95 7, 135, 71, 199, 39, 167, 103, 231,
96 23, 151, 87, 215, 55, 183, 119, 247,
97 15, 143, 79, 207, 47, 175, 111, 239,
98 31, 159, 95, 223, 63, 191, 127, 255
105 static uint32_t reverse(uint32_t key)
107 return ((reversed_byte[key & 0xff] << 24) |
108 (reversed_byte[(key >> 8) & 0xff] << 16) |
109 (reversed_byte[(key >> 16) & 0xff] << 8) |
110 (reversed_byte[(key >> 24) & 0xff]));
113 static lrad_hash_entry_t *list_find(lrad_hash_entry_t *head, uint32_t key)
115 lrad_hash_entry_t *cur;
117 for (cur = head; cur != NULL; cur = cur->next) {
118 if (cur->key > key) return NULL;
119 if (cur->key == key) return cur;
126 * Inserts a new entry into the list, in order.
128 static int list_insert(lrad_hash_entry_t **head, lrad_hash_entry_t *node)
130 lrad_hash_entry_t **last, *cur;
134 for (cur = *head; cur != NULL; cur = cur->next) {
135 if (cur->key > node->key) break;
147 * Delete an entry from the list.
149 static int list_delete(lrad_hash_entry_t **head, lrad_hash_entry_t *node)
151 lrad_hash_entry_t **last, *cur;
155 for (cur = *head; cur != NULL; cur = cur->next) {
156 if (cur == node) break;
166 * Split a list. Everything >= key is returned, and the returned
167 * list is removed from the input list.
169 static lrad_hash_entry_t *list_split(lrad_hash_entry_t **head, uint32_t key)
171 lrad_hash_entry_t **last, *cur;
175 for (cur = *head; cur != NULL; cur = cur->next) {
176 if (cur->key >= key) break;
187 * Create the table. Size is a power of two (i.e. 1..31)
189 lrad_hash_table_t *lrad_hash_table_create(int size, void (*freeNode)(void *),
192 lrad_hash_table_t *ht;
194 if ((size <= 1) || (size > 31)) return NULL;
197 * Get the nearest power of two.
201 ht = malloc(sizeof(*ht));
202 if (!ht) return NULL;
204 memset(ht, 0, sizeof(*ht));
206 ht->num_buckets = size;
207 ht->replace_flag = replace_flag;
209 ht->buckets = malloc(sizeof(*ht->buckets) * ht->num_buckets);
214 memset(ht->buckets, 0, sizeof(*ht->buckets) * ht->num_buckets);
220 * Insert data, OR copy it, if ht->data_size != 0
222 int lrad_hash_table_insert(lrad_hash_table_t *ht, uint32_t key, void *data)
226 lrad_hash_entry_t *node;
228 if (!ht || !data) return 0;
230 entry = key & (ht->num_buckets - 1);
231 reversed = reverse(key);
234 * Already in the table.
236 node = list_find(ht->buckets[entry], reversed);
238 if (!ht->replace_flag) return 0;
240 list_delete(&ht->buckets[entry], node);
242 if (ht->free && node->data) ht->free(node->data);
245 * Fall through to re-using the node.
248 node = malloc(sizeof(*node) + ht->data_size);
251 memset(node, 0, sizeof(*node) + ht->data_size);
254 node->key = reversed;
256 node->data = &node[1]; /* point to the end of the node */
257 memcpy(node->data, data, ht->data_size);
262 list_insert(&(ht->buckets[entry]), node);
266 * Check the load factor, and grow the table if
269 if (ht->num_elements >= (3 * ht->num_buckets)) {
271 lrad_hash_entry_t **buckets;
273 buckets = malloc(sizeof(*buckets) * 2 * ht->num_buckets);
274 if (!buckets) return 1;
276 memcpy(buckets, ht->buckets,
277 sizeof(*buckets) * ht->num_buckets);
280 * Split the hash entries.
282 * When we double the size of the hash array, we
283 * do O(N/2) work. Since this only happens after
284 * we've inserted N elements, we're still amortized
285 * at O(1) inserts, deletes, and updates.
287 for (i = 0; i < ht->num_buckets; i++) {
288 buckets[ht->num_buckets + i] = list_split(&buckets[i],
289 reverse((uint32_t) i + ht->num_buckets));
292 ht->buckets = buckets;
293 ht->num_buckets *= 2;
296 fprintf(stderr, "GROW TO %d\n", ht->num_buckets);
305 * Find data from a key.
307 void *lrad_hash_table_finddata(lrad_hash_table_t *ht, uint32_t key)
311 lrad_hash_entry_t *node;
313 entry = key & (ht->num_buckets - 1);
314 reversed = reverse(key);
316 if (!ht) return NULL;
318 node = list_find(ht->buckets[entry], reversed);
319 if (!node) return NULL;
321 return node->data; /* may be NULL */
326 * Delete a piece of data from the hash table.
328 int lrad_hash_table_delete(lrad_hash_table_t *ht, uint32_t key)
332 lrad_hash_entry_t *node;
336 entry = key & (ht->num_buckets - 1);
337 reversed = reverse(key);
339 node = list_find(ht->buckets[entry], reversed);
342 if (ht->free) ht->free(node->data);
343 list_delete(&ht->buckets[entry], node);
354 void lrad_hash_table_free(lrad_hash_table_t *ht)
356 lrad_hash_entry_t *node, *next;
361 * The entries MUST be all in one linked list.
363 for (node = ht->buckets[0]; node != NULL; node = next) {
366 if (!node->data) continue; /* dummy entry */
368 if (ht->free) ht->free(node->data);
378 * Count number of elements
380 int lrad_hash_table_num_elements(lrad_hash_table_t *ht)
384 return ht->num_elements;
389 * Walk over the nodes, allowing deletes & inserts to happen.
391 int lrad_hash_table_walk(lrad_hash_table_t *ht,
392 int (*callback)(void * /* ctx */,
398 if (!ht || !callback) return 0;
400 for (i = 0; i < ht->num_buckets; i++) {
401 lrad_hash_entry_t *node, *next;
403 if (!ht->buckets[i]) continue;
405 for (node = ht->buckets[i]; node != NULL; node = next) {
408 rcode = callback(context, node->data);
409 if (rcode != 0) return rcode;
418 * For users that have a small amount of data, and wish to associate
419 * it directly with the hash entry, this saves a bit of memory &
422 int lrad_hash_table_set_data_size(lrad_hash_table_t *ht, size_t data_size)
424 if (!ht || ht->free) return 0;
426 if (ht->num_elements != 0) return 0;
428 ht->data_size = data_size;
434 #define FNV_MAGIC_INIT (0x811c9dc5)
435 #define FNV_MAGIC_PRIME (0x01000193)
438 * A fast hash function. For details, see:
440 * http://www.isthe.com/chongo/tech/comp/fnv/
442 * Which also includes public domain source. We've re-written
443 * it here for our purposes.
445 uint32_t lrad_hash(const void *data, size_t size)
447 const uint8_t *p = data;
448 const uint8_t *q = p + size;
449 uint32_t hash = FNV_MAGIC_INIT;
452 * FNV-1 hash each octet in the buffer
456 * Multiple by 32-bit magic FNV prime, mod 2^32
458 hash *= FNV_MAGIC_PRIME;
461 * Potential optimization.
463 hash += (hash<<1) + (hash<<4) + (hash<<7) + (hash<<8) + (hash<<24);
466 * XOR the 8-bit quantity into the bottom of
469 hash ^= (uint32_t) (*p++);
476 * Continue hashing data.
478 uint32_t lrad_hash_update(const void *data, size_t size, uint32_t hash)
480 const uint8_t *p = data;
481 const uint8_t *q = p + size;
484 hash *= FNV_MAGIC_PRIME;
485 hash ^= (uint32_t) (*p++);
493 * Return a "folded" hash, where the lower "bits" are the
494 * hash, and the upper bits are zero.
496 * If you need a non-power-of-two hash, cope.
498 uint32_t lrad_hash_fold(uint32_t hash, int bits)
503 if ((bits <= 0) || (bits >= 32)) return hash;
508 * Never use the same bits twice in an xor.
510 for (count = 0; count < 32; count += bits) {
515 return result & (((uint32_t) (1 << bits)) - 1);
521 * cc -DTESTING -I ../include/ hash.c -o hash
530 int main(int argc, char **argv)
533 lrad_hash_table_t *ht;
535 ht = lrad_hash_table_create(10, free, 0);
537 fprintf(stderr, "Hash create failed\n");
541 for (i = 0; i < MAX; i++) {
542 p = malloc(sizeof(i));
544 if (!lrad_hash_table_insert(ht, i, p)) {
545 fprintf(stderr, "Failed insert %08x\n", i);
552 for (j = 0; j < i; j++) {
553 q = lrad_hash_table_finddata(ht, j);
554 if (!q || (*q != j)) {
555 fprintf(stderr, "BAD DATA %d %p\n",
563 q = lrad_hash_table_finddata(ht, i);
565 fprintf(stderr, "Bad data %d\n", i);
570 for (i = 0; i < MAX; i++) {
571 lrad_hash_table_delete(ht, i);
572 q = lrad_hash_table_finddata(ht, i);
574 fprintf(stderr, "Failed to delete %08x\n", i);
579 lrad_hash_table_free(ht);