From 575737b964590103f60542849d4536b5ccd4eb3c Mon Sep 17 00:00:00 2001 From: Brian Wellington Date: Fri, 29 Dec 2000 00:59:41 +0000 Subject: [PATCH] Rewrite the name compression code to use a hashtable instead of an RBT and avoid allocating memory in most cases. This increases performance by about 225% on the rc query test (~305 q/s to 990 q/s). --- lib/dns/compress.c | 334 +++++++++++---------------------- lib/dns/include/dns/compress.h | 95 +++++----- lib/dns/name.c | 13 +- 3 files changed, 162 insertions(+), 280 deletions(-) diff --git a/lib/dns/compress.c b/lib/dns/compress.c index 4857014eb2..7d8a96dd29 100644 --- a/lib/dns/compress.c +++ b/lib/dns/compress.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: compress.c,v 1.36 2000/11/14 23:29:49 bwelling Exp $ */ +/* $Id: compress.c,v 1.37 2000/12/29 00:59:38 bwelling Exp $ */ #define DNS_NAME_USEINLINE 1 @@ -36,17 +36,12 @@ #define DCTX_MAGIC 0x44435458U /* DCTX */ #define VALID_DCTX(x) ISC_MAGIC_VALID(x, DCTX_MAGIC) -static void -free_offset(void *offset, void *mctx); - static isc_boolean_t -compress_find(dns_rbt_t *root, dns_name_t *name, dns_name_t *prefix, - dns_name_t *suffix, isc_uint16_t *offset, - isc_buffer_t *workspace); +compress_find(dns_compress_t *cctx, dns_name_t *name, dns_name_t *prefix, + dns_name_t *suffix, isc_uint16_t *offset); static void -compress_add(dns_rbt_t *root, dns_name_t *prefix, dns_name_t *suffix, - isc_uint16_t offset, isc_boolean_t global16, isc_mem_t *mctx); +compress_add(dns_compress_t *cctx, dns_name_t *name, isc_uint16_t offset); /*** *** Compression @@ -54,35 +49,44 @@ compress_add(dns_rbt_t *root, dns_name_t *prefix, dns_name_t *suffix, isc_result_t dns_compress_init(dns_compress_t *cctx, int edns, isc_mem_t *mctx) { - isc_result_t result; + unsigned int i; REQUIRE(cctx != NULL); REQUIRE(mctx != NULL); cctx->allowed = 0; cctx->rdata = 0; - cctx->global16 = ISC_FALSE; cctx->edns = edns; - cctx->global = NULL; - result = dns_rbt_create(mctx, free_offset, mctx, &cctx->global); - if (result != ISC_R_SUCCESS) - return (result); + for (i = 0; i < DNS_COMPRESS_TABLESIZE; i++) + cctx->table[i] = NULL; cctx->mctx = mctx; + cctx->count = 0; cctx->magic = CCTX_MAGIC; return (ISC_R_SUCCESS); } void dns_compress_invalidate(dns_compress_t *cctx) { + dns_compressnode_t *node; + unsigned int i; REQUIRE(VALID_CCTX(cctx)); cctx->magic = 0; - if (cctx->global != NULL) - dns_rbt_destroy(&cctx->global); + if (cctx->table != NULL) { + for (i = 0; i < DNS_COMPRESS_TABLESIZE; i++) { + while (cctx->table[i] != NULL) { + node = cctx->table[i]; + cctx->table[i] = cctx->table[i]->next; + if (node->count < DNS_COMPRESS_INITIALNODES) + continue; + isc_mem_put(cctx->mctx, node, + sizeof(dns_compressnode_t)); + } + } + } cctx->allowed = 0; cctx->rdata = 0; - cctx->global16 = ISC_FALSE; cctx->edns = -1; } @@ -108,86 +112,41 @@ dns_compress_getedns(dns_compress_t *cctx) { isc_boolean_t dns_compress_findglobal(dns_compress_t *cctx, dns_name_t *name, dns_name_t *prefix, dns_name_t *suffix, - isc_uint16_t *offset, isc_buffer_t *workspace) + isc_uint16_t *offset) { REQUIRE(VALID_CCTX(cctx)); REQUIRE(dns_name_isabsolute(name) == ISC_TRUE); REQUIRE(offset != NULL); - return (compress_find(cctx->global, name, prefix, suffix, offset, - workspace)); + return (compress_find(cctx, name, prefix, suffix, offset)); } void -dns_compress_add(dns_compress_t *cctx, dns_name_t *prefix, - dns_name_t *suffix, isc_uint16_t offset) -{ +dns_compress_add(dns_compress_t *cctx, dns_name_t *name, isc_uint16_t offset) { REQUIRE(VALID_CCTX(cctx)); + REQUIRE(dns_name_isabsolute(name)); - compress_add(cctx->global, prefix, suffix, offset, - cctx->global16, cctx->mctx); + compress_add(cctx, name, offset); } void dns_compress_rollback(dns_compress_t *cctx, isc_uint16_t offset) { - dns_rbtnode_t *node; - dns_fixedname_t foundfixed; - dns_fixedname_t fullfixed; - dns_fixedname_t originfixed; - dns_name_t *foundname; - dns_name_t *fullname; - dns_name_t *origin; - dns_rbtnodechain_t chain; - isc_result_t result; + unsigned int i; + dns_compressnode_t *node; REQUIRE(VALID_CCTX(cctx)); - /* - * Initalise things. - */ - dns_fixedname_init(&foundfixed); - foundname = dns_fixedname_name(&foundfixed); - dns_fixedname_init(&fullfixed); - fullname = dns_fixedname_name(&fullfixed); - dns_fixedname_init(&originfixed); - origin = dns_fixedname_name(&originfixed); - dns_rbtnodechain_init(&chain, cctx->mctx); - - again: - result = dns_rbtnodechain_first(&chain, cctx->global, foundname, - origin); - - while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { - result = dns_rbtnodechain_current(&chain, foundname, - origin, &node); - - if (result != ISC_R_SUCCESS) - break; - - if (node->data != NULL && - (*(isc_uint16_t*)node->data >= offset)) { - result = dns_name_concatenate(foundname, - dns_name_isabsolute(foundname) ? - NULL : origin, - fullname, NULL); - - if (result != ISC_R_SUCCESS) - break; - - result = dns_rbt_deletename(cctx->global, fullname, - ISC_FALSE); - if (result != ISC_R_SUCCESS) - break; - /* - * If the delete is successful the chain is broken. - */ - dns_rbtnodechain_reset(&chain); - goto again; + for (i = 0; i < DNS_COMPRESS_TABLESIZE; i++) { + node = cctx->table[i]; + while (node != NULL && node->offset >= offset) { + cctx->table[i] = node->next; + if (node->count >= DNS_COMPRESS_INITIALNODES) + isc_mem_put(cctx->mctx, node, + sizeof(dns_compressnode_t)); + cctx->count--; + node = cctx->table[i]; } - - result = dns_rbtnodechain_next(&chain, foundname, origin); } - dns_rbtnodechain_invalidate(&chain); } /*** @@ -262,177 +221,102 @@ dns_decompress_type(dns_decompress_t *dctx) { *** Private ***/ -static void -free_offset(void *offset, void *mctx) { - REQUIRE(offset != NULL); - REQUIRE(mctx != NULL); - isc_mem_put(mctx, offset, sizeof(isc_uint16_t)); -} - /* - * Add the labels in prefix to RBT. + * Add the labels in prefix to the compression table. */ static void -compress_add(dns_rbt_t *root, dns_name_t *prefix, dns_name_t *suffix, - isc_uint16_t offset, isc_boolean_t global16, isc_mem_t *mctx) -{ - - dns_name_t name; - dns_name_t full; +compress_add(dns_compress_t *cctx, dns_name_t *name, isc_uint16_t offset) { + dns_name_t tname, nname; dns_label_t label; - unsigned int count; unsigned int start; unsigned int n; - isc_uint16_t *data; - isc_result_t result; - unsigned char buffer[255]; - isc_buffer_t target; - dns_offsets_t offsets; + unsigned int hash; + dns_compressnode_t *node; - count = dns_name_countlabels(prefix); - if (dns_name_isabsolute(prefix)) - count--; + dns_name_init(&tname, NULL); + dns_name_init(&nname, NULL); + + n = dns_name_countlabels(name); start = 0; - dns_name_init(&full, offsets); - dns_name_init(&name, NULL); - isc_buffer_init(&target, buffer, sizeof(buffer)); - result = dns_name_concatenate(prefix, suffix, &full, &target); - if (result != ISC_R_SUCCESS) - return; - n = dns_name_countlabels(&full); - while (count > 0) { - if (offset >= 16384 && !global16) + while (n > 1) { + if (offset >= 0x4000) break; - dns_name_getlabelsequence(&full, start, n, &name); - data = isc_mem_get(mctx, sizeof *data); - if (data == NULL) - return; - *data = offset; - result = dns_rbt_addname(root, &name, data); - if (result != ISC_R_SUCCESS) { - isc_mem_put(mctx, data, sizeof *data); - return; + dns_name_getlabelsequence(name, start, n, &tname); + hash = dns_name_hash(&tname, ISC_FALSE) % DNS_COMPRESS_TABLESIZE; + /* + * Look for the name in the hash bucket. If it's there, + * we're done. + */ + for (node = cctx->table[hash]; node != NULL; node = node->next) + { + dns_name_fromregion(&nname, &node->r); + if (dns_name_equal(&nname, &tname)) + return; } - dns_name_getlabel(&name, 0, &label); + /* + * It's not there. Create a new node and add it. + */ + if (cctx->count < DNS_COMPRESS_INITIALNODES) + node = &cctx->initialnodes[cctx->count]; + else { + node = isc_mem_get(cctx->mctx, + sizeof(dns_compressnode_t)); + if (node == NULL) + return; + } + node->count = cctx->count++; + node->offset = offset; + dns_name_toregion(&tname, &node->r); + node->next = cctx->table[hash]; + cctx->table[hash] = node; + dns_name_getlabel(&tname, 0, &label); offset += label.length; start++; n--; - count--; } } /* - * Find the longest match of name in root. - * If match is found return ISC_TRUE. prefix, suffix and offset - * are updated. - * If no match is found return ISC_FALSE. + * Find the longest match of name in the table. + * If match is found return ISC_TRUE. prefix, suffix and offset are updated. + * If no match is found return ISC_FALSE. */ - static isc_boolean_t -compress_find(dns_rbt_t *root, dns_name_t *name, dns_name_t *prefix, - dns_name_t *suffix, isc_uint16_t *offset, - isc_buffer_t *workspace) +compress_find(dns_compress_t *cctx, + dns_name_t *name, dns_name_t *prefix, + dns_name_t *suffix, isc_uint16_t *offset) { - dns_fixedname_t found; - dns_name_t *foundname; - dns_name_t tmpprefix; - dns_name_t tmpsuffix; - isc_result_t result; - isc_uint16_t *data = NULL; - dns_label_t foundlabel; - dns_label_t namelabel; - unsigned int foundlabels; - unsigned int namelabels; - unsigned int foundbits; - unsigned int namebits; - unsigned int bits; - unsigned int prefixlen; - unsigned int j; - unsigned char buf[2 + 256/8]; /* size of biggest bit label */ - dns_bitlabel_t bit; - isc_region_t region; + dns_name_t tname, nname; + dns_compressnode_t *node; + unsigned int labels, hash, n; - dns_fixedname_init(&found); - foundname = dns_fixedname_name(&found); - /* - * Getting rid of the offsets table for foundname improves - * perfomance, since the offsets table is not needed and maintaining - * it has costs. - */ - foundname->offsets = NULL; - result = dns_rbt_findname(root, name, 0, foundname, (void *)&data); - if (result != ISC_R_SUCCESS && result != DNS_R_PARTIALMATCH) - return (ISC_FALSE); - if (data == NULL) /* root label */ - return (ISC_FALSE); - /* - * Do we have to do bit string processing? - */ - dns_name_getlabel(foundname, 0, &foundlabel); - foundlabels = dns_name_countlabels(foundname); - INSIST(foundlabels > 1); /* root labels are not added to tree */ - namelabels = dns_name_countlabels(name); - if (dns_label_type(&foundlabel) == dns_labeltype_bitstring) { - dns_name_getlabel(name, namelabels - foundlabels, &namelabel); - INSIST(dns_label_type(&namelabel) == dns_labeltype_bitstring); - foundbits = dns_label_countbits(&foundlabel); - namebits = dns_label_countbits(&namelabel); - } else - namebits = foundbits = 0; + labels = dns_name_countlabels(name); + INSIST(labels > 0); - if (namebits == foundbits) { - INSIST(namelabels >= foundlabels); - prefixlen = namelabels - foundlabels; - if (prefixlen == 0) { - prefix->length = 0; - prefix->labels = 0; - } else - dns_name_getlabelsequence(name, 0, prefixlen, prefix); - result = dns_name_concatenate(NULL, foundname, suffix, - workspace); - if (result != ISC_R_SUCCESS) - return (ISC_FALSE); - *offset = *data; - return (ISC_TRUE); + dns_name_init(&tname, NULL); + dns_name_init(&nname, NULL); + + for (n = 0; n < labels - 1; n++) { + dns_name_getlabelsequence(name, n, labels - n, &tname); + hash = dns_name_hash(&tname, ISC_FALSE) % DNS_COMPRESS_TABLESIZE; + for (node = cctx->table[hash]; node != NULL; node = node->next) + { + dns_name_fromregion(&nname, &node->r); + if (dns_name_equal(&nname, &tname)) + break; + } + if (node != NULL) + break; } + /* - * At this stage we have a bit string label to split in two. - * There is potentially a prefix before this label and definitly - * a suffix after it (if only the root). + * If node == NULL, we found no match at all. */ - INSIST(result == DNS_R_PARTIALMATCH); - result = dns_name_concatenate(NULL, foundname, suffix, workspace); - if (result != ISC_R_SUCCESS) + if (node == NULL) return (ISC_FALSE); - prefixlen = namelabels - foundlabels; - dns_name_init(&tmpprefix, NULL); - dns_name_init(&tmpsuffix, NULL); - if (prefixlen != 0) { - dns_name_getlabelsequence(name, 0, prefixlen, &tmpprefix); - } - INSIST(namebits > foundbits); - bits = namebits - foundbits; - j = 0; - memset(buf, 0, sizeof buf); - INSIST((bits / 8 + 1) < sizeof buf); - /* - * Copy least significant bits. - */ - while (j < bits) { - bit = dns_label_getbit(&namelabel, foundbits + j); - if (bit) - buf[2 + j / 8] |= (1 << (7 - (j % 8))); - j++; - } - buf[0] = DNS_LABELTYPE_BITSTRING; - buf[1] = j; - region.base = buf; - region.length = 2 + (j + 7) / 8; - dns_name_fromregion(&tmpsuffix, ®ion); - result = dns_name_concatenate(&tmpprefix, &tmpsuffix, prefix, - workspace); - if (result != ISC_R_SUCCESS) - return (ISC_FALSE); - *offset = *data; + + dns_name_clone(&tname, suffix); + dns_name_getlabelsequence(name, 0, n, prefix); + *offset = node->offset; return (ISC_TRUE); } diff --git a/lib/dns/include/dns/compress.h b/lib/dns/include/dns/compress.h index c466544c7d..656f7cdd22 100644 --- a/lib/dns/include/dns/compress.h +++ b/lib/dns/include/dns/compress.h @@ -15,12 +15,13 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: compress.h,v 1.20 2000/12/28 00:42:56 bwelling Exp $ */ +/* $Id: compress.h,v 1.21 2000/12/29 00:59:41 bwelling Exp $ */ #ifndef DNS_COMPRESS_H #define DNS_COMPRESS_H 1 #include +#include #include @@ -28,28 +29,35 @@ ISC_LANG_BEGINDECLS #define DNS_COMPRESS_NONE 0x00 /* no compression */ #define DNS_COMPRESS_GLOBAL14 0x01 /* "normal" compression. */ -#define DNS_COMPRESS_GLOBAL 0x01 /* all global comp. */ -/* - * Synonymous with DNS_COMPRESS_GLOBAL. A genuine difference existed when - * local compression was an IETF draft, but that draft has been retired without - * becoming a standard. Numerous bits of code referred to DNS_COMPRESS_ALL - * already, and rather than change them all, the DNS_COMPRESS_ALL definition - * was left in, but no longer refers to local compression. - */ -#define DNS_COMPRESS_ALL 0x03 /* all compression. */ +#define DNS_COMPRESS_ALL 0x01 /* all compression. */ /* * Direct manipulation of the structures is strongly discouraged. */ +#define DNS_COMPRESS_TABLESIZE 64 +#define DNS_COMPRESS_INITIALNODES 16 + +typedef struct dns_compressnode dns_compressnode_t; + +struct dns_compressnode { + isc_region_t r; + isc_uint16_t offset; + isc_uint16_t count; + dns_compressnode_t *next; +}; + struct dns_compress { - unsigned int magic; /* Magic number. */ - unsigned int allowed; /* Allowed methods. */ - unsigned int rdata; /* Start of local rdata. */ - isc_boolean_t global16; /* 16 bit offsets allowed. */ - int edns; /* Edns version or -1. */ - dns_rbt_t *global; /* Global RBT. */ - isc_mem_t *mctx; /* Memeory context. */ + unsigned int magic; /* Magic number. */ + unsigned int allowed; /* Allowed methods. */ + unsigned int rdata; /* Start of local rdata. */ + int edns; /* Edns version or -1. */ + /* Global compression table. */ + dns_compressnode_t *table[DNS_COMPRESS_TABLESIZE]; + /* Preallocated nodes for the table. */ + dns_compressnode_t initialnodes[DNS_COMPRESS_INITIALNODES]; + isc_uint16_t count; /* Number of nodes. */ + isc_mem_t *mctx; /* Memory context. */ }; typedef enum { @@ -73,9 +81,9 @@ dns_compress_init(dns_compress_t *cctx, int edns, isc_mem_t *mctx); * * Requires: * 'cctx' is a valid dns_compress_t structure. - * 'mctx' is a initalised memory context. + * 'mctx' is an initialized memory context. * Ensures: - * cctx->global is initalised. + * cctx->global is initialized. * * Returns: * ISC_R_SUCCESS @@ -87,10 +95,9 @@ dns_compress_invalidate(dns_compress_t *cctx); /* * Invalidate the compression structure pointed to by cctx. - * Destroys 'cctx->glocal' and 'cctx->local' RBT. * * Requires: - * 'cctx' to be initalised. + * 'cctx' to be initialized. */ void @@ -100,7 +107,7 @@ dns_compress_setmethods(dns_compress_t *cctx, unsigned int allowed); * Sets allowed compression methods. * * Requires: - * 'cctx' to be initalised. + * 'cctx' to be initialized. */ unsigned int @@ -110,7 +117,7 @@ dns_compress_getmethods(dns_compress_t *cctx); * Gets allowed compression methods. * * Requires: - * 'cctx' to be initalised. + * 'cctx' to be initialized. * * Returns: * allowed compression bitmap. @@ -123,7 +130,7 @@ dns_compress_getedns(dns_compress_t *cctx); * Gets edns value. * * Requires: - * 'cctx' to be initalised. + * 'cctx' to be initialized. * * Returns: * -1 .. 255 @@ -132,19 +139,16 @@ dns_compress_getedns(dns_compress_t *cctx); isc_boolean_t dns_compress_findglobal(dns_compress_t *cctx, dns_name_t *name, dns_name_t *prefix, dns_name_t *suffix, - isc_uint16_t *offset, isc_buffer_t *workspace); + isc_uint16_t *offset); /* - * Finds longest possible match of 'name' in the global compression - * RBT. Workspace needs to be large enough to hold 'name' when split - * in two (length->name + 3). + * Finds longest possible match of 'name' in the global compression table. * * Requires: - * 'cctx' to be initalised. + * 'cctx' to be initialized. * 'name' to be a absolute name. - * 'prefix' to be initalised. - * 'suffix' to be initalised. + * 'prefix' to be initialized. + * 'suffix' to be initialized. * 'offset' to point it a isc_uint16_t. - * 'workspace' to be initalised. * * Ensures: * 'prefix', 'suffix' and 'offset' are valid if ISC_TRUE is @@ -155,27 +159,24 @@ dns_compress_findglobal(dns_compress_t *cctx, dns_name_t *name, */ void -dns_compress_add(dns_compress_t *cctx, dns_name_t *prefix, - dns_name_t *suffix, isc_uint16_t offset); +dns_compress_add(dns_compress_t *cctx, dns_name_t *name, isc_uint16_t offset); /* - * Add compression pointers for labels in prefix to RBT's. - * If 'prefix' is absolute 'suffix' must be NULL otherwise - * suffix must be absolute. + * Add compression pointers for name to the compression table, + * not replacing existing pointers. * * Requires: - * 'cctx' initalised - * 'prefix' to be initalised - * 'suffix' to be initalised or NULL + * 'cctx' initialized + * 'name' must be initialized and absolute */ void dns_compress_rollback(dns_compress_t *cctx, isc_uint16_t offset); /* - * Remove any compression pointers from global RBT >= offset. + * Remove any compression pointers from global table >= offset. * * Requires: - * 'cctx' is initalised. + * 'cctx' is initialized. */ void @@ -197,7 +198,7 @@ dns_decompress_invalidate(dns_decompress_t *dctx); * Invalidates 'dctx'. * * Requires: - * 'dctx' to be initalised + * 'dctx' to be initialized */ void @@ -207,7 +208,7 @@ dns_decompress_setmethods(dns_decompress_t *dctx, unsigned int allowed); * Sets 'dctx->allowed' to 'allowed'. * * Requires: - * 'dctx' to be initalised + * 'dctx' to be initialized */ unsigned int @@ -217,7 +218,7 @@ dns_decompress_getmethods(dns_decompress_t *dctx); * Returns 'dctx->allowed' * * Requires: - * 'dctx' to be initalised + * 'dctx' to be initialized */ int @@ -227,7 +228,7 @@ dns_decompress_edns(dns_decompress_t *dctx); * Returns 'dctx->edns' * * Requires: - * 'dctx' to be initalised + * 'dctx' to be initialized */ dns_decompresstype_t @@ -237,7 +238,7 @@ dns_decompress_type(dns_decompress_t *dctx); * Returns 'dctx->type' * * Requires: - * 'dctx' to be initalised + * 'dctx' to be initialized */ ISC_LANG_ENDDECLS diff --git a/lib/dns/name.c b/lib/dns/name.c index 55c2e1306c..044ce1d3ac 100644 --- a/lib/dns/name.c +++ b/lib/dns/name.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: name.c,v 1.110 2000/12/28 00:42:55 bwelling Exp $ */ +/* $Id: name.c,v 1.111 2000/12/29 00:59:39 bwelling Exp $ */ #include @@ -2385,8 +2385,6 @@ dns_name_towire(dns_name_t *name, dns_compress_t *cctx, isc_buffer_t *target) { dns_name_t gp, gs; isc_boolean_t gf; isc_uint16_t go; - unsigned char gb[257]; - isc_buffer_t gws; dns_offsets_t po, so, clo; dns_name_t clname; @@ -2409,14 +2407,13 @@ dns_name_towire(dns_name_t *name, dns_compress_t *cctx, isc_buffer_t *target) { } dns_name_init(&gp, po); dns_name_init(&gs, so); - isc_buffer_init(&gws, gb, sizeof (gb)); offset = target->used; /*XXX*/ methods = dns_compress_getmethods(cctx); - if ((methods & DNS_COMPRESS_GLOBAL) != 0) - gf = dns_compress_findglobal(cctx, name, &gp, &gs, &go, &gws); + if ((methods & DNS_COMPRESS_GLOBAL14) != 0) + gf = dns_compress_findglobal(cctx, name, &gp, &gs, &go); else gf = ISC_FALSE; @@ -2444,14 +2441,14 @@ dns_name_towire(dns_name_t *name, dns_compress_t *cctx, isc_buffer_t *target) { return (ISC_R_NOSPACE); isc_buffer_putuint16(target, go); if (gp.length != 0) - dns_compress_add(cctx, &gp, &gs, offset); + dns_compress_add(cctx, name, offset); } else { if (target->length - target->used < name->length) return (ISC_R_NOSPACE); (void)memcpy((unsigned char *)target->base + target->used, name->ndata, (size_t)name->length); isc_buffer_add(target, name->length); - dns_compress_add(cctx, name, NULL, offset); + dns_compress_add(cctx, name, offset); } return (ISC_R_SUCCESS); }