On Mon, 2001-11-19 at 22:38, Brian Pane wrote: > Here's the latest revision of my apr_table_t optimizations. > It's the same as the last one, except that the hash table in > apr_table_overlap() now uses a red-black tree rather than > a linear list to hold multiple entries per hash bucket, so > that the worst-case run time of apr_table_overlap() is > O(n*log(n)) instead of O(n^2). > > --Brian Committed. Thanks Brian > > > ---- >
> Index: modules/arch/win32/mod_isapi.c > =================================================================== > RCS file: /home/cvs/httpd-2.0/modules/arch/win32/mod_isapi.c,v > retrieving revision 1.51 > diff -u -r1.51 mod_isapi.c > --- modules/arch/win32/mod_isapi.c 2001/11/11 22:31:03 1.51 > +++ modules/arch/win32/mod_isapi.c 2001/11/20 06:19:11 > @@ -567,13 +567,15 @@ > /* lf delimited, colon split, comma seperated and > * null terminated list of HTTP_ vars > */ > - const char * const *env = (const char* const *) >apr_table_elts(r->subprocess_env)->elts; > - int nelts = 2 * apr_table_elts(r->subprocess_env)->nelts; > + const apr_array_header_t *arr = apr_table_elts(r->subprocess_env); > + const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts; > int i; > > - for (len = 0, i = 0; i < nelts; i += 2) > - if (!strncmp(env[i], "HTTP_", 5)) > - len += strlen(env[i]) + strlen(env[i + 1]) + 2; > + for (len = 0, i = 0; i < arr->nelts; i++) { > + if (!strncmp(elts[i].key, "HTTP_", 5)) { > + len += strlen(elts[i].key) + strlen(elts[i].val) + 2; > + } > + } > > if (*lpdwSizeofBuffer < len + 1) { > *lpdwSizeofBuffer = len + 1; > @@ -581,15 +583,16 @@ > return FALSE; > } > > - for (i = 0; i < nelts; i += 2) > - if (!strncmp(env[i], "HTTP_", 5)) { > - strcpy(lpvBuffer, env[i]); > - ((char*)lpvBuffer) += strlen(env[i]); > + for (i = 0; i < arr->nelts; i++) { > + if (!strncmp(elts[i].key, "HTTP_", 5)) { > + strcpy(lpvBuffer, elts[i].key); > + ((char*)lpvBuffer) += strlen(elts[i].key); > *(((char*)lpvBuffer)++) = ':'; > - strcpy(lpvBuffer, env[i + 1]); > - ((char*)lpvBuffer) += strlen(env[i + 1]); > + strcpy(lpvBuffer, elts[i].val); > + ((char*)lpvBuffer) += strlen(elts[i].val); > *(((char*)lpvBuffer)++) = '\n'; > } > + } > > *(((char*)lpvBuffer)++) = '\0'; > *lpdwSizeofBuffer = len; > @@ -601,12 +604,13 @@ > /* lf delimited, colon split, comma seperated and > * null terminated list of the raw request header > */ > - const char * const *raw = (const char* const *) >apr_table_elts(r->headers_in)->elts; > - int nelts = 2 * apr_table_elts(r->headers_in)->nelts; > + const apr_array_header_t *arr = apr_table_elts(r->headers_in); > + const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts; > int i; > > - for (len = 0, i = 0; i < nelts; i += 2) > - len += strlen(raw[i]) + strlen(raw[i + 1]) + 2; > + for (len = 0, i = 0; i < arr->nelts; i++) { > + len += strlen(elts[i].key) + strlen(elts[i].val) + 2; > + } > > if (*lpdwSizeofBuffer < len + 1) { > *lpdwSizeofBuffer = len + 1; > @@ -614,15 +618,14 @@ > return FALSE; > } > > - for (i = 0; i < nelts; i += 2) { > - strcpy(lpvBuffer, raw[i]); > - ((char*)lpvBuffer) += strlen(raw[i]); > + for (i = 0; i < arr->nelts; i++) { > + strcpy(lpvBuffer, elts[i].key); > + ((char*)lpvBuffer) += strlen(elts[i].key); > *(((char*)lpvBuffer)++) = ':'; > *(((char*)lpvBuffer)++) = ' '; > - strcpy(lpvBuffer, raw[i + 1]); > - ((char*)lpvBuffer) += strlen(raw[i + 1]); > + strcpy(lpvBuffer, elts[i].val); > + ((char*)lpvBuffer) += strlen(elts[i].val); > *(((char*)lpvBuffer)++) = '\n'; > - i += 2; > } > *(((char*)lpvBuffer)++) = '\0'; > *lpdwSizeofBuffer = len; > Index: srclib/apr/include/apr_tables.h > =================================================================== > RCS file: /home/cvspublic/apr/include/apr_tables.h,v > retrieving revision 1.24 > diff -u -r1.24 apr_tables.h > --- srclib/apr/include/apr_tables.h 2001/11/11 22:31:04 1.24 > +++ srclib/apr/include/apr_tables.h 2001/11/20 06:19:11 > @@ -130,6 +130,9 @@ > */ > /** The value for the current table entry */ > char *val; > + > + /** A checksum for the key, for use by the apr_table internals */ > + apr_uint32_t key_checksum; > }; > > /** > Index: srclib/apr/tables/apr_tables.c > =================================================================== > RCS file: /home/cvspublic/apr/tables/apr_tables.c,v > retrieving revision 1.17 > diff -u -r1.17 apr_tables.c > --- srclib/apr/tables/apr_tables.c 2001/08/02 03:18:44 1.17 > +++ srclib/apr/tables/apr_tables.c 2001/11/20 06:19:12 > @@ -89,7 +89,7 @@ > */ > > static void make_array_core(apr_array_header_t *res, apr_pool_t *p, > - int nelts, int elt_size) > + int nelts, int elt_size, int clear) > { > /* > * Assure sanity if someone asks for > @@ -99,7 +99,12 @@ > nelts = 1; > } > > - res->elts = apr_pcalloc(p, nelts * elt_size); > + if (clear) { > + res->elts = apr_pcalloc(p, nelts * elt_size); > + } > + else { > + res->elts = apr_palloc(p, nelts * elt_size); > + } > > res->pool = p; > res->elt_size = elt_size; > @@ -113,7 +118,7 @@ > apr_array_header_t *res; > > res = (apr_array_header_t *) apr_palloc(p, sizeof(apr_array_header_t)); > - make_array_core(res, p, nelts, elt_size); > + make_array_core(res, p, nelts, elt_size, 1); > return res; > } > > @@ -277,6 +282,28 @@ > * The "table" functions. > */ > > +#if APR_CHARSET_EBCDIC > +#define CASE_MASK 0xbfbfbfbf > +#else > +#define CASE_MASK 0xdfdfdfdf > +#endif > + > +/* Compute the "checksum" for a key, consisting of the first > + * 4 bytes, normalized for case-insensitivity and packed into > + * an int...this checksum allows us to do a single integer > + * comparison as a fast check to determine whether we can > + * skip a strcasecmp > + */ > +#define COMPUTE_KEY_CHECKSUM(key, checksum) \ > +{ \ > + if ((key)[0] && (key)[1] && (key)[2] && (key)[3]) { \ > + checksum = (*((apr_uint32_t *)(key))) & CASE_MASK; \ > + } \ > + else { \ > + checksum = 0; \ > + } \ > +} > + > /* > * XXX: if you tweak this you should look at is_empty_table() and table_elts() > * in alloc.h > @@ -298,7 +325,7 @@ > { > apr_table_t *t = apr_palloc(p, sizeof(apr_table_t)); > > - make_array_core(&t->a, p, nelts, sizeof(apr_table_entry_t)); > + make_array_core(&t->a, p, nelts, sizeof(apr_table_entry_t), 0); > #ifdef MAKE_TABLE_PROFILE > t->creator = __builtin_return_address(0); > #endif > @@ -318,7 +345,7 @@ > abort(); > } > #endif > - make_array_core(&new->a, p, t->a.nalloc, sizeof(apr_table_entry_t)); > + make_array_core(&new->a, p, t->a.nalloc, sizeof(apr_table_entry_t), 0); > memcpy(new->a.elts, t->a.elts, t->a.nelts * sizeof(apr_table_entry_t)); > new->a.nelts = t->a.nelts; > return new; > @@ -333,13 +360,15 @@ > { > apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; > int i; > + apr_uint32_t checksum; > > if (key == NULL) { > return NULL; > } > > + COMPUTE_KEY_CHECKSUM(key, checksum); > for (i = 0; i < t->a.nelts; ++i) { > - if (!strcasecmp(elts[i].key, key)) { > + if ((checksum == elts[i].key_checksum) && !strcasecmp(elts[i].key, key)) { > return elts[i].val; > } > } > @@ -353,9 +382,11 @@ > register int i, j, k; > apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; > int done = 0; > + apr_uint32_t checksum; > > + COMPUTE_KEY_CHECKSUM(key, checksum); > for (i = 0; i < t->a.nelts; ) { > - if (!strcasecmp(elts[i].key, key)) { > + if ((checksum == elts[i].key_checksum) && !strcasecmp(elts[i].key, key)) { > if (!done) { > elts[i].val = apr_pstrdup(t->a.pool, val); > done = 1; > @@ -365,6 +396,7 @@ > for (j = i, k = i + 1; k < t->a.nelts; ++j, ++k) { > elts[j].key = elts[k].key; > elts[j].val = elts[k].val; > + elts[j].key_checksum = elts[k].key_checksum; > } > --t->a.nelts; > } > @@ -378,6 +410,7 @@ > elts = (apr_table_entry_t *) table_push(t); > elts->key = apr_pstrdup(t->a.pool, key); > elts->val = apr_pstrdup(t->a.pool, val); > + elts->key_checksum = checksum; > } > } > > @@ -387,6 +420,7 @@ > register int i, j, k; > apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; > int done = 0; > + apr_uint32_t checksum; > > #ifdef POOL_DEBUG > { > @@ -401,8 +435,9 @@ > } > #endif > > + COMPUTE_KEY_CHECKSUM(key, checksum); > for (i = 0; i < t->a.nelts; ) { > - if (!strcasecmp(elts[i].key, key)) { > + if ((checksum == elts[i].key_checksum) && !strcasecmp(elts[i].key, key)) { > if (!done) { > elts[i].val = (char *)val; > done = 1; > @@ -412,6 +447,7 @@ > for (j = i, k = i + 1; k < t->a.nelts; ++j, ++k) { > elts[j].key = elts[k].key; > elts[j].val = elts[k].val; > + elts[j].key_checksum = elts[k].key_checksum; > } > --t->a.nelts; > } > @@ -425,6 +461,7 @@ > elts = (apr_table_entry_t *) table_push(t); > elts->key = (char *)key; > elts->val = (char *)val; > + elts->key_checksum = checksum; > } > } > > @@ -432,9 +469,11 @@ > { > register int i, j, k; > apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; > + apr_uint32_t checksum; > > + COMPUTE_KEY_CHECKSUM(key, checksum); > for (i = 0; i < t->a.nelts; ) { > - if (!strcasecmp(elts[i].key, key)) { > + if ((checksum == elts[i].key_checksum) && !strcasecmp(elts[i].key, key)) { > > /* found an element to skip over > * there are any number of ways to remove an element from > @@ -444,6 +483,7 @@ > for (j = i, k = i + 1; k < t->a.nelts; ++j, ++k) { > elts[j].key = elts[k].key; > elts[j].val = elts[k].val; > + elts[j].key_checksum = elts[k].key_checksum; > } > --t->a.nelts; > } > @@ -458,9 +498,11 @@ > { > apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; > int i; > + apr_uint32_t checksum; > > + COMPUTE_KEY_CHECKSUM(key, checksum); > for (i = 0; i < t->a.nelts; ++i) { > - if (!strcasecmp(elts[i].key, key)) { > + if ((checksum == elts[i].key_checksum) && !strcasecmp(elts[i].key, key)) { > elts[i].val = apr_pstrcat(t->a.pool, elts[i].val, ", ", val, NULL); > return; > } > @@ -469,6 +511,7 @@ > elts = (apr_table_entry_t *) table_push(t); > elts->key = apr_pstrdup(t->a.pool, key); > elts->val = apr_pstrdup(t->a.pool, val); > + elts->key_checksum = checksum; > } > > APR_DECLARE(void) apr_table_mergen(apr_table_t *t, const char *key, > @@ -476,6 +519,7 @@ > { > apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; > int i; > + apr_uint32_t checksum; > > #ifdef POOL_DEBUG > { > @@ -490,8 +534,9 @@ > } > #endif > > + COMPUTE_KEY_CHECKSUM(key, checksum); > for (i = 0; i < t->a.nelts; ++i) { > - if (!strcasecmp(elts[i].key, key)) { > + if ((checksum == elts[i].key_checksum) && !strcasecmp(elts[i].key, key)) { > elts[i].val = apr_pstrcat(t->a.pool, elts[i].val, ", ", val, NULL); > return; > } > @@ -500,22 +545,27 @@ > elts = (apr_table_entry_t *) table_push(t); > elts->key = (char *)key; > elts->val = (char *)val; > + elts->key_checksum = checksum; > } > > APR_DECLARE(void) apr_table_add(apr_table_t *t, const char *key, > const char *val) > { > - apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; > + apr_table_entry_t *elts; > + apr_uint32_t checksum; > > + COMPUTE_KEY_CHECKSUM(key, checksum); > elts = (apr_table_entry_t *) table_push(t); > elts->key = apr_pstrdup(t->a.pool, key); > elts->val = apr_pstrdup(t->a.pool, val); > + elts->key_checksum = checksum; > } > > APR_DECLARE(void) apr_table_addn(apr_table_t *t, const char *key, > const char *val) > { > - apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; > + apr_table_entry_t *elts; > + apr_uint32_t checksum; > > #ifdef POOL_DEBUG > { > @@ -530,9 +580,11 @@ > } > #endif > > + COMPUTE_KEY_CHECKSUM(key, checksum); > elts = (apr_table_entry_t *) table_push(t); > elts->key = (char *)key; > elts->val = (char *)val; > + elts->key_checksum = checksum; > } > > APR_DECLARE(apr_table_t *) apr_table_overlay(apr_pool_t *p, > @@ -626,171 +678,333 @@ > int rv, i; > argp = va_arg(vp, char *); > do { > + apr_uint32_t checksum = 0; > + if (argp) { > + COMPUTE_KEY_CHECKSUM(argp, checksum); > + } > for (rv = 1, i = 0; rv && (i < t->a.nelts); ++i) { > - if (elts[i].key && (!argp || !strcasecmp(elts[i].key, argp))) { > + if (elts[i].key && (!argp || > + ((checksum == elts[i].key_checksum) && > + !strcasecmp(elts[i].key, argp)))) { > rv = (*comp) (rec, elts[i].key, elts[i].val); > } > } > } while (argp && ((argp = va_arg(vp, char *)) != NULL)); > } > > -/* Curse libc and the fact that it doesn't guarantee a stable sort. We > - * have to enforce stability ourselves by using the order field. If it > - * provided a stable sort then we wouldn't even need temporary storage to > - * do the work below. -djg > - * > - * ("stable sort" means that equal keys retain their original relative > - * ordering in the output.) > +/* During apr_table_overlap(), we build an overlap key for > + * each element in the two tables. > */ > -typedef struct { > - char *key; > - char *val; > - int order; > +#define RED 0 > +#define BLACK 1 > +typedef struct overlap_key { > + /* The table element */ > + apr_table_entry_t *elt; > + > + /* 0 if from table 'a', 1 if from table 'b' */ > + int level; > + > + /* Whether to omit this element when building the result table */ > + int skip; > + > + /* overlap_keys can be assembled into a red-black tree */ > + int black; > + struct overlap_key *tree_parent; > + struct overlap_key *tree_left; > + struct overlap_key *tree_right; > + int color; > + > + /* List of multiple values for this key */ > + struct overlap_key *merge_next; > + struct overlap_key *merge_last; > } overlap_key; > > -static int sort_overlap(const void *va, const void *vb) > -{ > - const overlap_key *a = va; > - const overlap_key *b = vb; > - int r; > - > - r = strcasecmp(a->key, b->key); > - if (r) { > - return r; > +/* Rotate a subtree of a red-black tree */ > +static void rotate_counterclockwise(overlap_key **root, > + overlap_key *rotate_node) > +{ > + overlap_key *child = rotate_node->tree_right; > + rotate_node->tree_right = child->tree_left; > + if (rotate_node->tree_right) { > + rotate_node->tree_right->tree_parent = rotate_node; > + } > + child->tree_parent = rotate_node->tree_parent; > + if (child->tree_parent == NULL) { > + *root = child; > } > - return a->order - b->order; > + else { > + if (rotate_node == rotate_node->tree_parent->tree_left) { > + rotate_node->tree_parent->tree_left = child; > + } > + else { > + rotate_node->tree_parent->tree_right = child; > + } > + } > + child->tree_left = rotate_node; > + rotate_node->tree_parent = child; > } > > -/* prefer to use the stack for temp storage for overlaps smaller than this */ > -#ifndef APR_OVERLAP_TABLES_ON_STACK > -#define APR_OVERLAP_TABLES_ON_STACK (512) > -#endif > - > -APR_DECLARE(void) apr_table_overlap(apr_table_t *a, const apr_table_t *b, > - unsigned flags) > +static void rotate_clockwise(overlap_key **root, overlap_key *rotate_node) > { > - overlap_key cat_keys_buf[APR_OVERLAP_TABLES_ON_STACK]; > - overlap_key *cat_keys; > - int nkeys; > - apr_table_entry_t *e; > - apr_table_entry_t *last_e; > - overlap_key *left; > - overlap_key *right; > - overlap_key *last; > - > - nkeys = a->a.nelts + b->a.nelts; > - if (nkeys < APR_OVERLAP_TABLES_ON_STACK) { > - cat_keys = cat_keys_buf; > + overlap_key *child = rotate_node->tree_left; > + rotate_node->tree_left = child->tree_right; > + if (rotate_node->tree_left) { > + rotate_node->tree_left->tree_parent = rotate_node; > + } > + child->tree_parent = rotate_node->tree_parent; > + if (child->tree_parent == NULL) { > + *root = child; > } > else { > - /* XXX: could use scratch free space in a or b's pool instead... > - * which could save an allocation in b's pool. > - */ > - cat_keys = apr_palloc(b->a.pool, sizeof(overlap_key) * nkeys); > + if (rotate_node == rotate_node->tree_parent->tree_left) { > + rotate_node->tree_parent->tree_left = child; > + } > + else { > + rotate_node->tree_parent->tree_right = child; > + } > } > + child->tree_right = rotate_node; > + rotate_node->tree_parent = child; > +} > > - nkeys = 0; > > - /* Create a list of the entries from a concatenated with the entries > - * from b. > +static void overlap_hash(overlap_key *elt, > + overlap_key **hash_table, int nhash, > + unsigned flags) > +{ > + /* Each bucket in the hash table holds a red-black tree > + * containing the overlap_keys that hash into that bucket > */ > - e = (apr_table_entry_t *)a->a.elts; > - last_e = e + a->a.nelts; > - while (e < last_e) { > - cat_keys[nkeys].key = e->key; > - cat_keys[nkeys].val = e->val; > - cat_keys[nkeys].order = nkeys; > - ++nkeys; > - ++e; > - } > - > - e = (apr_table_entry_t *)b->a.elts; > - last_e = e + b->a.nelts; > - while (e < last_e) { > - cat_keys[nkeys].key = e->key; > - cat_keys[nkeys].val = e->val; > - cat_keys[nkeys].order = nkeys; > - ++nkeys; > - ++e; > + overlap_key **child = &(hash_table[elt->elt->key_checksum & (nhash - 1)]); > + overlap_key **root = child; > + overlap_key *parent = NULL; > + overlap_key *next; > + > + /* Look for the element in the tree */ > + while ((next = *child) != NULL) { > + int direction = strcasecmp(elt->elt->key, next->elt->key); > + if (direction < 0) { > + parent = next; > + child = &(next->tree_left); > + } > + else if (direction > 0) { > + parent = next; > + child = &(next->tree_right); > + } > + else { > + /* This is the key we're looking for */ > + if (flags == APR_OVERLAP_TABLES_MERGE) { > + /* Just link this node at the end of the list > + * of values for the key. It doesn't need to > + * be linked into the tree, becaue the node at > + * the head of this key's value list is in the > + * tree already. > + */ > + elt->skip = 1; > + elt->merge_next = NULL; > + if (next->merge_last) { > + next->merge_last->merge_next = elt; > + } > + else { > + next->merge_next = elt; > + } > + next->merge_last = elt; > + } > + else { > + /* In the "set" case, don't bother storing > + * this value in the tree if it's already > + * there, except if the previous value was > + * from table 'a' (level==0) and this value > + * is from table 'b' (level==1) > + */ > + if (elt->level > next->level) { > + elt->tree_left = next->tree_left; > + if (next->tree_left) { > + next->tree_left->tree_parent = elt; > + } > + elt->tree_right = next->tree_right; > + if (next->tree_right) { > + next->tree_right->tree_parent = elt; > + } > + elt->tree_parent = next->tree_parent; > + (*child) = elt; > + elt->merge_next = NULL; > + elt->merge_last = NULL; > + elt->skip = 0; > + next->skip = 1; > + } > + else { > + elt->skip = 1; > + } > + } > + return; > + } > } > - > - qsort(cat_keys, nkeys, sizeof(overlap_key), sort_overlap); > > - /* Now iterate over the sorted list and rebuild a. > - * Start by making sure it has enough space. > + /* The element wasn't in the tree, so add it */ > + elt->tree_left = NULL; > + elt->tree_right = NULL; > + elt->tree_parent = parent; > + (*child) = elt; > + elt->merge_next = NULL; > + elt->merge_last = NULL; > + elt->skip = 0; > + elt->color = RED; > + > + /* Shuffle the nodes to maintain the red-black tree's balanced > + * shape property. (This is what guarantees O(n*log(n)) worst-case > + * performance for apr_table_overlap().) > */ > - a->a.nelts = 0; > - if (a->a.nalloc < nkeys) { > - a->a.elts = apr_palloc(a->a.pool, a->a.elt_size * nkeys * 2); > - a->a.nalloc = nkeys * 2; > + next = elt; > + while ((next->tree_parent) && (next->tree_parent->color == RED)) { > + /* Note: Root is always black, and red and black nodes > + * alternate on any path down the tree. So if we're inside > + * this block, the grandparent node is non-NULL. > + */ > + overlap_key *grandparent = next->tree_parent->tree_parent; > + if (next->tree_parent == grandparent->tree_left) { > + overlap_key *parent_sibling = grandparent->tree_right; > + if (parent_sibling && (parent_sibling->color == RED)) { > + next->tree_parent->color = BLACK; > + parent_sibling->color = BLACK; > + grandparent->color = RED; > + next = grandparent; > + } > + else { > + if (next == next->tree_parent->tree_right) { > + next = next->tree_parent; > + rotate_counterclockwise(root, next); > + } > + next->tree_parent->color = BLACK; > + next->tree_parent->tree_parent->color = RED; > + rotate_clockwise(root, next->tree_parent->tree_parent); > + } > + } > + else { > + overlap_key *parent_sibling = grandparent->tree_left; > + if (parent_sibling && (parent_sibling->color == RED)) { > + next->tree_parent->color = BLACK; > + parent_sibling->color = BLACK; > + grandparent->color = RED; > + next = grandparent; > + } > + else { > + if (next == next->tree_parent->tree_left) { > + next = next->tree_parent; > + rotate_clockwise(root, next); > + } > + next->tree_parent->color = BLACK; > + next->tree_parent->tree_parent->color = RED; > + rotate_counterclockwise(root, next->tree_parent->tree_parent); > + } > + } > } > + (*root)->color = BLACK; > +} > > - /* > - * In both the merge and set cases we retain the invariant: > - * > - * left->key, (left+1)->key, (left+2)->key, ..., (right-1)->key > - * are all equal keys. (i.e. strcasecmp returns 0) > - * > - * We essentially need to find the maximal > - * right for each key, then we can do a quick merge or set as > - * appropriate. > +/* Must be a power of 2 */ > +#define DEFAULT_HASH_SIZE 16 > + > +APR_DECLARE(void) apr_table_overlap(apr_table_t *a, const apr_table_t *b, > + unsigned flags) > +{ > + int max_keys; > + int nkeys; > + overlap_key *cat_keys; /* concatenation of the keys of a and b */ > + overlap_key *b_start; > + overlap_key **hash_table; > + int nhash; > + int i; > + apr_table_entry_t *elts; > + > + max_keys = a->a.nelts + b->a.nelts; > + cat_keys = apr_palloc(b->a.pool, sizeof(overlap_key) * max_keys); > + nhash = DEFAULT_HASH_SIZE; > + while (nhash < max_keys) { > + nhash <<= 1; > + } > + hash_table = (overlap_key **)apr_pcalloc(b->a.pool, > + sizeof(overlap_key *) * nhash); > + > + /* The cat_keys array contains an element for each entry in a, > + * followed by one for each in b. While populating this array, > + * we also use it as: > + * 1) a hash table, to detect matching keys, and > + * 2) a linked list of multiple values for a given key (in the > + * APR_OVERLAP_TABLES_MERGE case) > */ > > - if (flags & APR_OVERLAP_TABLES_MERGE) { > - left = cat_keys; > - last = left + nkeys; > - while (left < last) { > - right = left + 1; > - if (right == last > - || strcasecmp(left->key, right->key)) { > - apr_table_addn(a, left->key, left->val); > - left = right; > - } > - else { > - char *strp; > - char *value; > - size_t len; > - > - /* Have to merge some headers. Let's re-use the order field, > - * since it's handy... we'll store the length of val there. > - */ > - left->order = strlen(left->val); > - len = left->order; > - do { > - right->order = strlen(right->val); > - len += 2 + right->order; > - ++right; > - } while (right < last > - && !strcasecmp(left->key, right->key)); > - /* right points one past the last header to merge */ > - value = apr_palloc(a->a.pool, len + 1); > - strp = value; > - for (;;) { > - memcpy(strp, left->val, left->order); > - strp += left->order; > - ++left; > - if (left == right) { > - break; > - } > - *strp++ = ','; > - *strp++ = ' '; > - } > - *strp = 0; > - apr_table_addn(a, (left-1)->key, value); > - } > - } > - } > - else { > - left = cat_keys; > - last = left + nkeys; > - while (left < last) { > - right = left + 1; > - while (right < last && !strcasecmp(left->key, right->key)) { > - ++right; > - } > - apr_table_addn(a, (right-1)->key, (right-1)->val); > - left = right; > - } > + /* First, the elements of a */ > + nkeys = 0; > + elts = (apr_table_entry_t *)a->a.elts; > + for (i = 0; i < a->a.nelts; i++, nkeys++) { > + cat_keys[nkeys].elt = &(elts[i]); > + cat_keys[nkeys].level = 0; > + overlap_hash(&(cat_keys[nkeys]), hash_table, nhash, flags); > + } > + > + /* Then the elements of b */ > + elts = (apr_table_entry_t *)b->a.elts; > + for (i = 0; i < b->a.nelts; i++, nkeys++) { > + cat_keys[nkeys].elt = &(elts[i]); > + cat_keys[nkeys].level = 1; > + overlap_hash(&(cat_keys[nkeys]), hash_table, nhash, flags); > + } > + > + /* Copy concatenated list of elements into table a to > + * form the new table contents, but: > + * 1) omit the ones marked "skip," and > + * 2) merge values for the same key if needed > + */ > + make_array_core(&a->a, b->a.pool, max_keys, sizeof(apr_table_entry_t), 0); > + nkeys = 0; > + for (i = 0; i < max_keys; i++) { > + if (cat_keys[i].skip) { > + continue; > + } > + if (cat_keys[i].merge_next) { > + apr_table_entry_t *elt; > + char *new_val; > + char *val_next; > + overlap_key *next = cat_keys[i].merge_next; > + int len = (cat_keys[i].elt->val ? > + strlen(cat_keys[i].elt->val) : 0); > + do { > + len += 2; > + if (next->elt->val) { > + len += strlen(next->elt->val); > + } > + next = next->merge_next; > + } while (next); > + len++; > + new_val = (char *)apr_palloc(b->a.pool, len); > + val_next = new_val; > + if (cat_keys[i].elt->val) { > + strcpy(val_next, cat_keys[i].elt->val); > + val_next += strlen(cat_keys[i].elt->val); > + } > + next = cat_keys[i].merge_next; > + do { > + *val_next++ = ','; > + *val_next++ = ' '; > + if (next->elt->val) { > + strcpy(val_next, next->elt->val); > + val_next += strlen(next->elt->val); > + } > + next = next->merge_next; > + } while (next); > + *val_next = 0; > + elt = (apr_table_entry_t *)table_push(a); > + elt->key = cat_keys[i].elt->key; > + elt->val = new_val; > + elt->key_checksum = cat_keys[i].elt->key_checksum; > + } > + else { > + apr_table_entry_t *elt = (apr_table_entry_t *)table_push(a); > + elt->key = cat_keys[i].elt->key; > + elt->val = cat_keys[i].elt->val; > + elt->key_checksum = cat_keys[i].elt->key_checksum; > + } > } > } > -- Ian Holsman [EMAIL PROTECTED] Performance Measurement & Analysis CNET Networks - (415) 344-2608