Joe Schaefer <[EMAIL PROTECTED]> writes:

[...]

> BRIAN'S-PATCH
> 
> 0000c1e8 559      1.24604     apr_table_get           ...
> 0000c4f8 253      0.563952    apr_table_setn          ...
> 0000ce94 203      0.452499    table_mergesort         ...
> 0000c06c 187      0.416834    apr_table_make          ...
> 0000cbd8 179      0.399001    apr_table_addn          ...
> 0000d048 138      0.30761     apr_table_compress      ...
> 0000ccec 86       0.191699    apr_table_vdo           ...
> 0000c9e4 84       0.187241    apr_table_mergen        ...
> 0000c160 82       0.182783    table_reindex           ...
> 0000c048 71       0.158263    apr_table_elts          ...
> 0000c050 62       0.138202    apr_is_empty_table      ...
> 0000ccc4 57       0.127056    apr_table_do            ...
> 0000c700 54       0.120369    apr_table_unset         ...
> 
> TOTAL:         2015     4.491549 %

I added back the callbacks apreq needs,
and moved the table_reindex call inside
the dups_found test.  Here's the corresponding
oprofile data:

0000c748 561      1.25878     apr_table_get           ...
0000ca54 256      0.574416    apr_table_setn          ...
0000c2e4 232      0.520565    table_mergesort         ...
0000d490 194      0.4353      apr_table_addn          ...
0000c134 178      0.399399    apr_table_make          ...
0000c4b8 117      0.262526    apr_table_compress      ...
0000d0f4 116      0.260282    apr_table_mergen        ...
0000d704 85       0.190724    apr_table_vdo           ...
0000cc5c 66       0.148092    apr_table_unset         ...
0000c0e4 63       0.14136     apr_table_elts          ...
0000c0ec 59       0.132385    apr_is_empty_table      ...
0000d6dc 53       0.118922    apr_table_do            ...

TOTAL:   1980     4.442751 %

apr patch is attached below; the same patch Brian wrote for 
httpd-2.0 applies.

Index: include/apr_tables.h
===================================================================
RCS file: /home/cvspublic/apr/include/apr_tables.h,v
retrieving revision 1.37
diff -u -r1.37 apr_tables.h
--- include/apr_tables.h	5 Mar 2003 21:22:26 -0000	1.37
+++ include/apr_tables.h	18 Jun 2003 14:50:39 -0000
@@ -224,6 +224,16 @@
 				      const char sep);
 
 /**
+ * Same as apr_array_pstrcat, but takes a (char *) as third argument.
+ * This allows the array elements to be joined on a string instead of
+ * a single character.
+ */
+
+APR_DECLARE(char *)apr_array_pstrjoin(apr_pool_t *p,
+                                      const apr_array_header_t *arr,
+                                      const char *sep);
+
+/**
  * Make a new table
  * @param p The pool to allocate the pool out of
  * @param nelts The number of elements in the initial table.
@@ -242,6 +252,31 @@
                                           const apr_table_t *t);
 
 /**
+ * Get/set method for the table's value copier.
+ * @param t Table.
+ * @param c The new t->copy callback.  c = NULL is ignored;
+ *          a non-NULL value replaces the table's internal copier.
+ * @return The original t->copy callback (prior to any assignment).
+ */
+typedef char *(apr_table_copier_t)(apr_pool_t *p, const char *val);
+
+APR_DECLARE(apr_table_copier_t *) apr_table_copier(apr_table_t *t, 
+                                                   apr_table_copier_t *c);
+
+/**
+ * Get/set method for the table's merger callback.
+ * @param t Table.
+ * @param m The new t->merge callback.  m = NULL is ignored;
+ *          a non-NULL value replaces the table's internal merger.
+ * @return The original t->merge callback (prior to any assignment).
+ */
+typedef char *(apr_table_merger_t)(apr_pool_t *p, 
+                                   const apr_array_header_t *a);
+
+APR_DECLARE(apr_table_merger_t *) apr_table_merger(apr_table_t *t,
+                                                   apr_table_merger_t *m);
+
+/**
  * Delete all of the elements from a table
  * @param t The table to clear
  */
@@ -334,6 +369,27 @@
  */
 APR_DECLARE(void) apr_table_addn(apr_table_t *t, const char *key,
                                  const char *val);
+
+/**
+ * Eliminates redunandant entries with the apr_table_merger_t 
+ * callback associated to t. Use apr_table_merger() to access
+ * and alter this callback.
+ *
+ * @param t Table.
+ * @param flags ::APR_OVERLAP_TABLES_MERGE or ::APR_OVERLAP_TABLES_SET
+ *              see apr_overlap_tables for details on this parameter.
+ */
+APR_DECLARE(void) apr_table_compress(apr_table_t *t, unsigned int flags);
+
+/**
+ * Append one table to the end of another.
+ * @param t The table to be modified.
+ * @param s The entries from this table are added to "t".
+ * @remark This function overlays the internal indices from "s"
+ * onto "t", so it shoul be faster than iterating over s with apr_table_addn.
+ * In any case, the result should be identical.
+ */
+APR_DECLARE(void) apr_table_cat(apr_table_t *t, const apr_table_t *s);
 
 /**
  * Merge two tables into one new table
Index: tables/apr_tables.c
===================================================================
RCS file: /home/cvspublic/apr/tables/apr_tables.c,v
retrieving revision 1.47
diff -u -r1.47 apr_tables.c
--- tables/apr_tables.c	4 Apr 2003 12:05:53 -0000	1.47
+++ tables/apr_tables.c	18 Jun 2003 14:50:40 -0000
@@ -249,6 +249,40 @@
     return res;
 }
 
+APR_DECLARE(char *)apr_array_pstrjoin(apr_pool_t *p,
+                                      const apr_array_header_t *arr,
+                                      const char *sep)
+{
+    apr_size_t len;
+    const char *src, **a = (const char **)arr->elts;
+    char *dest, *rv;
+    const int n = arr->nelts;
+    int j;
+
+    if (n == 0)
+        return apr_pcalloc(p, 1);
+
+    for (j=0, len=0; j<n; ++j)
+        if (a[j] != NULL)
+            len += strlen(a[j]);
+
+    rv = apr_palloc(p, len + 1 + strlen(sep)*(n-1));
+
+    /* ~ strcpy: this leaves "dest" & "src" pointing at nul-characters */
+    for (dest = rv, src = a[0]; (*dest = *src); ++src, ++dest)
+        ;
+
+    for (j=1; j<n; ++j) {
+        for (src = sep; (*dest = *src); ++src, ++dest)
+            ;
+        if (a[j] != NULL)
+            for (src = a[j]; (*dest = *src); ++src, ++dest)
+                ;
+    }
+
+    return rv;
+}
+
 /* apr_array_pstrcat generates a new string from the apr_pool_t containing
  * the concatenated sequence of substrings referenced as elements within
  * the array.  The string will be empty if all substrings are empty or null,
@@ -259,55 +293,8 @@
 				     const apr_array_header_t *arr,
 				     const char sep)
 {
-    char *cp, *res, **strpp;
-    apr_size_t len;
-    int i;
-
-    if (arr->nelts <= 0 || arr->elts == NULL) {    /* Empty table? */
-        return (char *) apr_pcalloc(p, 1);
-    }
-
-    /* Pass one --- find length of required string */
-
-    len = 0;
-    for (i = 0, strpp = (char **) arr->elts; ; ++strpp) {
-        if (strpp && *strpp != NULL) {
-            len += strlen(*strpp);
-        }
-        if (++i >= arr->nelts) {
-            break;
-	}
-        if (sep) {
-            ++len;
-	}
-    }
-
-    /* Allocate the required string */
-
-    res = (char *) apr_palloc(p, len + 1);
-    cp = res;
-
-    /* Pass two --- copy the argument strings into the result space */
-
-    for (i = 0, strpp = (char **) arr->elts; ; ++strpp) {
-        if (strpp && *strpp != NULL) {
-            len = strlen(*strpp);
-            memcpy(cp, *strpp, len);
-            cp += len;
-        }
-        if (++i >= arr->nelts) {
-            break;
-	}
-        if (sep) {
-            *cp++ = sep;
-	}
-    }
-
-    *cp = '\0';
-
-    /* Return the result string */
-
-    return res;
+    char s[2] = {sep,0};
+    return apr_array_pstrjoin(p, arr, s);
 }
 
 
@@ -382,9 +369,12 @@
      *     of index_initialized will be zero.  (Check this before
      *     trying to use index_first[i] or index_last[i]!)
      */
+    apr_table_copier_t *copy;
+    apr_table_merger_t *merge;
     apr_uint32_t index_initialized;
     int index_first[TABLE_HASH_SIZE];
     int index_last[TABLE_HASH_SIZE];
+
 };
 
 /*
@@ -413,6 +403,12 @@
     return ((t == NULL) || (t->a.nelts == 0));
 }
 
+static char *merge_values(apr_pool_t *p, const apr_array_header_t *a)
+{
+
+    return apr_array_pstrjoin(p, a, ", ");
+}
+
 APR_DECLARE(apr_table_t *) apr_table_make(apr_pool_t *p, int nelts)
 {
     apr_table_t *t = apr_palloc(p, sizeof(apr_table_t));
@@ -421,6 +417,8 @@
 #ifdef MAKE_TABLE_PROFILE
     t->creator = __builtin_return_address(0);
 #endif
+    t->copy = apr_pstrdup;
+    t->merge = merge_values;
     t->index_initialized = 0;
     return t;
 }
@@ -441,12 +439,36 @@
     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;
+    new->copy = t->copy;
+    new->merge = t->merge;
     memcpy(new->index_first, t->index_first, sizeof(int) * TABLE_HASH_SIZE);
     memcpy(new->index_last, t->index_last, sizeof(int) * TABLE_HASH_SIZE);
     new->index_initialized = t->index_initialized;
     return new;
 }
 
+APR_DECLARE(apr_table_copier_t *)apr_table_copier(apr_table_t *t, 
+                                                  apr_table_copier_t *c)
+{
+    apr_table_copier_t *original = t->copy;
+
+    if (c != NULL)
+        t->copy = c;
+    return original;
+}
+
+
+APR_DECLARE(apr_table_merger_t *)apr_table_merger(apr_table_t *t, 
+                                                  apr_table_merger_t *m)
+{
+    apr_table_merger_t *original = t->merge;
+
+    if (m != NULL)
+        t->merge = m;
+    return original;
+}
+
+
 static void table_reindex(apr_table_t *t)
 {
     int i;
@@ -464,6 +486,194 @@
     }
 }
 
+static void table_mergesort(apr_pool_t *pool,
+                            apr_table_entry_t **values,
+                            int n)
+{
+    /* Bottom-up mergesort, based on design in Sedgewick's "Algorithms
+     * in C," chapter 8
+     */
+    apr_table_entry_t **values_tmp = apr_palloc(pool, n * sizeof *values_tmp);
+    int i;
+    int blocksize;
+
+    /* First pass: sort pairs of elements (blocksize=1) */
+    for (i = 0; i + 1 < n; i += 2) {
+        if (strcasecmp(values[i]->key, values[i + 1]->key) > 0) {
+            apr_table_entry_t *swap = values[i];
+            values[i] = values[i + 1];
+            values[i + 1] = swap;
+        }
+    }
+    /* Merge successively larger blocks */
+    blocksize = 2;
+    while (blocksize < n) {
+        apr_table_entry_t **dst = values_tmp;
+        int next_start;
+        apr_table_entry_t **swap;
+
+        /* Merge consecutive pairs blocks of the next blocksize.
+         * Within a block, elements are in sorted order due to
+         * the previous iteration.
+         */
+        for (next_start = 0; next_start + blocksize < n;
+             next_start += (blocksize + blocksize)) {
+            int block1_start = next_start;
+            int block2_start = block1_start + blocksize;
+            int block1_end = block2_start;
+            int block2_end = block2_start + blocksize;
+            if (block2_end > n) {
+                /* The last block may be smaller than blocksize */
+                block2_end = n;
+            }
+
+            for (;;) {
+
+                /* Merge the next two blocks:
+                 * Pick the smaller of the next element from
+                 * block 1 and the next element from block 2.
+                 * Once either of the blocks is emptied, copy
+                 * over all the remaining elements from the
+                 * other block
+                 */
+                if (block1_start == block1_end) {
+                    for (; block2_start < block2_end; block2_start++) {
+                        *dst++ = values[block2_start];
+                    }
+                    break;
+                }
+                else if (block2_start == block2_end) {
+                    for (; block1_start < block1_end; block1_start++) {
+                        *dst++ = values[block1_start];
+                    }
+
+                    break;
+                }
+                if (strcasecmp(values[block1_start]->key,
+                               values[block2_start]->key) > 0) {
+                    *dst++ = values[block2_start++];
+                }
+                else {
+                    *dst++ = values[block1_start++];
+                }
+             }
+        }
+        /* If n is not a multiple of 2*blocksize, some elements
+         * will be left over at the end of the array.
+         */
+        for (i = dst - values_tmp; i < n; i++) {
+            values_tmp[i] = values[i];
+        }
+
+        /* The output array of this pass becomes the input
+         * array of the next pass, and vice versa
+         */
+        swap = values_tmp;
+        values_tmp = values;
+        values = swap;
+
+        blocksize += blocksize;
+    }
+}
+
+#define DEFAULT_NELTS 8
+
+APR_DECLARE(void) apr_table_compress(apr_table_t *t, unsigned flags)
+{
+
+    apr_table_entry_t **sort_array;
+    apr_table_entry_t **sort_next;
+    apr_table_entry_t **sort_end;
+    apr_table_entry_t *table_next;
+    apr_table_entry_t **last;
+    int i;
+    int dups_found;
+    char *a[DEFAULT_NELTS];
+    apr_array_header_t arr = {t->a.pool, sizeof(char *), 0,
+                              DEFAULT_NELTS, (char *)a};
+
+    if (t->a.nelts <= 1) {
+         return;
+     }
+
+    /* Copy pointers to all the table elements into an
+     * array and sort to allow for easy detection of
+     * duplicate keys
+     */
+    sort_array = (apr_table_entry_t **)
+        apr_palloc(t->a.pool, t->a.nelts * sizeof(apr_table_entry_t*));
+    sort_next = sort_array;
+    table_next = (apr_table_entry_t *)t->a.elts;
+    i = t->a.nelts;
+    do {
+        *sort_next++ = table_next++;
+    } while (--i);
+
+    /* Note: the merge is done with mergesort instead of quicksort
+     * because mergesort is a stable sort and runs in n*log(n)
+     * time regardless of its inputs (quicksort is quadratic in
+     * the worst case)
+     */
+    table_mergesort(t->a.pool, sort_array, t->a.nelts);
+
+    /* Process any duplicate keys */
+    dups_found = 0;
+    sort_next = sort_array;
+    sort_end = sort_array + t->a.nelts;
+    last = sort_next;
+    while (++sort_next < sort_end) {
+        if (((*sort_next)->key_checksum == (*last)->key_checksum) &&
+            !strcasecmp((*sort_next)->key, (*last)->key)) {
+
+            apr_table_entry_t **dup_last = sort_next + 1;
+
+            dups_found = 1;
+            while ((dup_last < sort_end) &&
+                   ((*dup_last)->key_checksum == (*last)->key_checksum) &&
+                   !strcasecmp((*dup_last)->key, (*last)->key)) {
+                dup_last++;
+            }
+            dup_last--;
+            /* Elements from last through dup_last, inclusive,
+             * all have the same key
+             */
+            if (flags == APR_OVERLAP_TABLES_MERGE) {
+                apr_table_entry_t **next = last;
+                arr.nelts = 0;
+                do {
+                    *(char **)apr_array_push(&arr) = (*next)->val;
+                } while (++next <= dup_last);
+
+                (*last)->val = t->merge(t->a.pool, &arr);
+            }
+            else { /* overwrite */
+                (*last)->val = (*dup_last)->val;
+            }
+            do {
+                (*sort_next)->key = NULL;
+            } while (++sort_next <= dup_last);
+        }
+
+        last = sort_next;
+    }
+
+    /* Shift elements to the left to fill holes left by removing duplicates */
+    if (dups_found) {
+        apr_table_entry_t *src = (apr_table_entry_t *)t->a.elts;
+        apr_table_entry_t *dst = (apr_table_entry_t *)t->a.elts;
+        apr_table_entry_t *last_elt = src + t->a.nelts;
+        do {
+            if (src->key) {
+                *dst++ = *src;
+            }
+        } while (++src < last_elt);
+        t->a.nelts -= (last_elt - dst);
+
+        table_reindex(t);
+    }
+}
+
+
 APR_DECLARE(void) apr_table_clear(apr_table_t *t)
 {
     t->a.nelts = 0;
@@ -528,7 +738,7 @@
             int must_reindex = 0;
             apr_table_entry_t *dst_elt = NULL;
 
-            next_elt->val = apr_pstrdup(t->a.pool, val);
+            next_elt->val = t->copy(t->a.pool, val);
 
             /* Remove any other instances of this key */
             for (next_elt++; next_elt <= end_elt; next_elt++) {
@@ -567,7 +777,7 @@
     t->index_last[hash] = t->a.nelts;
     next_elt = (apr_table_entry_t *) table_push(t);
     next_elt->key = apr_pstrdup(t->a.pool, key);
-    next_elt->val = apr_pstrdup(t->a.pool, val);
+    next_elt->val = t->copy(t->a.pool, val);
     next_elt->key_checksum = checksum;
 }
 
@@ -702,8 +912,12 @@
 {
     apr_table_entry_t *next_elt;
     apr_table_entry_t *end_elt;
+    apr_table_entry_t *table_end;
     apr_uint32_t checksum;
     int hash;
+    int a[DEFAULT_NELTS];
+    apr_array_header_t arr = { t->a.pool, sizeof(int), 0,
+                               DEFAULT_NELTS, (char *)a };
 
     COMPUTE_KEY_CHECKSUM(key, checksum);
     hash = TABLE_HASH(key);
@@ -714,14 +928,52 @@
     }
     next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash];
     end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash];
+    table_end = ((apr_table_entry_t *) t->a.elts) + t->a.nelts;
 
     for (; next_elt <= end_elt; next_elt++) {
 	if ((checksum == next_elt->key_checksum) &&
             !strcasecmp(next_elt->key, key)) {
+            int must_reindex = 0;
+            apr_table_entry_t *dst_elt = NULL;
+            apr_table_entry_t *match_elt = next_elt;
+
+            *(char **)apr_array_push(&arr) = next_elt->val;
+
+            /* Remove any other instances of this key */
+            for (next_elt++; next_elt <= end_elt; next_elt++) {
+                if ((checksum == next_elt->key_checksum) &&
+                    !strcasecmp(next_elt->key, key)) {
+                    t->a.nelts--;
+                    *(const char **)apr_array_push(&arr) = next_elt->val;
+                    if (!dst_elt) {
+                        dst_elt = next_elt;
+                    }
+                }
+                else if (dst_elt) {
+                    *dst_elt++ = *next_elt;
+                    must_reindex = 1;
+                }
+            }
+
+
+            /* If we've removed anything, shift over the remainder
+             * of the table (note that the previous loop didn't
+             * run to the end of the table, just to the last match
+             * for the index)
+             */
+            if (dst_elt) {
+                for (; next_elt < table_end; next_elt++) {
+                    *dst_elt++ = *next_elt;
+                }
+                must_reindex = 1;
+            }
+            if (must_reindex) {
+                table_reindex(t);
+            }
 
             /* Found an existing entry with the same key, so merge with it */
-	    next_elt->val = apr_pstrcat(t->a.pool, next_elt->val, ", ",
-                                        val, NULL);
+            *(const char **)apr_array_push(&arr) = t->copy(t->a.pool, val);
+            match_elt->val = t->merge(t->a.pool, &arr);
             return;
         }
     }
@@ -730,7 +982,7 @@
     t->index_last[hash] = t->a.nelts;
     next_elt = (apr_table_entry_t *) table_push(t);
     next_elt->key = apr_pstrdup(t->a.pool, key);
-    next_elt->val = apr_pstrdup(t->a.pool, val);
+    next_elt->val = t->copy(t->a.pool, val);
     next_elt->key_checksum = checksum;
 }
 
@@ -739,8 +991,12 @@
 {
     apr_table_entry_t *next_elt;
     apr_table_entry_t *end_elt;
+    apr_table_entry_t *table_end;
     apr_uint32_t checksum;
     int hash;
+    int a[DEFAULT_NELTS];
+    apr_array_header_t arr = { t->a.pool, sizeof(int), 0,
+                               DEFAULT_NELTS, (char *)a };
 
 #ifdef POOL_DEBUG
     {
@@ -764,14 +1020,51 @@
     }
     next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash];;
     end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash];
-
+    table_end = ((apr_table_entry_t *) t->a.elts) + t->a.nelts;
     for (; next_elt <= end_elt; next_elt++) {
 	if ((checksum == next_elt->key_checksum) &&
             !strcasecmp(next_elt->key, key)) {
+            int must_reindex = 0;
+            apr_table_entry_t *dst_elt = NULL;
+            apr_table_entry_t *match_elt = next_elt;
+
+            *(const char **)apr_array_push(&arr) = next_elt->val;
+
+            /* Remove any other instances of this key */
+            for (next_elt++; next_elt <= end_elt; next_elt++) {
+                if ((checksum == next_elt->key_checksum) &&
+                    !strcasecmp(next_elt->key, key)) {
+                    t->a.nelts--;
+                    *(const char **)apr_array_push(&arr) = next_elt->val;
+                    if (!dst_elt) {
+                        dst_elt = next_elt;
+                    }
+                }
+                else if (dst_elt) {
+                    *dst_elt++ = *next_elt;
+                    must_reindex = 1;
+                }
+            }
+
+
+            /* If we've removed anything, shift over the remainder
+             * of the table (note that the previous loop didn't
+             * run to the end of the table, just to the last match
+             * for the index)
+             */
+            if (dst_elt) {
+                for (; next_elt < table_end; next_elt++) {
+                    *dst_elt++ = *next_elt;
+                }
+                must_reindex = 1;
+            }
+            if (must_reindex) {
+                table_reindex(t);
+            }
 
             /* Found an existing entry with the same key, so merge with it */
-	    next_elt->val = apr_pstrcat(t->a.pool, next_elt->val, ", ",
-                                        val, NULL);
+            *(const char **)apr_array_push(&arr) = val;
+            match_elt->val = t->merge(t->a.pool, &arr);
             return;
         }
     }
@@ -800,7 +1093,7 @@
     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->val = t->copy(t->a.pool, val);
     elts->key_checksum = checksum;
 }
 
@@ -835,13 +1128,41 @@
     elts->key = (char *)key;
     elts->val = (char *)val;
     elts->key_checksum = checksum;
+ }
+
+
+APR_DECLARE(void) apr_table_cat(apr_table_t *t, const apr_table_t *s)
+{
+    const int n = t->a.nelts;
+    register int idx;
+
+    apr_array_cat(&t->a,&s->a);
+
+    if (n == 0) {
+        memcpy(t->index_first,s->index_first,sizeof(int) * TABLE_HASH_SIZE);
+        memcpy(t->index_last, s->index_last, sizeof(int) * TABLE_HASH_SIZE);
+        t->index_initialized = s->index_initialized;
+        return;
+    }
+
+    for (idx = 0; idx < TABLE_HASH_SIZE; ++idx) {
+        if (TABLE_INDEX_IS_INITIALIZED(s, idx)) {
+            t->index_last[idx] = s->index_last[idx] + n;
+            if (!TABLE_INDEX_IS_INITIALIZED(t, idx)) {
+                t->index_first[idx] = s->index_first[idx] + n;
+            }
+        }
+    }
+
+    t->index_initialized |= s->index_initialized;
 }
 
+
 APR_DECLARE(apr_table_t *) apr_table_overlay(apr_pool_t *p,
 					     const apr_table_t *overlay,
 					     const apr_table_t *base)
-{
-    apr_table_t *res;
+{ 
+    apr_table_t *t = apr_palloc(p, sizeof *t);
 
 #ifdef POOL_DEBUG
     /* we don't copy keys and values, so it's necessary that
@@ -860,13 +1181,27 @@
     }
 #endif
 
-    res = apr_palloc(p, sizeof(apr_table_t));
-    /* behave like append_arrays */
-    res->a.pool = p;
-    copy_array_hdr_core(&res->a, &overlay->a);
-    apr_array_cat(&res->a, &base->a);
-    table_reindex(res);
-    return res;
+    t->a.pool = p;
+    t->a.elt_size = sizeof(apr_table_entry_t);
+    t->copy = overlay->copy;
+    t->merge = overlay->merge;
+    t->index_initialized = overlay->index_initialized;
+
+    memcpy(t->index_first,overlay->index_first,TABLE_HASH_SIZE * sizeof(int));
+    memcpy(t->index_last, overlay->index_last, TABLE_HASH_SIZE * sizeof(int));
+
+    t->a.elts = apr_palloc(p, t->a.elt_size *
+                           (overlay->a.nalloc + base->a.nalloc));
+
+    memcpy(t->a.elts, overlay->a.elts, overlay->a.elt_size * 
+                                       overlay->a.nelts);
+
+    t->a.nelts = overlay->a.nelts;
+    t->a.nalloc = overlay->a.nalloc + base->a.nalloc;
+
+    if (base->a.nelts)
+        apr_table_cat(t, base);
+    return t;
 }
 
 /* And now for something completely abstract ...
@@ -996,334 +1331,24 @@
     return vdorv;
 }
 
-/* During apr_table_overlap(), we build an overlap key for
- * each element in the two tables.
- */
-#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;
-
-/* 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;
-    }
-    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;
-}
 
-static void rotate_clockwise(overlap_key **root, overlap_key *rotate_node)
+APR_DECLARE(void) apr_table_overlap(apr_table_t *a, 
+                                    const apr_table_t *b,
+                                    const unsigned flags)
 {
-    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 {
-        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;
-}
 
+    const int m = a->a.nelts;
+    const int n = b->a.nelts;
+    apr_pool_t *p = b->a.pool;
 
-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
-     */
-    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, because 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;
-                    elt->color = next->color;
-                    (*child) = elt;
-                    elt->merge_next = NULL;
-                    elt->merge_last = NULL;
-                    elt->skip = 0;
-                    next->skip = 1;
-                }
-                else {
-                    elt->skip = 1;
-                }
-            }
-            return;
-        }
-    }
-
-    /* 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().)
-     */
-    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;
-}
-
-/* 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 **hash_table;
-    int nhash;
-    int i;
-    apr_table_entry_t *elts;
-    apr_table_entry_t *dst_elt;
-
-    max_keys = a->a.nelts + b->a.nelts;
-    if (!max_keys) {
-        /* The following logic won't do anything harmful if we keep
-         * going in this situation, but
-         *
-         *    1) certain memory debuggers don't like an alloc size of 0
-         *       so we'd like to avoid that...
-         *    2) this isn't all that rare a call anyway, so it is useful
-         *       to skip the storage allocation and other checks in the
-         *       following logic
-         */
+    if (m + n == 0)
         return;
-    }
-    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)
-     */
 
-    /* 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;
-    dst_elt = (apr_table_entry_t *)a->a.elts;
-    for (i = 0; i < max_keys; i++) {
-        if (cat_keys[i].skip) {
-            continue;
-        }
-        if (cat_keys[i].merge_next) {
-            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;
-            dst_elt->key = cat_keys[i].elt->key;
-            dst_elt->val = new_val;
-            dst_elt->key_checksum = cat_keys[i].elt->key_checksum;
-            dst_elt++;
-        }
-        else {
-            dst_elt->key = cat_keys[i].elt->key;
-            dst_elt->val = cat_keys[i].elt->val;
-            dst_elt->key_checksum = cat_keys[i].elt->key_checksum;
-            dst_elt++;
-        }
+    /* copy (extend) a using b's pool */
+    if (a->a.pool != p) {
+        make_array_core(&a->a, p, m+n, sizeof(apr_table_entry_t), 0);
     }
-    a->a.nelts = dst_elt - (apr_table_entry_t *)a->a.elts;
-    table_reindex(a);
-}
 
+    apr_table_cat(a, b);
+    apr_table_compress(a, flags);
+}

-- 
Joe Schaefer

Reply via email to