p = name;
q = buffer;
while (*p && (len < sizeof(buffer))) {
- if (isalpha((int) *p)) {
+ if (isalpha(*p)) {
*(q++) = tolower((int) *(p++));
} else {
*(q++) = *(p++);
dv->vendorpec = value;
dv->type = dv->length = 1; /* defaults */
- if (lrad_hash_table_insert(vendors_byname, hash, dv) == 0) {
+ if (!lrad_hash_table_insert(vendors_byname, hash, dv)) {
DICT_VENDOR *old_dv;
old_dv = lrad_hash_table_finddata(vendors_byname, hash);
* Insert the SAME pointer (not free'd when this tree is
* deleted), into another tree.
*
- * If the newly inserted entry is a duplicate of an existing
- * entry, then the old entry is tossed, and the new one
- * replaces it. This behaviour is configured in the
- * lrad_hash_table_create() function.
- *
* We want this behaviour because we want OLD names for
* the attributes to be read from the configuration
* files, but when we're printing them, (and looking up
* by value) we want to use the NEW name.
*/
- lrad_hash_table_insert(vendors_byvalue, dv->vendorpec, dv);
+ if (!lrad_hash_table_replace(vendors_byvalue,
+ lrad_hash(&dv->vendorpec,
+ sizeof(dv->vendorpec)),
+ dv)) {
+ librad_log("dict_addvendor: Failed inserting vendor %s",
+ name);
+ return -1;
+ }
return 0;
}
/*
* Insert the attribute, only if it's not a duplicate.
*/
- if (lrad_hash_table_insert(attributes_byname, hash, attr) == 0) {
+ if (!lrad_hash_table_insert(attributes_byname, hash, attr)) {
DICT_ATTR *a;
/*
* Insert the SAME pointer (not free'd when this tree is
* deleted), into another tree.
*
- * If the newly inserted entry is a duplicate of an existing
- * entry, then the old entry is tossed, and the new one
- * replaces it. This behaviour is configured in the
- * lrad_hash_table_create() function.
- *
* We want this behaviour because we want OLD names for
* the attributes to be read from the configuration
* files, but when we're printing them, (and looking up
* by value) we want to use the NEW name.
*/
- lrad_hash_table_insert(attributes_byvalue, (uint32_t) attr->attr, attr);
+ if (!lrad_hash_table_replace(attributes_byvalue,
+ lrad_hash(&attr->attr, sizeof(attr->attr)),
+ attr)) {
+ librad_log("dict_addattr: Failed inserting attribute name %s", name);
+ return -1;
+ }
return 0;
}
/*
* Add the value into the dictionary.
*/
- if (lrad_hash_table_insert(values_byname, hash, dval) == 0) {
+ if (!lrad_hash_table_insert(values_byname, hash, dval)) {
if (dattr) {
DICT_VALUE *old;
*/
hash = dval->attr;
hash = lrad_hash_update(&dval->value, sizeof(dval->value), hash);
- lrad_hash_table_insert(values_byvalue, hash, dval);
+ if (!lrad_hash_table_replace(values_byvalue, hash, dval)) {
+ librad_log("dict_addvalue: Failed inserting value %s",
+ namestr);
+ return -1;
+ }
return 0;
}
* if it sees values which look like integers, so we can't
* use them here.
*/
- if (isdigit((int) argv[1][0])) {
+ if (isdigit(argv[1][0])) {
librad_log("dict_init: %s[%d]: Names for VALUEs cannot start with a digit.",
fn, line);
}
*
* Each vendor is malloc'd, so the free function is free.
*/
- vendors_byname = lrad_hash_table_create(8, free, 0);
+ vendors_byname = lrad_hash_table_create(free);
if (!vendors_byname) {
return -1;
}
* be vendors of the same value. If there are, we
* pick the latest one.
*/
- vendors_byvalue = lrad_hash_table_create(8, NULL, 1);
+ vendors_byvalue = lrad_hash_table_create(NULL);
if (!vendors_byvalue) {
return -1;
}
*
* Each attribute is malloc'd, so the free function is free.
*/
- attributes_byname = lrad_hash_table_create(11, free, 0);
+ attributes_byname = lrad_hash_table_create(free);
if (!attributes_byname) {
return -1;
}
* be attributes of the same value. If there are, we
* pick the latest one.
*/
- attributes_byvalue = lrad_hash_table_create(11, NULL, 1);
+ attributes_byvalue = lrad_hash_table_create(NULL);
if (!attributes_byvalue) {
return -1;
}
- values_byname = lrad_hash_table_create(11, free, 0);
+ values_byname = lrad_hash_table_create(free);
if (!values_byname) {
return -1;
}
- values_byvalue = lrad_hash_table_create(11, NULL, 1);
+ values_byvalue = lrad_hash_table_create(NULL);
if (!values_byvalue) {
return -1;
}
hash = lrad_hash_update(&this->dval->attr,
sizeof(this->dval->attr),
this->hash);
- if (lrad_hash_table_insert(values_byname,
- hash, this->dval) == 0) {
+ if (!lrad_hash_table_replace(values_byname,
+ hash, this->dval)) {
librad_log("dict_addvalue: Duplicate value name %s for attribute %s", this->dval->name, a->name);
return -1;
}
* prefer the new name when printing
* values.
*/
- hash = (uint32_t) this->dval->attr;
+ hash = lrad_hash(&this->dval->attr,
+ sizeof(this->dval->attr));
hash = lrad_hash_update(&this->dval->value,
sizeof(this->dval->value),
hash);
if (!lrad_hash_table_finddata(values_byvalue, hash)) {
- lrad_hash_table_insert(values_byvalue,
- hash, this->dval);
+ lrad_hash_table_replace(values_byvalue,
+ hash, this->dval);
}
free(this);
*/
DICT_ATTR *dict_attrbyvalue(int val)
{
- return lrad_hash_table_finddata(attributes_byvalue, (uint32_t) val);
+ return lrad_hash_table_finddata(attributes_byvalue,
+ lrad_hash(&val, sizeof(val)));
}
/*
*/
DICT_VENDOR *dict_vendorbyvalue(int vendor)
{
- return lrad_hash_table_finddata(vendors_byvalue, (uint32_t) vendor);
+ return lrad_hash_table_finddata(vendors_byvalue,
+ lrad_hash(&vendor, sizeof(vendor)));
}
#include <freeradius-devel/missing.h>
#include <freeradius-devel/libradius.h>
+/*
+ * A reasonable number of buckets to start off with.
+ * Should be a power of two.
+ */
+#define LRAD_HASH_NUM_BUCKETS (64)
+
typedef struct lrad_hash_entry_t {
struct lrad_hash_entry_t *next;
- uint32_t key; /* reversed image of the key */
- void *data;
+ uint32_t reversed;
+ uint32_t key;
+ void *data;
} lrad_hash_entry_t;
+
struct lrad_hash_table_t {
int num_elements;
int num_buckets; /* power of 2 */
- int replace_flag;
- size_t data_size;
- void (*free)(void *);
+ int next_grow;
+ int mask;
+
+ lrad_hash_table_free_t free;
+
+ lrad_hash_entry_t null;
+
lrad_hash_entry_t **buckets;
};
static int grow = 0;
#endif
-
/*
* 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";}}'
*/
/*
+ * 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";}}'
+ */
+static uint8_t parent_byte[256] = {
+ 0, 0, 0, 1, 0, 1, 2, 3,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127
+};
+
+
+/*
* Reverse a key.
*/
static uint32_t reverse(uint32_t key)
(reversed_byte[(key >> 24) & 0xff]));
}
-static lrad_hash_entry_t *list_find(lrad_hash_entry_t *head, uint32_t key)
+/*
+ * Take the parent by discarding the highest bit that is set.
+ */
+static uint32_t parent_of(uint32_t key)
+{
+ if (key > 0x00ffffff)
+ return (key & 0x00ffffff) | (parent_byte[key >> 24] << 24);
+
+ if (key > 0x0000ffff)
+ return (key & 0x0000ffff) | (parent_byte[key >> 16] << 16);
+
+ if (key > 0x000000ff)
+ return (key & 0x000000ff) | (parent_byte[key >> 8] << 8);
+
+ return parent_byte[key];
+}
+
+
+static lrad_hash_entry_t *list_find(lrad_hash_table_t *ht,
+ lrad_hash_entry_t *head,
+ uint32_t reversed)
{
lrad_hash_entry_t *cur;
- for (cur = head; cur != NULL; cur = cur->next) {
- if (cur->key > key) return NULL;
- if (cur->key == key) return cur;
+ for (cur = head; cur != &ht->null; cur = cur->next) {
+ if (cur->reversed == reversed) return cur;
+ if (cur->reversed > reversed) return NULL;
}
return NULL;
}
+
/*
* Inserts a new entry into the list, in order.
*/
-static int list_insert(lrad_hash_entry_t **head, lrad_hash_entry_t *node)
+static int list_insert(lrad_hash_table_t *ht,
+ lrad_hash_entry_t **head, lrad_hash_entry_t *node)
{
lrad_hash_entry_t **last, *cur;
last = head;
- for (cur = *head; cur != NULL; cur = cur->next) {
- if (cur->key > node->key) break;
+ for (cur = *head; cur != &ht->null; cur = cur->next) {
+ if (cur->reversed > node->reversed) break;
last = &(cur->next);
+
+ if (cur->reversed == node->reversed) return 0;
}
node->next = *last;
/*
* Delete an entry from the list.
*/
-static int list_delete(lrad_hash_entry_t **head, lrad_hash_entry_t *node)
+static int list_delete(lrad_hash_table_t *ht,
+ lrad_hash_entry_t **head, lrad_hash_entry_t *node)
{
lrad_hash_entry_t **last, *cur;
last = head;
-
- for (cur = *head; cur != NULL; cur = cur->next) {
+
+ for (cur = *head; cur != &ht->null; cur = cur->next) {
if (cur == node) break;
last = &(cur->next);
}
/*
- * Split a list. Everything >= key is returned, and the returned
- * list is removed from the input list.
+ * Create the table.
+ *
+ * Memory usage in bytes is (20/3) * number of entries.
*/
-static lrad_hash_entry_t *list_split(lrad_hash_entry_t **head, uint32_t key)
+lrad_hash_table_t *lrad_hash_table_create(lrad_hash_table_free_t freeNode)
{
- lrad_hash_entry_t **last, *cur;
+ lrad_hash_table_t *ht;
- last = head;
-
- for (cur = *head; cur != NULL; cur = cur->next) {
- if (cur->key >= key) break;
- last = &(cur->next);
+ ht = malloc(sizeof(*ht));
+ if (!ht) return NULL;
+
+ memset(ht, 0, sizeof(*ht));
+ ht->free = freeNode;
+ ht->num_buckets = LRAD_HASH_NUM_BUCKETS;
+ ht->mask = ht->num_buckets - 1;
+
+ /*
+ * Have a default load factor of 2.5. In practice this
+ * means that the average load will hit 3 before the
+ * table grows.
+ */
+ ht->next_grow = (ht->num_buckets << 1) + (ht->num_buckets >> 1);
+
+ ht->buckets = malloc(sizeof(*ht->buckets) * ht->num_buckets);
+ if (!ht->buckets) {
+ free(ht);
+ return NULL;
}
+ memset(ht->buckets, 0, sizeof(*ht->buckets) * ht->num_buckets);
- *last = NULL;
+ ht->null.reversed = ~0;
+ ht->null.key = ~0;
+ ht->null.next = &ht->null;
- return cur;
+ ht->buckets[0] = &ht->null;
+
+ return ht;
}
/*
- * Create the table. Size is a power of two (i.e. 1..31)
+ * If the current bucket is uninitialized, initialize it
+ * by recursively copying information from the parent.
+ *
+ * We may have a situation where entry E is a parent to 2 other
+ * entries E' and E". If we split E into E and E', then the
+ * nodes meant for E" end up in E or E', both of which are wrong.
+ * To solve that problem, we walk down the whole chain, trying to
+ * insert the elements into the correct place.
+ *
+ * This slows down the growth a bit, but it works.
*/
-lrad_hash_table_t *lrad_hash_table_create(int size, void (*freeNode)(void *),
- int replace_flag)
+static void lrad_hash_table_fixup(lrad_hash_table_t *ht, uint32_t entry)
{
- lrad_hash_table_t *ht;
+ uint32_t parent_entry = parent_of(entry);
+ lrad_hash_entry_t **last, *cur;
+ uint32_t this;
- if ((size <= 1) || (size > 31)) return NULL;
+ parent_entry = parent_of(entry);
+
+ /* parent_entry == entry if and only if entry == 0 */
+
+ if (!ht->buckets[parent_entry]) {
+ lrad_hash_table_fixup(ht, parent_entry);
+ }
/*
- * Get the nearest power of two.
+ * Keep walking down cur, trying to find entries that
+ * don't belong here any more. There may be multiple
+ * ones, so we can't have a naive algorithm...
*/
- size = 1 << size;
+ last = &ht->buckets[parent_entry];
+ this = parent_entry;
+
+ for (cur = *last; cur != &ht->null; cur = cur->next) {
+ uint32_t real_entry;
+
+ real_entry = cur->key & ht->mask;
+ if (real_entry != this) { /* ht->buckets[real_entry] == NULL */
+ *last = &ht->null;
+ ht->buckets[real_entry] = cur;
+ this = real_entry;
+ }
- ht = malloc(sizeof(*ht));
- if (!ht) return NULL;
+ last = &(cur->next);
+ }
- memset(ht, 0, sizeof(*ht));
- ht->free = freeNode;
- ht->num_buckets = size;
- ht->replace_flag = replace_flag;
+ /*
+ * We may NOT have initialized this bucket, so do it now.
+ */
+ if (!ht->buckets[entry]) ht->buckets[entry] = &ht->null;
+}
- ht->buckets = malloc(sizeof(*ht->buckets) * ht->num_buckets);
- if (!ht->buckets) {
- free(ht);
- return NULL;
- }
- memset(ht->buckets, 0, sizeof(*ht->buckets) * ht->num_buckets);
+/*
+ * This should be a power of two. Changing it to 4 doesn't seem
+ * to make any difference.
+ */
+#define GROW_FACTOR (2)
- return ht;
+/*
+ * Grow the hash table.
+ */
+static void lrad_hash_table_grow(lrad_hash_table_t *ht)
+{
+ lrad_hash_entry_t **buckets;
+
+ buckets = malloc(sizeof(*buckets) * GROW_FACTOR * ht->num_buckets);
+ if (!buckets) return;
+
+ memcpy(buckets, ht->buckets,
+ sizeof(*buckets) * ht->num_buckets);
+ memset(&buckets[ht->num_buckets], 0,
+ sizeof(*buckets) * ht->num_buckets);
+
+ free(ht->buckets);
+ ht->buckets = buckets;
+ ht->num_buckets *= GROW_FACTOR;
+ ht->next_grow *= GROW_FACTOR;
+ ht->mask = ht->num_buckets - 1;
+#ifdef TESTING
+ grow = 1;
+ fprintf(stderr, "GROW TO %d\n", ht->num_buckets);
+#endif
}
+
/*
- * Insert data, OR copy it, if ht->data_size != 0
+ * Insert data.
*/
int lrad_hash_table_insert(lrad_hash_table_t *ht, uint32_t key, void *data)
{
if (!ht || !data) return 0;
- entry = key & (ht->num_buckets - 1);
+ entry = key & ht->mask;
reversed = reverse(key);
+ if (!ht->buckets[entry]) lrad_hash_table_fixup(ht, entry);
+
/*
- * Already in the table.
+ * If we try to do our own memory allocation here, the
+ * speedup is only ~15% or so, which isn't worth it.
*/
- node = list_find(ht->buckets[entry], reversed);
- if (node) {
- if (!ht->replace_flag) return 0;
+ node = malloc(sizeof(*node));
+ if (!node) return 0;
+ memset(node, 0, sizeof(*node));
- list_delete(&ht->buckets[entry], node);
+ node->next = &ht->null;
+ node->reversed = reversed;
+ node->key = key;
+ node->data = data;
- if (ht->free && node->data) ht->free(node->data);
- /*
- * Fall through to re-using the node.
- */
- } else {
- node = malloc(sizeof(*node) + ht->data_size);
- if (!node) return 0;
- ht->num_elements++;
- }
- memset(node, 0, sizeof(*node) + ht->data_size);
-
- node->next = NULL;
- node->key = reversed;
- if (ht->data_size) {
- node->data = &node[1]; /* point to the end of the node */
- memcpy(node->data, data, ht->data_size);
- } else {
- node->data = data;
+ /* already in the table, can't insert it */
+ if (!list_insert(ht, &ht->buckets[entry], node)) {
+ free(node);
+ return 0;
}
- list_insert(&(ht->buckets[entry]), node);
-
/*
* Check the load factor, and grow the table if
* necessary.
*/
- if (ht->num_elements >= (3 * ht->num_buckets)) {
- int i;
- lrad_hash_entry_t **buckets;
-
- buckets = malloc(sizeof(*buckets) * 2 * ht->num_buckets);
- if (!buckets) return 1;
-
- memcpy(buckets, ht->buckets,
- sizeof(*buckets) * ht->num_buckets);
-
- /*
- * Split the hash entries.
- *
- * When we double the size of the hash array, we
- * do O(N/2) work. Since this only happens after
- * we've inserted N elements, we're still amortized
- * at O(1) inserts, deletes, and updates.
- */
- for (i = 0; i < ht->num_buckets; i++) {
- buckets[ht->num_buckets + i] = list_split(&buckets[i],
- reverse((uint32_t) i + ht->num_buckets));
- }
- free(ht->buckets);
- ht->buckets = buckets;
- ht->num_buckets *= 2;
-#ifdef TESTING
- grow = 1;
- fprintf(stderr, "GROW TO %d\n", ht->num_buckets);
-#endif
+ ht->num_elements++;
+ if (ht->num_elements >= ht->next_grow) {
+ lrad_hash_table_grow(ht);
}
return 1;
/*
- * Find data from a key.
+ * Internal find a node routine.
*/
-void *lrad_hash_table_finddata(lrad_hash_table_t *ht, uint32_t key)
+static lrad_hash_entry_t *lrad_hash_table_find(lrad_hash_table_t *ht,
+ uint32_t key)
{
uint32_t entry;
uint32_t reversed;
- lrad_hash_entry_t *node;
- entry = key & (ht->num_buckets - 1);
+ if (!ht) return NULL;
+
+ entry = key & ht->mask;
reversed = reverse(key);
- if (!ht) return NULL;
+ if (!ht->buckets[entry]) lrad_hash_table_fixup(ht, entry);
+
+ return list_find(ht, ht->buckets[entry], reversed);
+}
+
+
+/*
+ * Replace old data with new data
+ */
+int lrad_hash_table_replace(lrad_hash_table_t *ht, uint32_t key, void *data)
+{
+ lrad_hash_entry_t *node;
+
+ if (!data) return 0;
+
+ node = lrad_hash_table_find(ht, key);
+ if (!node) return lrad_hash_table_insert(ht, key, data);
+
+ if (ht->free) ht->free(node->data);
+ node->data = data;
+
+ return 1;
+}
+
- node = list_find(ht->buckets[entry], reversed);
+/*
+ * Find data from a template
+ */
+void *lrad_hash_table_finddata(lrad_hash_table_t *ht, uint32_t key)
+{
+ lrad_hash_entry_t *node;
+
+ node = lrad_hash_table_find(ht, key);
if (!node) return NULL;
- return node->data; /* may be NULL */
+ return node->data;
}
+
/*
- * Delete a piece of data from the hash table.
+ * Yank an entry from the hash table, without freeing the data.
*/
-int lrad_hash_table_delete(lrad_hash_table_t *ht, uint32_t key)
+void *lrad_hash_table_yank(lrad_hash_table_t *ht, uint32_t key)
{
uint32_t entry;
uint32_t reversed;
+ void *data;
lrad_hash_entry_t *node;
if (!ht) return 0;
- entry = key & (ht->num_buckets - 1);
+ entry = key & ht->mask;
reversed = reverse(key);
- node = list_find(ht->buckets[entry], reversed);
- if (!node) return 0;
-
- if (ht->free) ht->free(node->data);
- list_delete(&ht->buckets[entry], node);
+ if (!ht->buckets[entry]) lrad_hash_table_fixup(ht, entry);
+
+ node = list_find(ht, ht->buckets[entry], reversed);
+ if (!node) return NULL;
+
+ list_delete(ht, &ht->buckets[entry], node);
ht->num_elements--;
+ data = node->data;
free(node);
+
+ return data;
+}
+
+
+/*
+ * Delete a piece of data from the hash table.
+ */
+int lrad_hash_table_delete(lrad_hash_table_t *ht, uint32_t key)
+{
+ void *data;
+
+ data = lrad_hash_table_yank(ht, key);
+ if (!data) return 0;
+
+ if (ht->free) ht->free(data);
+
return 1;
}
/*
* The entries MUST be all in one linked list.
*/
- for (node = ht->buckets[0]; node != NULL; node = next) {
+ for (node = ht->buckets[0]; node != &ht->null; node = next) {
next = node->next;
if (!node->data) continue; /* dummy entry */
* Walk over the nodes, allowing deletes & inserts to happen.
*/
int lrad_hash_table_walk(lrad_hash_table_t *ht,
- int (*callback)(void * /* ctx */,
- void * /* data */),
+ lrad_hash_table_walk_t callback,
void *context)
{
int i, rcode;;
if (!ht->buckets[i]) continue;
- for (node = ht->buckets[i]; node != NULL; node = next) {
+ for (node = ht->buckets[i]; node != &ht->null; node = next) {
next = node->next;
rcode = callback(context, node->data);
}
+#ifdef TESTING
/*
- * For users that have a small amount of data, and wish to associate
- * it directly with the hash entry, this saves a bit of memory &
- * malloc/free stuff.
+ * Show what the hash table is doing.
*/
-int lrad_hash_table_set_data_size(lrad_hash_table_t *ht, size_t data_size)
+int lrad_hash_table_info(lrad_hash_table_t *ht)
{
- if (!ht || ht->free) return 0;
+ int i, a, collisions, uninitialized;
+ int array[256];
+
+ if (!ht) return 0;
+
+ uninitialized = collisions = 0;
+ memset(array, 0, sizeof(array));
+
+ for (i = 0; i < ht->num_buckets; i++) {
+ uint32_t key;
+ int load;
+ lrad_hash_entry_t *node, *next;
+
+ /*
+ * If we haven't inserted or looked up an entry
+ * in a bucket, it's uninitialized.
+ */
+ if (!ht->buckets[i]) {
+ uninitialized++;
+ continue;
+ }
+
+ load = 0;
+ key = ~0;
+ for (node = ht->buckets[i]; node != &ht->null; node = next) {
+ if (node->reversed == key) {
+ collisions++;
+ } else {
+ key = node->reversed;
+ }
+ next = node->next;
+ load++;
+ }
- if (ht->num_elements != 0) return 0;
+ if (load > 255) load = 255;
+ array[load]++;
+ }
+
+ printf("HASH TABLE %p\tbuckets: %d\t(%d uninitialized)\n", ht,
+ ht->num_buckets, uninitialized);
+ printf("\tnum entries %d\thash collisions %d\n",
+ ht->num_elements, collisions);
- ht->data_size = data_size;
+ a = 0;
+ for (i = 1; i < 256; i++) {
+ if (!array[i]) continue;
+ printf("%d\t%d\n", i, array[i]);
- return 1;
+ /*
+ * Since the entries are ordered, the lookup cost
+ * for any one element in a chain is (on average)
+ * the cost of walking half of the chain.
+ */
+ if (i > 1) {
+ a += array[i] * i;
+ }
+ }
+ a /= 2;
+ a += array[1];
+
+ printf("\texpected lookup cost = %d/%d or %f\n\n",
+ ht->num_elements, a,
+ (float) ht->num_elements / (float) a);
+
+ return 0;
}
+#endif
#define FNV_MAGIC_INIT (0x811c9dc5)
#include <stdio.h>
#include <stdlib.h>
-#define MAX 8000
+#define MAX 1024*64
int main(int argc, char **argv)
{
- uint32_t i, *p, *q;
+ uint32_t hash;
+ int i, *p, *q, k;
lrad_hash_table_t *ht;
+ int *array;
- ht = lrad_hash_table_create(10, free, 0);
+ ht = lrad_hash_table_create(NULL);
if (!ht) {
fprintf(stderr, "Hash create failed\n");
exit(1);
}
+ array = malloc(sizeof(int) * MAX);
+
for (i = 0; i < MAX; i++) {
- p = malloc(sizeof(i));
+ p = array + i;
*p = i;
- if (!lrad_hash_table_insert(ht, i, p)) {
+ hash = lrad_hash(&i, sizeof(i));
+
+ if (!lrad_hash_table_insert(ht, hash, p)) {
fprintf(stderr, "Failed insert %08x\n", i);
exit(1);
}
-
- if (grow) {
- uint32_t j;
-
- for (j = 0; j < i; j++) {
- q = lrad_hash_table_finddata(ht, j);
- if (!q || (*q != j)) {
- fprintf(stderr, "BAD DATA %d %p\n",
- j, q);
- exit(1);
- }
- }
- grow = 0;
- }
-
- q = lrad_hash_table_finddata(ht, i);
+#ifdef TEST_INSERT
+ q = lrad_hash_table_finddata(ht, hash);
if (q != p) {
fprintf(stderr, "Bad data %d\n", i);
exit(1);
}
+#endif
}
- for (i = 0; i < MAX; i++) {
- lrad_hash_table_delete(ht, i);
- q = lrad_hash_table_finddata(ht, i);
- if (q) {
- fprintf(stderr, "Failed to delete %08x\n", i);
- exit(1);
+ lrad_hash_table_info(ht);
+
+ /*
+ * Build this to see how lookups result in shortening
+ * of the hash chains.
+ */
+ if (0) {
+ for (i = 0; i < MAX ; i++) {
+ hash = lrad_hash(&i, sizeof(i));
+
+ q = lrad_hash_table_finddata(ht, hash);
+ if (!q || *q != i) {
+ fprintf(stderr, "Failed finding %d\n", i);
+ exit(1);
+ }
+
+#if 0
+ if (!lrad_hash_table_delete(ht, hash)) {
+ fprintf(stderr, "Failed deleting %d\n", i);
+ exit(1);
+ }
+ q = lrad_hash_table_finddata(ht, hash);
+ if (q) {
+ fprintf(stderr, "Failed to delete %08x\n", i);
+ exit(1);
+ }
+#endif
}
+
+ lrad_hash_table_info(ht);
}
lrad_hash_table_free(ht);
+ free(array);
exit(0);
}
struct request_list_t {
lrad_hash_table_t *ht;
- int collisions;
};
-typedef struct request_entry_t {
- struct request_entry_t *next;
- REQUEST *request;
-} request_entry_t;
-
-
#ifdef HAVE_PTHREAD_H
static pthread_mutex_t proxy_mutex;
#else
}
-static int packet_cmp(const RADIUS_PACKET *a, const RADIUS_PACKET *b)
-{
-
- if (a->id != b->id) return 0;
-
- if (a->src_port != b->src_port) return 0;
-
- switch (a->dst_ipaddr.af) {
- case AF_INET:
- if (memcmp(&a->dst_ipaddr.ipaddr.ip4addr,
- &b->dst_ipaddr.ipaddr.ip4addr,
- sizeof(a->dst_ipaddr.ipaddr.ip4addr)) != 0)
- return 0;
- if (memcmp(&a->src_ipaddr.ipaddr.ip4addr,
- &b->src_ipaddr.ipaddr.ip4addr,
- sizeof(a->src_ipaddr.ipaddr.ip4addr)) != 0)
- return 0;
- break;
- case AF_INET6:
- if (memcmp(&a->dst_ipaddr.ipaddr.ip6addr,
- &b->dst_ipaddr.ipaddr.ip6addr,
- sizeof(a->dst_ipaddr.ipaddr.ip6addr)) != 0)
- return 0;
- if (memcmp(&a->src_ipaddr.ipaddr.ip6addr,
- &b->src_ipaddr.ipaddr.ip6addr,
- sizeof(a->src_ipaddr.ipaddr.ip6addr)) != 0)
- return 0;
- break;
- default:
- return 0;
- break;
- }
-
- /*
- * These next comparisons should reall be assertions.
- */
- if (a->src_ipaddr.af != b->src_ipaddr.af) return 0;
-
- if (a->dst_ipaddr.af != b->dst_ipaddr.af) return 0;
-
- if (a->sockfd != b->sockfd) return 0;
-
- if (a->code != b->code) return 0;
-
- if (a->dst_port != b->dst_port) return 0;
-
- /*
- * Everything's equal. Say so.
- */
- return 1;
-}
-
/*
* Compare two REQUEST data structures, based on a number
* of criteria, for proxied packets.
*/
memset(rl, 0, sizeof(*rl));
- rl->ht = lrad_hash_table_create(10, NULL, 0);
+ rl->ht = lrad_hash_table_create(NULL);
if (!rl->ht) {
rad_assert("FAIL" == NULL);
}
- lrad_hash_table_set_data_size(rl->ht, sizeof(request_entry_t));
return rl;
}
static int rl_free_entry(void *ctx, void *data)
{
- request_entry_t *next, *entry = data;
- REQUEST *request;
-
+ REQUEST *request = data;
+
ctx = ctx; /* -Wunused */
- for (entry = data; entry != NULL; entry = next) {
- next = entry->next;
- request = entry->request;
-
#ifdef HAVE_PTHREAD_H
- /*
- * If someone is processing this request, kill
- * them, and mark the request as not being used.
- */
- if (request->child_pid != NO_SUCH_CHILD_PID) {
- pthread_kill(request->child_pid, SIGKILL);
- request->child_pid = NO_SUCH_CHILD_PID;
- }
-#endif
- request_free(&request);
- if (entry != data) free(entry);
+ /*
+ * If someone is processing this request, kill
+ * them, and mark the request as not being used.
+ */
+ if (request->child_pid != NO_SUCH_CHILD_PID) {
+ pthread_kill(request->child_pid, SIGKILL);
+ request->child_pid = NO_SUCH_CHILD_PID;
}
+#endif
+ request_free(&request);
return 0;
}
*/
void rl_yank(request_list_t *rl, REQUEST *request)
{
- request_entry_t *entry;
-
#ifdef WITH_SNMP
/*
* Update the SNMP statistics.
/*
* Delete the request from the list.
*/
- entry = lrad_hash_table_finddata(rl->ht, request->packet->hash);
-
- /*
- * The entry managed by the hash table is being deleted.
- */
- if (entry->request == request) {
- if (entry->next) {
- request_entry_t *next = entry->next;
- entry->next = next->next;
- entry->request = next->request;
- free(next);
-
- rad_assert(rl->collisions > 0);
- rl->collisions--;
- } else {
- lrad_hash_table_delete(rl->ht, request->packet->hash);
- }
-
- } else { /* a secondary entry is being deleted */
- request_entry_t *this, *next, **last;
-
- last = &entry->next;
- for (this = entry->next; this != NULL; this = next) {
- next = this->next;
-
- if (this->request != request) {
- last = &this->next;
- continue;
- }
-
- *last = this->next;
- free(this);
-
- rad_assert(rl->collisions > 0);
- rl->collisions--;
- break;
- }
- }
-
+ lrad_hash_table_delete(rl->ht, request->packet->hash);
/*
* If there's a proxied packet, and we're still
*/
int rl_add(request_list_t *rl, REQUEST *request)
{
- request_entry_t *entry, myentry;
-
- entry = lrad_hash_table_finddata(rl->ht, request->packet->hash);
- if (!entry) {
- myentry.next = NULL;
- myentry.request = request;
- return lrad_hash_table_insert(rl->ht, request->packet->hash,
- &myentry);
- }
-
- /*
- * Collision: insert it into a linked list (yuck)
- */
- entry->next = rad_malloc(sizeof(*entry));
- entry->next->next = NULL;
- entry->next->request = request;
-
- DEBUG3(" FYI: hash collision...");
-
- rl->collisions++;
-
- return 1;
+ return lrad_hash_table_insert(rl->ht, request->packet->hash, request);
}
/*
*/
REQUEST *rl_find(request_list_t *rl, RADIUS_PACKET *packet)
{
- request_entry_t *entry;
-
packet_hash(packet);
- entry = lrad_hash_table_finddata(rl->ht, packet->hash);
- if (!entry) return NULL;
-
- /*
- * Call a packet comparison function?
- */
- while (entry && !packet_cmp(packet, entry->request->packet)) {
- entry = entry->next;
- }
-
- if (!entry) return NULL;
-
- return entry->request;
+ return lrad_hash_table_finddata(rl->ht, packet->hash);
}
/*
*/
int rl_num_requests(request_list_t *rl)
{
- return lrad_hash_table_num_elements(rl->ht) + rl->collisions;
+ return lrad_hash_table_num_elements(rl->ht);
}
rl_walk_t *info = (rl_walk_t *) ctx;
child_pid_t child_pid;
request_list_t *rl = info->rl;
- request_entry_t *entry = data;
- request_entry_t *next = entry->next;
- REQUEST *request = entry->request;
-
- if (next) refresh_request(ctx, next);
+ REQUEST *request = data;
rad_assert(request->magic == REQUEST_MAGIC);