Improve performance of RBT (#41165)

(cherry picked from commit 5d79b60fc5)
(cherry picked from commit 318158d66a)
This commit is contained in:
Mukund Sivaraman 2015-12-10 22:52:52 +05:30
parent 51aed18274
commit a28d8e8bf5
11 changed files with 403 additions and 229 deletions

View file

@ -1,3 +1,10 @@
4277. [performance] Improve performance of the RBT, the central zone
datastructure: The aux hashtable was improved,
hash function was updated to perform more
uniform mapping, uppernode was added to
dns_rbtnode, and other cleanups and performance
improvements were made. [RT #41165]
4276. [protocol] Add support for SMIMEA. [RT #40513]
4274. [performance] Speed up typemap processing from text. [RT #41196]

View file

@ -472,8 +472,7 @@ finddbent(dns_acache_t *acache, dns_db_t *db, dbentry_t **dbentryp) {
* The caller must be holding the acache lock.
*/
bucket = isc_hash_calc((const unsigned char *)&db,
sizeof(db), ISC_TRUE) % DBBUCKETS;
bucket = isc_hash_function(&db, sizeof(db), ISC_TRUE, NULL) % DBBUCKETS;
for (dbentry = ISC_LIST_HEAD(acache->dbbucket[bucket]);
dbentry != NULL;
@ -1264,8 +1263,7 @@ dns_acache_setdb(dns_acache_t *acache, dns_db_t *db) {
dbentry->db = NULL;
dns_db_attach(db, &dbentry->db);
bucket = isc_hash_calc((const unsigned char *)&db,
sizeof(db), ISC_TRUE) % DBBUCKETS;
bucket = isc_hash_function(&db, sizeof(db), ISC_TRUE, NULL) % DBBUCKETS;
ISC_LIST_APPEND(acache->dbbucket[bucket], dbentry, link);
@ -1353,8 +1351,8 @@ dns_acache_putdb(dns_acache_t *acache, dns_db_t *db) {
INSIST(ISC_LIST_EMPTY(dbentry->originlist) &&
ISC_LIST_EMPTY(dbentry->referlist));
bucket = isc_hash_calc((const unsigned char *)&db,
sizeof(db), ISC_TRUE) % DBBUCKETS;
bucket = isc_hash_function(&db, sizeof(db), ISC_TRUE, NULL) % DBBUCKETS;
ISC_LIST_UNLINK(acache->dbbucket[bucket], dbentry, link);
dns_db_detach(&dbentry->db);

View file

@ -85,6 +85,7 @@ struct dns_rbtnode {
dns_rbtnode_t *right;
dns_rbtnode_t *down;
#ifdef DNS_RBT_USEHASH
dns_rbtnode_t *uppernode;
dns_rbtnode_t *hashnext;
#endif

View file

@ -29,6 +29,7 @@
#include <isc/mem.h>
#include <isc/once.h>
#include <isc/print.h>
#include <isc/random.h>
#include <isc/string.h>
#include <isc/thread.h>
#include <isc/util.h>
@ -438,42 +439,10 @@ dns_name_internalwildcard(const dns_name_t *name) {
return (ISC_FALSE);
}
static inline unsigned int
name_hash(dns_name_t *name, isc_boolean_t case_sensitive) {
unsigned int length;
const unsigned char *s;
unsigned int h = 0;
unsigned char c;
length = name->length;
if (length > 16)
length = 16;
/*
* This hash function is similar to the one Ousterhout
* uses in Tcl.
*/
s = name->ndata;
if (case_sensitive) {
while (length > 0) {
h += ( h << 3 ) + *s;
s++;
length--;
}
} else {
while (length > 0) {
c = maptolower[*s];
h += ( h << 3 ) + c;
s++;
length--;
}
}
return (h);
}
unsigned int
dns_name_hash(dns_name_t *name, isc_boolean_t case_sensitive) {
unsigned int length;
/*
* Provide a hash value for 'name'.
*/
@ -482,7 +451,12 @@ dns_name_hash(dns_name_t *name, isc_boolean_t case_sensitive) {
if (name->labels == 0)
return (0);
return (name_hash(name, case_sensitive));
length = name->length;
if (length > 16)
length = 16;
return (isc_hash_function_reverse(name->ndata, length,
case_sensitive, NULL));
}
unsigned int
@ -495,19 +469,17 @@ dns_name_fullhash(dns_name_t *name, isc_boolean_t case_sensitive) {
if (name->labels == 0)
return (0);
return (isc_hash_calc((const unsigned char *)name->ndata,
name->length, case_sensitive));
return (isc_hash_function_reverse(name->ndata, name->length,
case_sensitive, NULL));
}
unsigned int
dns_fullname_hash(dns_name_t *name, isc_boolean_t case_sensitive) {
/*
* This function was deprecated due to the breakage of the name space
* convention. We only keep this internally to provide binary backward
* convention. We only keep this internally to provide binary backward
* compatibility.
*/
REQUIRE(VALID_NAME(name));
return (dns_name_fullhash(name, case_sensitive));
}
@ -527,7 +499,8 @@ dns_name_hashbylabel(dns_name_t *name, isc_boolean_t case_sensitive) {
if (name->labels == 0)
return (0);
else if (name->labels == 1)
return (name_hash(name, case_sensitive));
return (isc_hash_function_reverse(name->ndata, name->length,
case_sensitive, NULL));
SETUP_OFFSETS(name, offsets, odata);
DNS_NAME_INIT(&tname, NULL);
@ -539,7 +512,8 @@ dns_name_hashbylabel(dns_name_t *name, isc_boolean_t case_sensitive) {
tname.length = name->length - offsets[i];
else
tname.length = offsets[i + 1] - offsets[i];
h += name_hash(&tname, case_sensitive);
h += isc_hash_function_reverse(tname.ndata, tname.length,
case_sensitive, NULL);
}
return (h);
@ -597,12 +571,15 @@ dns_name_fullcompare(const dns_name_t *name1, const dns_name_t *name2,
ldiff = l1 - l2;
}
offsets1 += l1;
offsets2 += l2;
while (l > 0) {
l--;
l1--;
l2--;
label1 = &name1->ndata[offsets1[l1]];
label2 = &name2->ndata[offsets2[l2]];
offsets1--;
offsets2--;
label1 = &name1->ndata[*offsets1];
label2 = &name2->ndata[*offsets2];
count1 = *label1++;
count2 = *label2++;
@ -618,16 +595,41 @@ dns_name_fullcompare(const dns_name_t *name1, const dns_name_t *name2,
else
count = count2;
while (count > 0) {
chdiff = (int)maptolower[*label1] -
(int)maptolower[*label2];
while (count > 3) {
chdiff = (int)maptolower[label1[0]] -
(int)maptolower[label2[0]];
if (chdiff != 0) {
*orderp = chdiff;
goto done;
}
chdiff = (int)maptolower[label1[1]] -
(int)maptolower[label2[1]];
if (chdiff != 0) {
*orderp = chdiff;
goto done;
}
chdiff = (int)maptolower[label1[2]] -
(int)maptolower[label2[2]];
if (chdiff != 0) {
*orderp = chdiff;
goto done;
}
chdiff = (int)maptolower[label1[3]] -
(int)maptolower[label2[3]];
if (chdiff != 0) {
*orderp = chdiff;
goto done;
}
count -= 4;
label1 += 4;
label2 += 4;
}
while (count-- > 0) {
chdiff = (int)maptolower[*label1++] - (int)maptolower[*label2++];
if (chdiff != 0) {
*orderp = chdiff;
goto done;
}
count--;
label1++;
label2++;
}
if (cdiff != 0) {
*orderp = cdiff;
@ -643,11 +645,12 @@ dns_name_fullcompare(const dns_name_t *name1, const dns_name_t *name2,
namereln = dns_namereln_subdomain;
else
namereln = dns_namereln_equal;
*nlabelsp = nlabels;
return (namereln);
done:
*nlabelsp = nlabels;
if (nlabels > 0 && namereln == dns_namereln_none)
if (nlabels > 0)
namereln = dns_namereln_commonancestor;
return (namereln);
@ -709,16 +712,31 @@ dns_name_equal(const dns_name_t *name1, const dns_name_t *name2) {
label1 = name1->ndata;
label2 = name2->ndata;
while (l > 0) {
l--;
while (l-- > 0) {
count = *label1++;
if (count != *label2++)
return (ISC_FALSE);
INSIST(count <= 63); /* no bitstring support */
while (count > 0) {
count--;
while (count > 3) {
c = maptolower[label1[0]];
if (c != maptolower[label2[0]])
return (ISC_FALSE);
c = maptolower[label1[1]];
if (c != maptolower[label2[1]])
return (ISC_FALSE);
c = maptolower[label1[2]];
if (c != maptolower[label2[2]])
return (ISC_FALSE);
c = maptolower[label1[3]];
if (c != maptolower[label2[3]])
return (ISC_FALSE);
count -= 4;
label1 += 4;
label2 += 4;
}
while (count-- > 0) {
c = maptolower[*label1++];
if (c != maptolower[*label2++])
return (ISC_FALSE);

View file

@ -58,14 +58,14 @@
#endif
struct dns_rbt {
unsigned int magic;
isc_mem_t * mctx;
dns_rbtnode_t * root;
void (*data_deleter)(void *, void *);
void * deleter_arg;
unsigned int nodecount;
unsigned int hashsize;
dns_rbtnode_t ** hashtable;
unsigned int magic;
isc_mem_t * mctx;
dns_rbtnode_t * root;
void (*data_deleter)(void *, void *);
void * deleter_arg;
unsigned int nodecount;
unsigned int hashsize;
dns_rbtnode_t ** hashtable;
};
#define RED 0
@ -78,6 +78,9 @@ struct dns_rbt {
#define LEFT(node) ((node)->left)
#define RIGHT(node) ((node)->right)
#define DOWN(node) ((node)->down)
#ifdef DNS_RBT_USEHASH
#define UPPERNODE(node) ((node)->uppernode)
#endif /* DNS_RBT_USEHASH */
#define DATA(node) ((node)->data)
#define HASHNEXT(node) ((node)->hashnext)
#define HASHVAL(node) ((node)->hashval)
@ -174,25 +177,44 @@ Name(dns_rbtnode_t *node) {
return (name);
}
#endif /* DEBUG */
static void dns_rbt_printnodename(dns_rbtnode_t *node);
#endif
#ifdef DNS_RBT_USEHASH
/* Upper node is the parent of the root of the passed node's
* subtree. The passed node must not be NULL.
*/
static inline dns_rbtnode_t *
find_up(dns_rbtnode_t *node) {
get_upper_node(dns_rbtnode_t *node) {
return (UPPERNODE(node));
}
#else
/* The passed node must not be NULL. */
static inline dns_rbtnode_t *
get_subtree_root(dns_rbtnode_t *node) {
while (!IS_ROOT(node)) {
node = PARENT(node);
}
return (node);
}
/* Upper node is the parent of the root of the passed node's
* subtree. The passed node must not be NULL.
*/
static inline dns_rbtnode_t *
get_upper_node(dns_rbtnode_t *node) {
dns_rbtnode_t *root;
/*
* Return the node in the level above the argument node that points
* to the level the argument node is in. If the argument node is in
* the top level, the return value is NULL.
*/
for (root = node; ! IS_ROOT(root); root = PARENT(root))
; /* Nothing. */
root = get_subtree_root(node);
return (PARENT(root));
}
#endif /* DNS_RBT_USEHASH */
/*
* Forward declarations.
*/
@ -221,12 +243,9 @@ dns_rbt_addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order,
static void
dns_rbt_deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp);
static isc_result_t
dns_rbt_deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node);
static void
dns_rbt_deletetreeflat(dns_rbt_t *rbt, unsigned int quantum,
dns_rbtnode_t **nodep);
deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, isc_boolean_t unhash,
dns_rbtnode_t **nodep);
/*
* Initialize a red/black tree of trees.
@ -289,7 +308,7 @@ dns_rbt_destroy2(dns_rbt_t **rbtp, unsigned int quantum) {
rbt = *rbtp;
dns_rbt_deletetreeflat(rbt, quantum, &rbt->root);
deletetreeflat(rbt, quantum, ISC_FALSE, &rbt->root);
if (rbt->root != NULL)
return (ISC_R_QUOTA);
@ -399,6 +418,9 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
if (result == ISC_R_SUCCESS) {
rbt->nodecount++;
new_current->is_root = 1;
#ifdef DNS_RBT_USEHASH
UPPERNODE(new_current) = NULL;
#endif /* DNS_RBT_USEHASH */
rbt->root = new_current;
*nodep = new_current;
hash_node(rbt, new_current, name);
@ -578,7 +600,10 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
PARENT(current) = new_current;
DOWN(new_current) = current;
root = &DOWN(new_current);
#ifdef DNS_RBT_USEHASH
UPPERNODE(new_current) = UPPERNODE(current);
UPPERNODE(current) = new_current;
#endif /* DNS_RBT_USEHASH */
ADD_LEVEL(&chain, new_current);
LEFT(current) = NULL;
@ -635,6 +660,12 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
result = create_node(rbt->mctx, add_name, &new_current);
if (result == ISC_R_SUCCESS) {
#ifdef DNS_RBT_USEHASH
if (*root == NULL)
UPPERNODE(new_current) = current;
else
UPPERNODE(new_current) = PARENT(*root);
#endif /* DNS_RBT_USEHASH */
dns_rbt_addonlevel(new_current, current, order, root);
rbt->nodecount++;
*nodep = new_current;
@ -761,12 +792,6 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
unsigned int tlabels = 1;
unsigned int hash;
/*
* If there is no hash table, hashing can't be done.
*/
if (rbt->hashtable == NULL)
goto nohash;
/*
* The case of current != current_root, that
* means a left or right pointer was followed,
@ -781,7 +806,7 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
/*
* current_root is the root of the current level, so
* it's parent is the same as it's "up" pointer.
* its parent is the same as its "up" pointer.
*/
up_current = PARENT(current_root);
dns_name_init(&hash_name, NULL);
@ -807,7 +832,7 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
if (hash != HASHVAL(hnode))
continue;
if (find_up(hnode) != up_current)
if (get_upper_node(hnode) != up_current)
continue;
dns_name_init(&hnode_name, NULL);
NODENAME(hnode, &hnode_name);
@ -848,8 +873,8 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
current = NULL;
continue;
nohash:
#endif /* DNS_RBT_USEHASH */
#else /* DNS_RBT_USEHASH */
/*
* Standard binary search tree movement.
*/
@ -858,6 +883,8 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
else
current = RIGHT(current);
#endif /* DNS_RBT_USEHASH */
} else {
/*
* The names have some common suffix labels.
@ -1285,10 +1312,10 @@ dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, isc_boolean_t recurse)
REQUIRE(DNS_RBTNODE_VALID(node));
if (DOWN(node) != NULL) {
if (recurse)
RUNTIME_CHECK(dns_rbt_deletetree(rbt, DOWN(node))
== ISC_R_SUCCESS);
else {
if (recurse) {
PARENT(DOWN(node)) = NULL;
deletetreeflat(rbt, 0, ISC_TRUE, &DOWN(node));
} else {
if (DATA(node) != NULL && rbt->data_deleter != NULL)
rbt->data_deleter(DATA(node), rbt->deleter_arg);
DATA(node) = NULL;
@ -1300,6 +1327,7 @@ dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, isc_boolean_t recurse)
* by itself on a single level, so join_nodes() could
* be used to collapse the tree (with all the caveats
* of the comment at the start of this function).
* But join_nodes() function has now been removed.
*/
return (ISC_R_SUCCESS);
}
@ -1310,7 +1338,7 @@ dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, isc_boolean_t recurse)
* deleted. If the deleted node is the top level, parent will be set
* to NULL.
*/
parent = find_up(node);
parent = get_upper_node(node);
/*
* This node now has no down pointer (either because it didn't
@ -1391,7 +1419,7 @@ dns_rbt_fullnamefromnode(dns_rbtnode_t *node, dns_name_t *name) {
if (result != ISC_R_SUCCESS)
break;
node = find_up(node);
node = get_upper_node(node);
} while (! dns_name_isabsolute(name));
return (result);
@ -1527,6 +1555,7 @@ rehash(dns_rbt_t *rbt) {
unsigned int oldsize;
dns_rbtnode_t **oldtable;
dns_rbtnode_t *node;
dns_rbtnode_t *nextnode;
unsigned int hash;
unsigned int i;
@ -1541,19 +1570,15 @@ rehash(dns_rbt_t *rbt) {
return;
}
INSIST(rbt->hashsize > 0);
for (i = 0; i < rbt->hashsize; i++)
rbt->hashtable[i] = NULL;
for (i = 0; i < oldsize; i++) {
node = oldtable[i];
while (node != NULL) {
for (node = oldtable[i]; node != NULL; node = nextnode) {
hash = HASHVAL(node) % rbt->hashsize;
oldtable[i] = HASHNEXT(node);
nextnode = HASHNEXT(node);
HASHNEXT(node) = rbt->hashtable[hash];
rbt->hashtable[hash] = node;
node = oldtable[i];
}
}
@ -1578,19 +1603,17 @@ unhash_node(dns_rbt_t *rbt, dns_rbtnode_t *node) {
REQUIRE(DNS_RBTNODE_VALID(node));
if (rbt->hashtable != NULL) {
bucket = HASHVAL(node) % rbt->hashsize;
bucket_node = rbt->hashtable[bucket];
bucket = HASHVAL(node) % rbt->hashsize;
bucket_node = rbt->hashtable[bucket];
if (bucket_node == node)
rbt->hashtable[bucket] = HASHNEXT(node);
else {
while (HASHNEXT(bucket_node) != node) {
INSIST(HASHNEXT(bucket_node) != NULL);
bucket_node = HASHNEXT(bucket_node);
}
HASHNEXT(bucket_node) = HASHNEXT(node);
if (bucket_node == node) {
rbt->hashtable[bucket] = HASHNEXT(node);
} else {
while (HASHNEXT(bucket_node) != node) {
INSIST(HASHNEXT(bucket_node) != NULL);
bucket_node = HASHNEXT(bucket_node);
}
HASHNEXT(bucket_node) = HASHNEXT(node);
}
}
#endif /* DNS_RBT_USEHASH */
@ -2004,117 +2027,66 @@ dns_rbt_deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp) {
}
}
/*
* This should only be used on the root of a tree, because no color fixup
* is done at all.
*
* NOTE: No root pointer maintenance is done, because the function is only
* used for two cases:
* + deleting everything DOWN from a node that is itself being deleted, and
* + deleting the entire tree of trees from dns_rbt_destroy.
* In each case, the root pointer is no longer relevant, so there
* is no need for a root parameter to this function.
*
* If the function is ever intended to be used to delete something where
* a pointer needs to be told that this tree no longer exists,
* this function would need to adjusted accordingly.
*/
static isc_result_t
dns_rbt_deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node) {
isc_result_t result = ISC_R_SUCCESS;
REQUIRE(VALID_RBT(rbt));
if (node == NULL)
return (result);
if (LEFT(node) != NULL) {
result = dns_rbt_deletetree(rbt, LEFT(node));
if (result != ISC_R_SUCCESS)
goto done;
LEFT(node) = NULL;
}
if (RIGHT(node) != NULL) {
result = dns_rbt_deletetree(rbt, RIGHT(node));
if (result != ISC_R_SUCCESS)
goto done;
RIGHT(node) = NULL;
}
if (DOWN(node) != NULL) {
result = dns_rbt_deletetree(rbt, DOWN(node));
if (result != ISC_R_SUCCESS)
goto done;
DOWN(node) = NULL;
}
done:
if (result != ISC_R_SUCCESS)
return (result);
if (DATA(node) != NULL && rbt->data_deleter != NULL)
rbt->data_deleter(DATA(node), rbt->deleter_arg);
unhash_node(rbt, node);
#if DNS_RBT_USEMAGIC
node->magic = 0;
#endif
static void
freenode(dns_rbt_t *rbt, dns_rbtnode_t **nodep) {
dns_rbtnode_t *node = *nodep;
isc_mem_put(rbt->mctx, node, NODE_SIZE(node));
*nodep = NULL;
rbt->nodecount--;
return (result);
}
static void
dns_rbt_deletetreeflat(dns_rbt_t *rbt, unsigned int quantum,
dns_rbtnode_t **nodep)
deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, isc_boolean_t unhash,
dns_rbtnode_t **nodep)
{
dns_rbtnode_t *parent;
dns_rbtnode_t *node = *nodep;
REQUIRE(VALID_RBT(rbt));
dns_rbtnode_t *root = *nodep;
again:
if (node == NULL) {
*nodep = NULL;
return;
}
while (root != NULL) {
/*
* If there is a left, right or down node, walk into it
* and iterate.
*/
if (LEFT(root) != NULL) {
dns_rbtnode_t *node = root;
root = LEFT(root);
LEFT(node) = NULL;
} else if (RIGHT(root) != NULL) {
dns_rbtnode_t *node = root;
root = RIGHT(root);
RIGHT(node) = NULL;
} else if (DOWN(root) != NULL) {
dns_rbtnode_t *node = root;
root = DOWN(root);
DOWN(node) = NULL;
} else {
/*
* There are no left, right or down nodes, so we
* can free this one and go back to its parent.
*/
dns_rbtnode_t *node = root;
root = PARENT(root);
traverse:
if (LEFT(node) != NULL) {
node = LEFT(node);
goto traverse;
}
if (DOWN(node) != NULL) {
node = DOWN(node);
goto traverse;
}
if (DATA(node) != NULL && rbt->data_deleter != NULL)
rbt->data_deleter(DATA(node), rbt->deleter_arg);
/*
* Note: we don't call unhash_node() here as we are destroying
* the complete rbt tree.
*/
if (DATA(node) != NULL && rbt->data_deleter != NULL)
rbt->data_deleter(DATA(node),
rbt->deleter_arg);
if (unhash)
unhash_node(rbt, node);
/*
* Note: we don't call unhash_node() here as we
* are destroying the complete RBT tree.
*/
#if DNS_RBT_USEMAGIC
node->magic = 0;
node->magic = 0;
#endif
parent = PARENT(node);
if (RIGHT(node) != NULL)
PARENT(RIGHT(node)) = parent;
if (parent != NULL) {
if (LEFT(parent) == node)
LEFT(parent) = RIGHT(node);
else if (DOWN(parent) == node)
DOWN(parent) = RIGHT(node);
} else
parent = RIGHT(node);
isc_mem_put(rbt->mctx, node, NODE_SIZE(node));
rbt->nodecount--;
node = parent;
if (quantum != 0 && --quantum == 0) {
*nodep = node;
return;
freenode(rbt, &node);
if (quantum != 0 && --quantum == 0)
break;
}
}
goto again;
*nodep = root;
}
static void

View file

@ -57,6 +57,7 @@ isc_socketmgr_t *socketmgr = NULL;
dns_zonemgr_t *zonemgr = NULL;
isc_boolean_t app_running = ISC_FALSE;
int ncpus;
isc_boolean_t debug_mem_record = ISC_TRUE;
static isc_boolean_t hash_active = ISC_FALSE, dst_active = ISC_FALSE;
@ -115,7 +116,8 @@ dns_test_begin(FILE *logfile, isc_boolean_t start_managers) {
if (start_managers)
CHECK(isc_app_start());
isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
if (debug_mem_record)
isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
CHECK(isc_mem_create(0, 0, &mctx));
CHECK(isc_entropy_create(mctx, &ectx));

View file

@ -50,6 +50,7 @@ extern isc_socketmgr_t *socketmgr;
extern dns_zonemgr_t *zonemgr;
extern isc_boolean_t app_running;
extern int ncpus;
extern isc_boolean_t debug_mem_record;
isc_result_t
dns_test_begin(FILE *logfile, isc_boolean_t create_managers);

View file

@ -399,3 +399,142 @@ isc_hash_calc(const unsigned char *key, unsigned int keylen,
return (hash_calc(hash, key, keylen, case_sensitive));
}
static unsigned int fnv_offset_basis;
static isc_once_t fnv_once = ISC_ONCE_INIT;
static void
fnv_initialize(void) {
/*
* This function should not leave fnv_offset_basis set to
* 0. Also, after this function has been called, if it is called
* again, it should not change fnv_offset_basis.
*/
while (fnv_offset_basis == 0) {
isc_random_get(&fnv_offset_basis);
}
}
unsigned int
isc_hash_function(const void *data, size_t length,
isc_boolean_t case_sensitive,
unsigned int *previous_hashp)
{
unsigned int hval;
const unsigned char *bp;
const unsigned char *be;
RUNTIME_CHECK(isc_once_do(&fnv_once, fnv_initialize) == ISC_R_SUCCESS);
hval = previous_hashp != NULL ? *previous_hashp : fnv_offset_basis;
bp = (const unsigned char *) data;
be = bp + length;
/*
* Fowler-Noll-Vo FNV-1a hash function.
*
* NOTE: A random fnv_offset_basis is used by default to avoid
* collision attacks as the hash function is reversible. This
* makes the mapping non-deterministic, but the distribution in
* the domain is still uniform.
*/
if (case_sensitive) {
while (bp < be - 4) {
hval ^= (unsigned int) bp[0];
hval *= 16777619;
hval ^= (unsigned int) bp[1];
hval *= 16777619;
hval ^= (unsigned int) bp[2];
hval *= 16777619;
hval ^= (unsigned int) bp[3];
hval *= 16777619;
bp += 4;
}
while (bp < be) {
hval ^= (unsigned int) *bp++;
hval *= 16777619;
}
} else {
while (bp < be - 4) {
hval ^= (unsigned int) maptolower[bp[0]];
hval *= 16777619;
hval ^= (unsigned int) maptolower[bp[1]];
hval *= 16777619;
hval ^= (unsigned int) maptolower[bp[2]];
hval *= 16777619;
hval ^= (unsigned int) maptolower[bp[3]];
hval *= 16777619;
bp += 4;
}
while (bp < be) {
hval ^= (unsigned int) maptolower[*bp++];
hval *= 16777619;
}
}
return (hval);
}
unsigned int
isc_hash_function_reverse(const void *data, size_t length,
isc_boolean_t case_sensitive,
unsigned int *previous_hashp)
{
unsigned int hval;
const unsigned char *bp;
const unsigned char *be;
RUNTIME_CHECK(isc_once_do(&fnv_once, fnv_initialize) == ISC_R_SUCCESS);
hval = previous_hashp != NULL ? *previous_hashp : fnv_offset_basis;
bp = (const unsigned char *) data;
be = bp + length;
/*
* Fowler-Noll-Vo FNV-1a hash function.
*
* NOTE: A random fnv_offset_basis is used by default to avoid
* collision attacks as the hash function is reversible. This
* makes the mapping non-deterministic, but the distribution in
* the domain is still uniform.
*/
if (case_sensitive) {
while (be >= bp + 4) {
be -= 4;
hval ^= (unsigned int) be[3];
hval *= 16777619;
hval ^= (unsigned int) be[2];
hval *= 16777619;
hval ^= (unsigned int) be[1];
hval *= 16777619;
hval ^= (unsigned int) be[0];
hval *= 16777619;
}
while (--be >= bp) {
hval ^= (unsigned int) *be;
hval *= 16777619;
}
} else {
while (be >= bp + 4) {
be -= 4;
hval ^= (unsigned int) maptolower[be[3]];
hval *= 16777619;
hval ^= (unsigned int) maptolower[be[2]];
hval *= 16777619;
hval ^= (unsigned int) maptolower[be[1]];
hval *= 16777619;
hval ^= (unsigned int) maptolower[be[0]];
hval *= 16777619;
}
while (--be >= bp) {
hval ^= (unsigned int) maptolower[*be];
hval *= 16777619;
}
}
return (hval);
}

View file

@ -180,6 +180,44 @@ isc_hash_calc(const unsigned char *key, unsigned int keylen,
*/
/*@}*/
unsigned int
isc_hash_function(const void *data, size_t length,
isc_boolean_t case_sensitive,
unsigned int *previous_hashp);
unsigned int
isc_hash_function_reverse(const void *data, size_t length,
isc_boolean_t case_sensitive,
unsigned int *previous_hashp);
/*!<
* \brief Calculate a hash over data.
*
* This hash function is useful for hashtables. The hash function is
* opaque and not important to the caller. The returned hash values are
* non-deterministic and will have different mapping every time a
* process using this library is run, but will have uniform
* distribution.
*
* isc_hash_function() calculates the hash from start to end over the
* input data. isc_hash_function_reverse() calculates the hash from the
* end to the start over the input data. The difference in order is
* useful in incremental hashing.
*
* This is a new variant of isc_hash_calc() and will supercede
* isc_hash_calc() eventually.
*
* 'data' is the data to be hashed.
*
* 'length' is the size of the data to be hashed.
*
* 'case_sensitive' specifies whether the hash key should be treated as
* case_sensitive values. It should typically be ISC_FALSE if the hash key
* is a DNS name.
*
* 'previous_hashp' is a pointer to a previous hash value returned by
* this function. It can be used to perform incremental hashing. NULL
* must be passed during first calls.
*/
ISC_LANG_ENDDECLS
#endif /* ISC_HASH_H */

View file

@ -205,7 +205,6 @@ isc_sockaddr_hash(const isc_sockaddr_t *sockaddr, isc_boolean_t address_only) {
unsigned int length = 0;
const unsigned char *s = NULL;
unsigned int h = 0;
unsigned int g;
unsigned int p = 0;
const struct in6_addr *in6;
@ -239,12 +238,9 @@ isc_sockaddr_hash(const isc_sockaddr_t *sockaddr, isc_boolean_t address_only) {
p = 0;
}
h = isc_hash_calc(s, length, ISC_TRUE);
if (!address_only) {
g = isc_hash_calc((const unsigned char *)&p, sizeof(p),
ISC_TRUE);
h = h ^ g; /* XXX: we should concatenate h and p first */
}
h = isc_hash_function(s, length, ISC_TRUE, NULL);
if (!address_only)
h = isc_hash_function(&p, sizeof(p), ISC_TRUE, &h);
return (h);
}

View file

@ -274,6 +274,8 @@ isc_hash_ctxcreate
isc_hash_ctxdetach
isc_hash_ctxinit
isc_hash_destroy
isc_hash_function
isc_hash_function_reverse
isc_hash_init
isc_heap_create
isc_heap_decreased