You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Dean Gaudet <dg...@arctic.org> on 1998/01/24 22:00:48 UTC

[PATCH] 7% to 25% performance improvement

This patch is based on code submitted by Dmitry Khrustalev
<di...@bog.msu.su>.  The table_(set|add|merge) routines all do pstrdup() 
of their key and val.  But this isn't always required... in fact it's
almost never required.  In many of the cases where these routines are
called the key is a constant string, and the value has been constructed by
pstrcat() or getword() or something else which has already allocated in
the appropriate pool. 

Dmitry's patch adds three routines table_(set|add|merge)n, which do not
pstrdup() their args at all.  He made all the necessary changes in the
core of the code to use these routines effectively.  I went on and made
similar changes in the modules. 

One issue with this patch is erroneously passing a pointer from the wrong
pool to the table_*n routines.  For any string placed in table t, the
"right" pools are t->pool, any ancestor of t->pool, or a string which is
constant.  To detect this I wrote some code which is enabled by defining
POOL_DEBUG.  It currently only works on the unix hosts, but someone
knowing the right WIN32 foo could probably get it to work there too.  When
enabled, the server will abort() when it detects the wrong pool being
used. 

This patch shows a 7% to 25% speedup depending on the request type and the
machine it's done on and stuff like that. 

I'm posting for votes 'cause it extends the API... but hey, for 25%
performance improvement on a static server I'm pretty keen on getting this
in.

Dean

Index: main/alloc.c
===================================================================
RCS file: /export/home/cvs/apachen/src/main/alloc.c,v
retrieving revision 1.68
diff -u -r1.68 alloc.c
--- alloc.c	1998/01/24 19:00:21	1.68
+++ alloc.c	1998/01/24 20:51:33
@@ -88,6 +88,24 @@
  */
 /* #define ALLOC_USE_MALLOC */
 
+/* Pool debugging support.  This is intended to detect cases where the
+ * wrong pool is used when assigning data to an object in another pool.
+ * In particular, it causes the table_{set,add,merge}n routines to check
+ * that their arguments are safe for the table they're being placed in.
+ * It currently only works with the unix multiprocess model, but could
+ * be extended to others.
+ */
+/* #define POOL_DEBUG */
+
+#ifdef POOL_DEBUG
+#ifdef ALLOC_USE_MALLOC
+# error "sorry, no support for ALLOC_USE_MALLOC and POOL_DEBUG at the same time"
+#endif
+#ifdef MULTITHREAD
+# error "sorry, no support for MULTITHREAD and POOL_DEBUG at the same time"
+#endif
+#endif
+
 #ifdef ALLOC_USE_MALLOC
 #undef BLOCK_MINFREE
 #undef BLOCK_MINALLOC
@@ -124,12 +142,22 @@
 	char *endp;
 	union block_hdr *next;
 	char *first_avail;
+#ifdef POOL_DEBUG
+	union block_hdr *global_next;
+	struct pool *owning_pool;
+#endif
     } h;
 };
 
 union block_hdr *block_freelist = NULL;
 mutex *alloc_mutex = NULL;
 mutex *spawn_mutex = NULL;
+#ifdef POOL_DEBUG
+static char *known_stack_point;
+static int stack_direction;
+static union block_hdr *global_block_list;
+#define FREE_POOL	((struct pool *)(-1))
+#endif
 
 #ifdef ALLOC_DEBUG
 #define FILL_BYTE	((char)(0xa5))
@@ -170,16 +198,20 @@
     blok->h.next = NULL;
     blok->h.first_avail = (char *) (blok + 1);
     blok->h.endp = size + blok->h.first_avail;
+#ifdef POOL_DEBUG
+    blok->h.global_next = global_block_list;
+    global_block_list = blok;
+    blok->h.owning_pool = NULL;
+#endif
 
     return blok;
 }
 
 
 
-void chk_on_blk_list(union block_hdr *blok, union block_hdr *free_blk)
+#ifdef ALLOC_DEBUG
+static void chk_on_blk_list(union block_hdr *blok, union block_hdr *free_blk)
 {
-    /* Debugging code.  Left in for the moment. */
-
     while (free_blk) {
 	if (free_blk == blok) {
 	    fprintf(stderr, "Ouch!  Freeing free block\n");
@@ -189,6 +221,9 @@
 	free_blk = free_blk->h.next;
     }
 }
+#else
+#define chk_on_blk_list(_x, _y)
+#endif
 
 /* Free a chain of blocks --- must be called with alarms blocked. */
 
@@ -226,12 +261,18 @@
 	chk_on_blk_list(blok, old_free_list);
 	blok->h.first_avail = (char *) (blok + 1);
 	debug_fill(blok->h.first_avail, blok->h.endp - blok->h.first_avail);
+#ifdef POOL_DEBUG
+	blok->h.owning_pool = FREE_POOL;
+#endif
 	blok = blok->h.next;
     }
 
     chk_on_blk_list(blok, old_free_list);
     blok->h.first_avail = (char *) (blok + 1);
     debug_fill(blok->h.first_avail, blok->h.endp - blok->h.first_avail);
+#ifdef POOL_DEBUG
+    blok->h.owning_pool = FREE_POOL;
+#endif
 
     /* Finally, reset next pointer to get the old free blocks back */
 
@@ -346,6 +387,9 @@
     blok = new_block(POOL_HDR_BYTES);
     new_pool = (pool *) blok->h.first_avail;
     blok->h.first_avail += POOL_HDR_BYTES;
+#ifdef POOL_DEBUG
+    blok->h.owning_pool = new_pool;
+#endif
 
     memset((char *) new_pool, '\0', sizeof(struct pool));
     new_pool->free_first_avail = blok->h.first_avail;
@@ -365,8 +409,28 @@
     return new_pool;
 }
 
+#ifdef POOL_DEBUG
+static void stack_var_init(char *s)
+{
+    char t;
+
+    if (s < &t) {
+	stack_direction = 1; /* stack grows up */
+    }
+    else {
+	stack_direction = -1; /* stack grows down */
+    }
+}
+#endif
+
 void init_alloc(void)
 {
+#ifdef POOL_DEBUG
+    char s;
+
+    known_stack_point = &s;
+    stack_var_init(&s);
+#endif
     alloc_mutex = create_mutex(NULL);
     spawn_mutex = create_mutex(NULL);
     permanent_pool = make_sub_pool(NULL);
@@ -442,6 +506,87 @@
 }
 
 /*****************************************************************
+ * POOL_DEBUG support
+ */
+#ifdef POOL_DEBUG
+
+/* the unix linker defines this symbol as the last byte + 1 of
+ * the executable... so it includes TEXT, BSS, and DATA
+ */
+extern char _end;
+
+/* is ptr in the range [lo,hi) */
+#define is_ptr_in_range(ptr, lo, hi)	\
+    (((unsigned long)(ptr) - (unsigned long)(lo)) \
+	< \
+	(unsigned long)(hi) - (unsigned long)(lo))
+
+/* Find the pool that ts belongs to, return NULL if it doesn't
+ * belong to any pool.
+ */
+pool *find_pool(const void *ts)
+{
+    const char *s = ts;
+    union block_hdr **pb;
+    union block_hdr *b;
+
+    /* short-circuit stuff which is in TEXT, BSS, or DATA */
+    if (is_ptr_in_range(s, 0, &_end)) {
+	return NULL;
+    }
+    /* consider stuff on the stack to also be in the NULL pool...
+     * XXX: there's cases where we don't want to assume this
+     */
+    if ((stack_direction == -1 && is_ptr_in_range(s, &ts, known_stack_point))
+	|| (stack_direction == 1 && is_ptr_in_range(s, known_stack_point, &ts))) {
+	abort();
+	return NULL;
+    }
+    block_alarms();
+    /* search the global_block_list */
+    for (pb = &global_block_list; *pb; pb = &b->h.global_next) {
+	b = *pb;
+	if (is_ptr_in_range(s, b, b->h.endp)) {
+	    if (b->h.owning_pool == FREE_POOL) {
+		fprintf(stderr,
+		    "Ouch!  find_pool() called on pointer in a free block\n");
+		abort();
+		exit(1);
+	    }
+	    if (b != global_block_list) {
+		/* promote b to front of list, this is a hack to speed
+		 * up the lookup */
+		*pb = b->h.global_next;
+		b->h.global_next = global_block_list;
+		global_block_list = b;
+	    }
+	    unblock_alarms();
+	    return b->h.owning_pool;
+	}
+    }
+    unblock_alarms();
+    return NULL;
+}
+
+/* return TRUE iff a is an ancestor of b
+ * NULL is considered an ancestor of all pools
+ */
+int pool_is_ancestor(pool *a, pool *b)
+{
+    if (a == NULL) {
+	return 1;
+    }
+    while (b) {
+	if (a == b) {
+	    return 1;
+	}
+	b = b->parent;
+    }
+    return 0;
+}
+#endif
+
+/*****************************************************************
  *
  * Allocating stuff...
  */
@@ -501,6 +646,9 @@
     blok = new_block(size);
     a->last->h.next = blok;
     a->last = blok;
+#ifdef POOL_DEBUG
+    blok->h.owning_pool = a;
+#endif
 
     (void) release_mutex(alloc_mutex);
 
@@ -523,10 +671,13 @@
 API_EXPORT(char *) pstrdup(struct pool *a, const char *s)
 {
     char *res;
+    size_t len;
+
     if (s == NULL)
 	return NULL;
-    res = palloc(a, strlen(s) + 1);
-    strcpy(res, s);
+    len = strlen(s) + 1;
+    res = palloc(a, len);
+    memcpy(res, s, len);
     return res;
 }
 
@@ -781,6 +932,52 @@
     }
 }
 
+API_EXPORT(void) table_setn(table *t, char *key, char *val)
+{
+    register int i, j, k;
+    table_entry *elts = (table_entry *) t->a.elts;
+    int done = 0;
+
+#ifdef POOL_DEBUG
+    {
+	if (!pool_is_ancestor(find_pool(key), t->a.pool)) {
+	    fprintf(stderr, "table_set: key not in ancestor pool of t\n");
+	    abort();
+	}
+	if (!pool_is_ancestor(find_pool(val), t->a.pool)) {
+	    fprintf(stderr, "table_set: key not in ancestor pool of t\n");
+	    abort();
+	}
+    }
+#endif
+
+    for (i = 0; i < t->a.nelts; ) {
+	if (!strcasecmp(elts[i].key, key)) {
+	    if (!done) {
+		elts[i].val =  val;
+		done = 1;
+		++i;
+	    }
+	    else {		/* delete an extraneous element */
+		for (j = i, k = i + 1; k < t->a.nelts; ++j, ++k) {
+		    elts[j].key = elts[k].key;
+		    elts[j].val = elts[k].val;
+		}
+		--t->a.nelts;
+	    }
+	}
+	else {
+	    ++i;
+	}
+    }
+
+    if (!done) {
+	elts = (table_entry *) push_array(&t->a);
+	elts->key = key;
+	elts->val = val;
+    }
+}
+
 API_EXPORT(void) table_unset(table *t, const char *key)
 {
     register int i, j, k;
@@ -811,18 +1008,47 @@
     table_entry *elts = (table_entry *) t->a.elts;
     int i;
 
-    for (i = 0; i < t->a.nelts; ++i) {
+    for (i = 0; i < t->a.nelts; ++i)
 	if (!strcasecmp(elts[i].key, key)) {
 	    elts[i].val = pstrcat(t->a.pool, elts[i].val, ", ", val, NULL);
 	    return;
 	}
-    }
 
     elts = (table_entry *) push_array(&t->a);
     elts->key = pstrdup(t->a.pool, key);
     elts->val = pstrdup(t->a.pool, val);
 }
 
+API_EXPORT(void) table_mergen(table *t, char *key, char *val)
+{
+    table_entry *elts = (table_entry *) t->a.elts;
+    int i;
+
+#ifdef POOL_DEBUG
+    {
+	if (!pool_is_ancestor(find_pool(key), t->a.pool)) {
+	    fprintf(stderr, "table_set: key not in ancestor pool of t\n");
+	    abort();
+	}
+	if (!pool_is_ancestor(find_pool(val), t->a.pool)) {
+	    fprintf(stderr, "table_set: key not in ancestor pool of t\n");
+	    abort();
+	}
+    }
+#endif
+
+    for (i = 0; i < t->a.nelts; ++i) {
+	if (!strcasecmp(elts[i].key, key)) {
+	    elts[i].val = pstrcat(t->a.pool, elts[i].val, ", ", val, NULL);
+	    return;
+	}
+    }
+
+    elts = (table_entry *) push_array(&t->a);
+    elts->key = key;
+    elts->val = val;
+}
+
 API_EXPORT(void) table_add(table *t, const char *key, const char *val)
 {
     table_entry *elts = (table_entry *) t->a.elts;
@@ -830,6 +1056,28 @@
     elts = (table_entry *) push_array(&t->a);
     elts->key = pstrdup(t->a.pool, key);
     elts->val = pstrdup(t->a.pool, val);
+}
+
+API_EXPORT(void) table_addn(table *t, char *key, char *val)
+{
+    table_entry *elts = (table_entry *) t->a.elts;
+
+#ifdef POOL_DEBUG
+    {
+	if (!pool_is_ancestor(find_pool(key), t->a.pool)) {
+	    fprintf(stderr, "table_set: key not in ancestor pool of t\n");
+	    abort();
+	}
+	if (!pool_is_ancestor(find_pool(val), t->a.pool)) {
+	    fprintf(stderr, "table_set: key not in ancestor pool of t\n");
+	    abort();
+	}
+    }
+#endif
+
+    elts = (table_entry *) push_array(&t->a);
+    elts->key = key;
+    elts->val = val;
 }
 
 API_EXPORT(table *) overlay_tables(pool *p, const table *overlay, const table *base)
Index: main/alloc.h
===================================================================
RCS file: /export/home/cvs/apachen/src/main/alloc.h,v
retrieving revision 1.42
diff -u -r1.42 alloc.h
--- alloc.h	1998/01/24 20:02:19	1.42
+++ alloc.h	1998/01/24 20:51:36
@@ -155,9 +155,12 @@
 API_EXPORT(void) clear_table(table *);
 API_EXPORT(char *) table_get(const table *, const char *);
 API_EXPORT(void) table_set(table *, const char *name, const char *val);
+API_EXPORT(void) table_setn(table *, char *name, char *val);
 API_EXPORT(void) table_merge(table *, const char *name, const char *more_val);
+API_EXPORT(void) table_mergen(table *, char *name, char *more_val);
 API_EXPORT(void) table_unset(table *, const char *key);
 API_EXPORT(void) table_add(table *, const char *name, const char *val);
+API_EXPORT(void) table_addn(table *, char *name, char *val);
 API_EXPORT(void) table_do(int (*comp) (void *, const char *, const char *), void *rec,
 			  const table *t,...);
 
Index: main/http_core.c
===================================================================
RCS file: /export/home/cvs/apachen/src/main/http_core.c,v
retrieving revision 1.147
diff -u -r1.147 http_core.c
--- http_core.c	1998/01/21 22:15:56	1.147
+++ http_core.c	1998/01/24 20:51:40
@@ -1895,7 +1895,7 @@
 #endif
 
 	if (d->content_md5 & 1) {
-	    table_set (r->headers_out, "Content-MD5", ap_md5digest(r->pool, f));
+	    table_setn(r->headers_out, "Content-MD5", ap_md5digest(r->pool, f));
 	}
 
 	rangestatus = set_byterange(r);
@@ -1956,7 +1956,7 @@
 	    
 	    MD5Init(&context);
 	    MD5Update(&context, (void *)mm, r->finfo.st_size);
-	    table_set (r->headers_out, "Content-MD5",
+	    table_setn(r->headers_out, "Content-MD5",
 		ap_md5contextTo64(r->pool, &context));
 	}
 
Index: main/http_protocol.c
===================================================================
RCS file: /export/home/cvs/apachen/src/main/http_protocol.c,v
retrieving revision 1.178
diff -u -r1.178 http_protocol.c
--- http_protocol.c	1998/01/23 04:11:32	1.178
+++ http_protocol.c	1998/01/24 20:51:41
@@ -138,7 +138,7 @@
         range = table_get(r->headers_in, "Request-Range");
 
     if (!range || strncmp(range, "bytes=", 6)) {
-        table_set(r->headers_out, "Accept-Ranges", "bytes");
+        table_setn(r->headers_out, "Accept-Ranges", "bytes");
         return 0;
     }
 
@@ -165,9 +165,9 @@
 
         ap_snprintf(ts, sizeof(ts), "bytes %ld-%ld/%ld",
                     range_start, range_end, r->clength);
-        table_set(r->headers_out, "Content-Range", ts);
+        table_setn(r->headers_out, "Content-Range", pstrdup(r->pool, ts));
         ap_snprintf(ts, sizeof(ts), "%ld", range_end - range_start + 1);
-        table_set(r->headers_out, "Content-Length", ts);
+        table_setn(r->headers_out, "Content-Length", pstrdup(r->pool, ts));
     }
     else {
         /* a multiple range */
@@ -181,7 +181,7 @@
         r->boundary = pstrdup(r->pool, boundary);
         while (internal_byterange(0, &tlength, r, &r_range, NULL, NULL));
         ap_snprintf(ts, sizeof(ts), "%ld", tlength);
-        table_set(r->headers_out, "Content-Length", ts);
+        table_setn(r->headers_out, "Content-Length", pstrdup(r->pool, ts));
     }
 
     r->status = PARTIAL_CONTENT;
@@ -259,7 +259,7 @@
     r->clength = clength;
 
     ap_snprintf(ts, sizeof(ts), "%ld", clength);
-    table_set(r->headers_out, "Content-Length", ts);
+    table_setn(r->headers_out, "Content-Length", pstrdup(r->pool, ts));
 
     return 0;
 }
@@ -331,8 +331,8 @@
             else
                 ap_snprintf(header, sizeof(header), "timeout=%d",
                             r->server->keep_alive_timeout);
-            table_set(r->headers_out, "Keep-Alive", header);
-            table_merge(r->headers_out, "Connection", "Keep-Alive");
+            table_setn(r->headers_out, "Keep-Alive", pstrdup(r->pool, header));
+            table_mergen(r->headers_out, "Connection", "Keep-Alive");
         }
 
         return 1;
@@ -347,7 +347,7 @@
      * to a HTTP/1.1 client. Better safe than sorry.
      */
     if (!wimpy)
-      table_merge(r->headers_out, "Connection", "close");
+	table_mergen(r->headers_out, "Connection", "close");
 
     r->connection->keepalive = 0;
 
@@ -502,7 +502,7 @@
     }
 
     etag = weak_etag + ((r->request_time - r->mtime > 1) ? 2 : 0);
-    table_set(r->headers_out, "ETag", etag);
+    table_setn(r->headers_out, "ETag", pstrdup(r->pool, etag));
 }
 
 /*
@@ -514,7 +514,7 @@
 {
     time_t mod_time = rationalize_mtime(r, r->mtime);
 
-    table_set(r->headers_out, "Last-Modified",
+    table_setn(r->headers_out, "Last-Modified",
               gm_timestr_822(r->pool, mod_time));
 }
 
@@ -753,8 +753,10 @@
      * overflow (len == MAX_STRING_LEN-1)?
      */
     while ((len = getline(field, MAX_STRING_LEN, c->client, 1)) > 0) {
+        char *copy = palloc(r->pool, len + 1);
+        memcpy(copy, field, len + 1);
 
-        if (!(value = strchr(field, ':')))      /* Find the colon separator */
+        if (!(value = strchr(copy, ':')))      /* Find the colon separator */
             continue;           /* or should puke 400 here */
 
         *value = '\0';
@@ -762,7 +764,7 @@
         while (isspace(*value))
             ++value;            /* Skip to start of value   */
 
-        table_merge(r->headers_in, field, value);
+        table_mergen(r->headers_in, copy, value);
     }
 }
 
@@ -906,7 +908,7 @@
     if (strcasecmp(auth_type(r), "Basic"))
         note_auth_failure(r);
     else
-        table_set(r->err_headers_out,
+        table_setn(r->err_headers_out,
                   r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
                   pstrcat(r->pool, "Basic realm=\"", auth_name(r), "\"",
                           NULL));
@@ -917,7 +919,7 @@
     char nonce[256];
 
     ap_snprintf(nonce, sizeof(nonce), "%lu", r->request_time);
-    table_set(r->err_headers_out,
+    table_setn(r->err_headers_out,
               r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
               pstrcat(r->pool, "Digest realm=\"", auth_name(r),
                       "\", nonce=\"", nonce, "\"", NULL));
@@ -1173,8 +1175,8 @@
 
     basic_http_header(r);
 
-    table_set(r->headers_out, "Content-Length", "0");
-    table_set(r->headers_out, "Allow", make_allow(r));
+    table_setn(r->headers_out, "Content-Length", "0");
+    table_setn(r->headers_out, "Allow", make_allow(r));
     set_keepalive(r);
 
     table_do((int (*) (void *, const char *, const char *)) send_header_field,
@@ -1231,37 +1233,37 @@
     set_keepalive(r);
 
     if (r->chunked) {
-        table_merge(r->headers_out, "Transfer-Encoding", "chunked");
+        table_mergen(r->headers_out, "Transfer-Encoding", "chunked");
         table_unset(r->headers_out, "Content-Length");
     }
 
     if (r->byterange > 1)
-        table_set(r->headers_out, "Content-Type",
+        table_setn(r->headers_out, "Content-Type",
                   pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/",
                           "byteranges; boundary=", r->boundary, NULL));
     else if (r->content_type)
-        table_set(r->headers_out, "Content-Type", r->content_type);
+        table_setn(r->headers_out, "Content-Type", r->content_type);
     else
-        table_set(r->headers_out, "Content-Type", default_type(r));
+        table_setn(r->headers_out, "Content-Type", default_type(r));
 
     if (r->content_encoding)
-        table_set(r->headers_out, "Content-Encoding", r->content_encoding);
+        table_setn(r->headers_out, "Content-Encoding", r->content_encoding);
 
     if (r->content_languages && r->content_languages->nelts) {
         for (i = 0; i < r->content_languages->nelts; ++i) {
-            table_merge(r->headers_out, "Content-Language",
+            table_mergen(r->headers_out, "Content-Language",
                         ((char **) (r->content_languages->elts))[i]);
         }
     }
     else if (r->content_language)
-        table_set(r->headers_out, "Content-Language", r->content_language);
+        table_setn(r->headers_out, "Content-Language", r->content_language);
 
     /*
      * Control cachability for non-cachable responses if not already set by
      * some other part of the server configuration.
      */
     if (r->no_cache && !table_get(r->headers_out, "Expires"))
-        table_add(r->headers_out, "Expires",
+        table_addn(r->headers_out, "Expires",
                   gm_timestr_822(r->pool, r->request_time));
 
     /* Send the entire table of header fields, terminated by an empty line. */
@@ -1486,7 +1488,8 @@
                 get_mime_headers(r);
                 ap_snprintf(buffer, bufsiz, "%ld", r->read_length);
                 table_unset(r->headers_in, "Transfer-Encoding");
-                table_set(r->headers_in, "Content-Length", buffer);
+                table_setn(r->headers_in, "Content-Length",
+                    pstrdup(r->pool, buffer));
                 return 0;
             }
             r->remaining = -1;  /* Indicate footers in-progress */
@@ -1992,7 +1995,7 @@
 
         if (location && *location
             && (is_HTTP_REDIRECT(status) || status == HTTP_CREATED))
-            table_set(r->headers_out, "Location", location);
+            table_setn(r->headers_out, "Location", location);
 
         r->content_language = NULL;
         r->content_languages = NULL;
@@ -2001,7 +2004,7 @@
         r->content_type = "text/html";
 
         if ((status == METHOD_NOT_ALLOWED) || (status == NOT_IMPLEMENTED))
-            table_set(r->headers_out, "Allow", make_allow(r));
+            table_setn(r->headers_out, "Allow", make_allow(r));
 
         send_http_header(r);
 
Index: main/http_request.c
===================================================================
RCS file: /export/home/cvs/apachen/src/main/http_request.c,v
retrieving revision 1.100
diff -u -r1.100 http_request.c
--- http_request.c	1998/01/21 22:48:13	1.100
+++ http_request.c	1998/01/24 20:51:42
@@ -903,7 +903,7 @@
              * status...
              */
             r->status = REDIRECT;
-            table_set(r->headers_out, "Location", custom_response);
+            table_setn(r->headers_out, "Location", custom_response);
         }
         else if (custom_response[0] == '/') {
             r->no_local_copy = 1;       /* Do NOT send USE_LOCAL_COPY for
@@ -912,7 +912,7 @@
              * This redirect needs to be a GET no matter what the original
              * method was.
              */
-            table_set(r->subprocess_env, "REQUEST_METHOD", r->method);
+            table_setn(r->subprocess_env, "REQUEST_METHOD", r->method);
             r->method = pstrdup(r->pool, "GET");
             r->method_number = M_GET;
             internal_redirect(custom_response, r);
@@ -1168,7 +1168,7 @@
     for (i = 0; i < env_arr->nelts; ++i) {
         if (!elts[i].key)
             continue;
-        table_set(new, pstrcat(p, "REDIRECT_", elts[i].key, NULL),
+        table_setn(new, pstrcat(p, "REDIRECT_", elts[i].key, NULL),
                   elts[i].val);
     }
 
@@ -1228,7 +1228,7 @@
     new->read_length     = r->read_length;     /* We can only read it once */
 
     ap_snprintf(t, sizeof(t), "%d", r->status);
-    table_set(new->subprocess_env, "REDIRECT_STATUS", t);
+    table_setn(new->subprocess_env, "REDIRECT_STATUS", pstrdup(r->pool, t));
 
     /*
      * XXX: hmm.  This is because mod_setenvif and mod_unique_id really need
Index: main/util_script.c
===================================================================
RCS file: /export/home/cvs/apachen/src/main/util_script.c,v
retrieving revision 1.92
diff -u -r1.92 util_script.c
--- util_script.c	1998/01/21 22:31:46	1.92
+++ util_script.c	1998/01/24 20:51:44
@@ -205,9 +205,9 @@
 	 */
 
 	if (!strcasecmp(hdrs[i].key, "Content-type"))
-	    table_set(e, "CONTENT_TYPE", hdrs[i].val);
+	    table_setn(e, "CONTENT_TYPE", hdrs[i].val);
 	else if (!strcasecmp(hdrs[i].key, "Content-length"))
-	    table_set(e, "CONTENT_LENGTH", hdrs[i].val);
+	    table_setn(e, "CONTENT_LENGTH", hdrs[i].val);
 	/*
 	 * You really don't want to disable this check, since it leaves you
 	 * wide open to CGIs stealing passwords and people viewing them
@@ -218,7 +218,7 @@
 	    continue;
 #endif
 	else
-	    table_set(e, http2env(r->pool, hdrs[i].key), hdrs[i].val);
+	    table_setn(e, http2env(r->pool, hdrs[i].key), hdrs[i].val);
     }
 
     ap_snprintf(port, sizeof(port), "%u", s->port);
@@ -228,42 +228,42 @@
 
 #ifdef WIN32
     if (env_temp = getenv("SystemRoot"))
-        table_set(e, "SystemRoot", env_temp);         
+        table_setn(e, "SystemRoot", env_temp);         
     if (env_temp = getenv("COMSPEC"))
-        table_set(e, "COMSPEC", env_temp);            
+        table_setn(e, "COMSPEC", env_temp);            
     if (env_temp = getenv("WINDIR"))
-        table_set(e, "WINDIR", env_temp);             
+        table_setn(e, "WINDIR", env_temp);             
 #endif
 
-    table_set(e, "PATH", env_path);
-    table_set(e, "SERVER_SOFTWARE", SERVER_VERSION);
-    table_set(e, "SERVER_NAME", s->server_hostname);
-    table_set(e, "SERVER_PORT", port);
-    table_set(e, "REMOTE_HOST",
-	      get_remote_host(c, r->per_dir_config, REMOTE_NAME));
-    table_set(e, "REMOTE_ADDR", c->remote_ip);
-    table_set(e, "DOCUMENT_ROOT", document_root(r));	/* Apache */
-    table_set(e, "SERVER_ADMIN", s->server_admin);	/* Apache */
-    table_set(e, "SCRIPT_FILENAME", r->filename);	/* Apache */
+    table_setn(e, "PATH", env_path);
+    table_setn(e, "SERVER_SOFTWARE", SERVER_VERSION);
+    table_setn(e, "SERVER_NAME", s->server_hostname);
+    table_setn(e, "SERVER_PORT", pstrdup(r->pool,port));
+    table_setn(e, "REMOTE_HOST",
+        pstrdup(r->pool, get_remote_host(c, r->per_dir_config, REMOTE_NAME)));
+    table_setn(e, "REMOTE_ADDR", c->remote_ip);
+    table_setn(e, "DOCUMENT_ROOT", document_root(r));	/* Apache */
+    table_setn(e, "SERVER_ADMIN", s->server_admin);	/* Apache */
+    table_setn(e, "SCRIPT_FILENAME", r->filename);	/* Apache */
 
     ap_snprintf(port, sizeof(port), "%d", ntohs(c->remote_addr.sin_port));
-    table_set(e, "REMOTE_PORT", port);	/* Apache */
+    table_setn(e, "REMOTE_PORT", pstrdup(r->pool, port)); /* Apache */
 
     if (c->user)
-	table_set(e, "REMOTE_USER", c->user);
+	table_setn(e, "REMOTE_USER", c->user);
     if (c->auth_type)
-	table_set(e, "AUTH_TYPE", c->auth_type);
+	table_setn(e, "AUTH_TYPE", c->auth_type);
     rem_logname = get_remote_logname(r);
     if (rem_logname)
-	table_set(e, "REMOTE_IDENT", rem_logname);
+	table_setn(e, "REMOTE_IDENT", pstrdup(r->pool, rem_logname));
 
     /* Apache custom error responses. If we have redirected set two new vars */
 
     if (r->prev) {
 	if (r->prev->args)
-	    table_set(e, "REDIRECT_QUERY_STRING", r->prev->args);
+	    table_setn(e, "REDIRECT_QUERY_STRING", r->prev->args);
 	if (r->prev->uri)
-	    table_set(e, "REDIRECT_URL", r->prev->uri);
+	    table_setn(e, "REDIRECT_URL", r->prev->uri);
     }
 }
 
@@ -316,11 +316,11 @@
 {
     table *e = r->subprocess_env;
 
-    table_set(e, "GATEWAY_INTERFACE", "CGI/1.1");
-    table_set(e, "SERVER_PROTOCOL", r->protocol);
-    table_set(e, "REQUEST_METHOD", r->method);
-    table_set(e, "QUERY_STRING", r->args ? r->args : "");
-    table_set(e, "REQUEST_URI", original_uri(r));
+    table_setn(e, "GATEWAY_INTERFACE", "CGI/1.1");
+    table_setn(e, "SERVER_PROTOCOL", r->protocol);
+    table_setn(e, "REQUEST_METHOD", r->method);
+    table_setn(e, "QUERY_STRING", r->args ? r->args : "");
+    table_setn(e, "REQUEST_URI", original_uri(r));
 
     /* Note that the code below special-cases scripts run from includes,
      * because it "knows" that the sub_request has been hacked to have the
@@ -329,20 +329,20 @@
      */
 
     if (!strcmp(r->protocol, "INCLUDED")) {
-	table_set(e, "SCRIPT_NAME", r->uri);
+	table_setn(e, "SCRIPT_NAME", r->uri);
 	if (r->path_info && *r->path_info)
-	    table_set(e, "PATH_INFO", r->path_info);
+	    table_setn(e, "PATH_INFO", r->path_info);
     }
     else if (!r->path_info || !*r->path_info) {
-	table_set(e, "SCRIPT_NAME", r->uri);
+	table_setn(e, "SCRIPT_NAME", r->uri);
     }
     else {
 	int path_info_start = find_path_info(r->uri, r->path_info);
 
-	table_set(e, "SCRIPT_NAME", pstrndup(r->pool, r->uri,
+	table_setn(e, "SCRIPT_NAME", pstrndup(r->pool, r->uri,
 					     path_info_start));
 
-	table_set(e, "PATH_INFO", r->path_info);
+	table_setn(e, "PATH_INFO", r->path_info);
     }
 
     if (r->path_info && r->path_info[0]) {
@@ -370,9 +370,9 @@
 #ifdef WIN32
 	    /* We need to make this a real Windows path name */
 	    GetFullPathName(pt, HUGE_STRING_LEN, buffer, NULL);
-	    table_set(e, "PATH_TRANSLATED", buffer);
+	    table_setn(e, "PATH_TRANSLATED", pstrdup(r->pool, buffer));
 #else
-	    table_set(e, "PATH_TRANSLATED", pt);
+	    table_setn(e, "PATH_TRANSLATED", pt);
 #endif
 	}
     }
@@ -471,13 +471,13 @@
 	    r->status_line = pstrdup(r->pool, l);
 	}
 	else if (!strcasecmp(w, "Location")) {
-	    table_set(r->headers_out, w, l);
+	    table_setn(r->headers_out, pstrdup(r->pool,w), pstrdup(r->pool,l));
 	}
 	else if (!strcasecmp(w, "Content-Length")) {
-	    table_set(r->headers_out, w, l);
+	    table_setn(r->headers_out, pstrdup(r->pool,w), pstrdup(r->pool,l));
 	}
 	else if (!strcasecmp(w, "Transfer-Encoding")) {
-	    table_set(r->headers_out, w, l);
+	    table_setn(r->headers_out, pstrdup(r->pool,w), pstrdup(r->pool,l));
 	}
 	/*
 	 * If the script gave us a Last-Modified header, we can't just
@@ -494,7 +494,7 @@
 	 * we'll use - otherwise we assume 200 OK.
 	 */
 	else if (!strcasecmp(w, "Status")) {
-	    table_set(r->headers_out, w, l);
+	    table_setn(r->headers_out, pstrdup(r->pool,w), pstrdup(r->pool,l));
 	    cgi_status = atoi(l);
 	}
 
@@ -504,10 +504,10 @@
 	 * separately.  Lets humour those browsers.
 	 */
 	else if (!strcasecmp(w, "Set-Cookie")) {
-	    table_add(r->err_headers_out, w, l);
+	    table_addn(r->err_headers_out, pstrdup(r->pool,w), pstrdup(r->pool,l));
 	}
 	else {
-	    table_merge(r->err_headers_out, w, l);
+	    table_mergen(r->err_headers_out, pstrdup(r->pool,w), pstrdup(r->pool,l));
 	}
     }
 }
Index: modules/proxy/mod_proxy.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/proxy/mod_proxy.c,v
retrieving revision 1.32
diff -u -r1.32 mod_proxy.c
--- mod_proxy.c	1998/01/24 20:30:07	1.32
+++ mod_proxy.c	1998/01/24 20:51:44
@@ -251,7 +251,7 @@
 		       host, domain, strport, "/", path,
 		       NULL);
 
-	table_set(r->headers_out, "Location", nuri);
+	table_setn(r->headers_out, "Location", nuri);
 	aplog_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r->server,
 	    "Domain missing: %s sent to %s from %s", r->uri, nuri,
 	    ref ? ref : "-");
Index: modules/standard/mod_alias.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_alias.c,v
retrieving revision 1.29
diff -u -r1.29 mod_alias.c
--- mod_alias.c	1998/01/07 16:46:43	1.29
+++ mod_alias.c	1998/01/24 20:51:44
@@ -328,7 +328,7 @@
 	if (found) {
 	    if (p->handler) {	/* Set handler, and leave a note for mod_cgi */
 		r->handler = pstrdup(r->pool, p->handler);
-		table_set(r->notes, "alias-forced-type", p->handler);
+		table_setn(r->notes, "alias-forced-type", r->handler);
 	    }
 
 	    *status = p->redir_status;
@@ -363,7 +363,7 @@
 	    if (r->args) {
 		ret = pstrcat(r->pool, ret, "?", r->args, NULL);
 	    }
-	    table_set(r->headers_out, "Location", ret);
+	    table_setn(r->headers_out, "Location", ret);
 	}
 	return status;
     }
@@ -388,7 +388,7 @@
 
     if ((ret = try_alias_list(r, dirconf->redirects, 1, &status)) != NULL) {
 	if (is_HTTP_REDIRECT(status))
-	    table_set(r->headers_out, "Location", ret);
+	    table_setn(r->headers_out, "Location", ret);
 	return status;
     }
 
Index: modules/standard/mod_dir.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_dir.c,v
retrieving revision 1.44
diff -u -r1.44 mod_dir.c
--- mod_dir.c	1998/01/07 16:46:47	1.44
+++ mod_dir.c	1998/01/24 20:51:44
@@ -117,7 +117,7 @@
             ifile = pstrcat(r->pool, escape_uri(r->pool, r->uri),
                             "/", NULL);
 
-        table_set(r->headers_out, "Location",
+        table_setn(r->headers_out, "Location",
                   construct_url(r->pool, ifile, r->server));
         return HTTP_MOVED_PERMANENTLY;
     }
Index: modules/standard/mod_expires.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_expires.c,v
retrieving revision 1.21
diff -u -r1.21 mod_expires.c
--- mod_expires.c	1998/01/07 16:46:49	1.21
+++ mod_expires.c	1998/01/24 20:51:45
@@ -473,11 +473,11 @@
 
     expires = base + additional;
     ap_snprintf(age, sizeof(age), "max-age=%d", (int) expires - (int) r->request_time);
-    table_set(r->headers_out, "Cache-Control", age);
+    table_setn(r->headers_out, "Cache-Control", pstrdup(r->pool, age));
     tzset();                    /* redundant? called implicitly by localtime, at least 
                                  * under FreeBSD
                                  */
-    table_set(r->headers_out, "Expires", gm_timestr_822(r->pool, expires));
+    table_setn(r->headers_out, "Expires", gm_timestr_822(r->pool, expires));
     return OK;
 }
 
Index: modules/standard/mod_imap.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_imap.c,v
retrieving revision 1.39
diff -u -r1.39 mod_imap.c
--- mod_imap.c	1998/01/22 23:18:07	1.39
+++ mod_imap.c	1998/01/24 20:51:45
@@ -543,7 +543,7 @@
         return HTTP_NO_CONTENT; /* tell the client to keep the page it has */
     }
     if (redirect && *redirect) {
-        table_set(r->headers_out, "Location", redirect);
+        table_setn(r->headers_out, "Location", redirect);
         return REDIRECT;        /* must be a URL, so redirect to it */
     }
     return SERVER_ERROR;
Index: modules/standard/mod_include.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_include.c,v
retrieving revision 1.65
diff -u -r1.65 mod_include.c
--- mod_include.c	1998/01/24 19:00:24	1.65
+++ mod_include.c	1998/01/24 20:51:46
@@ -112,36 +112,36 @@
     char *t;
     time_t date = r->request_time;
 
-    table_set(e, "DATE_LOCAL", ht_time(r->pool, date, timefmt, 0));
-    table_set(e, "DATE_GMT", ht_time(r->pool, date, timefmt, 1));
-    table_set(e, "LAST_MODIFIED",
+    table_setn(e, "DATE_LOCAL", ht_time(r->pool, date, timefmt, 0));
+    table_setn(e, "DATE_GMT", ht_time(r->pool, date, timefmt, 1));
+    table_setn(e, "LAST_MODIFIED",
               ht_time(r->pool, r->finfo.st_mtime, timefmt, 0));
-    table_set(e, "DOCUMENT_URI", r->uri);
-    table_set(e, "DOCUMENT_PATH_INFO", r->path_info);
+    table_setn(e, "DOCUMENT_URI", r->uri);
+    table_setn(e, "DOCUMENT_PATH_INFO", r->path_info);
 #ifndef WIN32
     pw = getpwuid(r->finfo.st_uid);
     if (pw) {
-        table_set(e, "USER_NAME", pw->pw_name);
+        table_setn(e, "USER_NAME", pstrdup(r->pool, pw->pw_name));
     }
     else {
         char uid[16];
         ap_snprintf(uid, sizeof(uid), "user#%lu",
                     (unsigned long) r->finfo.st_uid);
-        table_set(e, "USER_NAME", uid);
+        table_setn(e, "USER_NAME", pstrdup(r->pool, uid));
     }
 #endif /* ndef WIN32 */
 
     if ((t = strrchr(r->filename, '/'))) {
-        table_set(e, "DOCUMENT_NAME", ++t);
+        table_setn(e, "DOCUMENT_NAME", ++t);
     }
     else {
-        table_set(e, "DOCUMENT_NAME", r->uri);
+        table_setn(e, "DOCUMENT_NAME", r->uri);
     }
     if (r->args) {
         char *arg_copy = pstrdup(r->pool, r->args);
 
         unescape_url(arg_copy);
-        table_set(e, "QUERY_STRING_UNESCAPED",
+        table_setn(e, "QUERY_STRING_UNESCAPED",
                   escape_shell_cmd(r->pool, arg_copy));
     }
 }
@@ -746,11 +746,11 @@
     if (r->path_info && r->path_info[0] != '\0') {
         request_rec *pa_req;
 
-        table_set(env, "PATH_INFO", escape_shell_cmd(r->pool, r->path_info));
+        table_setn(env, "PATH_INFO", escape_shell_cmd(r->pool, r->path_info));
 
         pa_req = sub_req_lookup_uri(escape_uri(r->pool, r->path_info), r);
         if (pa_req->filename) {
-            table_set(env, "PATH_TRANSLATED",
+            table_setn(env, "PATH_TRANSLATED",
                       pstrcat(r->pool, pa_req->filename, pa_req->path_info,
                               NULL));
         }
@@ -759,9 +759,9 @@
     if (r->args) {
         char *arg_copy = pstrdup(r->pool, r->args);
 
-        table_set(env, "QUERY_STRING", r->args);
+        table_setn(env, "QUERY_STRING", r->args);
         unescape_url(arg_copy);
-        table_set(env, "QUERY_STRING_UNESCAPED",
+        table_setn(env, "QUERY_STRING_UNESCAPED",
                   escape_shell_cmd(r->pool, arg_copy));
     }
 
@@ -951,9 +951,9 @@
             time_t date = r->request_time;
 
             parse_string(r, tag_val, tf, MAX_STRING_LEN, 0);
-            table_set(env, "DATE_LOCAL", ht_time(r->pool, date, tf, 0));
-            table_set(env, "DATE_GMT", ht_time(r->pool, date, tf, 1));
-            table_set(env, "LAST_MODIFIED",
+            table_setn(env, "DATE_LOCAL", ht_time(r->pool, date, tf, 0));
+            table_setn(env, "DATE_GMT", ht_time(r->pool, date, tf, 1));
+            table_setn(env, "LAST_MODIFIED",
                       ht_time(r->pool, r->finfo.st_mtime, tf, 0));
         }
         else if (!strcmp(tag, "sizefmt")) {
@@ -2015,7 +2015,7 @@
                 return -1;
             }
             parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
-            table_set(r->subprocess_env, var, parsed_string);
+            table_setn(r->subprocess_env, var, pstrdup(r->pool, parsed_string));
         }
         else {
             aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
@@ -2080,9 +2080,9 @@
     if (r->args) {              /* add QUERY stuff to env cause it ain't yet */
         char *arg_copy = pstrdup(r->pool, r->args);
 
-        table_set(r->subprocess_env, "QUERY_STRING", r->args);
+        table_setn(r->subprocess_env, "QUERY_STRING", r->args);
         unescape_url(arg_copy);
-        table_set(r->subprocess_env, "QUERY_STRING_UNESCAPED",
+        table_setn(r->subprocess_env, "QUERY_STRING_UNESCAPED",
                   escape_shell_cmd(r->pool, arg_copy));
     }
 
Index: modules/standard/mod_log_config.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_log_config.c,v
retrieving revision 1.42
diff -u -r1.42 mod_log_config.c
--- mod_log_config.c	1998/01/07 16:46:52	1.42
+++ mod_log_config.c	1998/01/24 20:51:47
@@ -764,7 +764,7 @@
     mls->default_format = NULL;
     mls->server_config_logs = NULL;
     mls->formats = make_table(p, 4);
-    table_set(mls->formats, "CLF", DEFAULT_LOG_FORMAT);
+    table_setn(mls->formats, "CLF", DEFAULT_LOG_FORMAT);
 
     return mls;
 }
Index: modules/standard/mod_negotiation.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_negotiation.c,v
retrieving revision 1.64
diff -u -r1.64 mod_negotiation.c
--- mod_negotiation.c	1998/01/24 19:00:25	1.64
+++ mod_negotiation.c	1998/01/24 20:51:48
@@ -1889,24 +1889,24 @@
         rec = pstrcat(r->pool, rec, "}", NULL);
 
         if (na_result != na_not_applied) {
-            table_merge(hdrs, "Alternates", rec);
+            table_mergen(hdrs, "Alternates", rec);
         }
     }
 
     if (na_result != na_not_applied) {
-        table_merge(hdrs, "Vary", "negotiate");
+        table_mergen(hdrs, "Vary", "negotiate");
     }
     if (vary_by_type) {
-        table_merge(hdrs, "Vary", "accept");
+        table_mergen(hdrs, "Vary", "accept");
     }
     if (vary_by_language) {
-        table_merge(hdrs, "Vary", "accept-language");
+        table_mergen(hdrs, "Vary", "accept-language");
     }
     if (vary_by_charset) {
-        table_merge(hdrs, "Vary", "accept-charset");
+        table_mergen(hdrs, "Vary", "accept-charset");
     }
     if (vary_by_encoding && na_result == na_not_applied) {
-        table_merge(hdrs, "Vary", "accept-encoding");
+        table_mergen(hdrs, "Vary", "accept-encoding");
     }
 }
 
@@ -1955,10 +1955,10 @@
 static void store_variant_list(request_rec *r, negotiation_state *neg)
 {
     if (r->main == NULL) {
-        table_set(r->notes, "variant-list", make_variant_list(r, neg));
+        table_setn(r->notes, "variant-list", make_variant_list(r, neg));
     }
     else {
-        table_set(r->main->notes, "variant-list",
+        table_setn(r->main->notes, "variant-list",
                   make_variant_list(r->main, neg));
     }
 }
@@ -2004,9 +2004,10 @@
     }
 
     if ((sub_vary = table_get(sub_req->err_headers_out, "Vary")) != NULL) {
-        table_set(r->err_headers_out, "Variant-Vary", sub_vary);
+        table_setn(r->err_headers_out, "Variant-Vary", sub_vary);
     }
-    table_set(r->err_headers_out, "Content-Location", variant->file_name);
+    table_setn(r->err_headers_out, "Content-Location",
+		pstrdup(r->pool, variant->file_name));
     set_neg_headers(r, neg, na_choice);         /* add Alternates and Vary */
     /* to do: add Expires */
 
Index: modules/standard/mod_rewrite.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_rewrite.c,v
retrieving revision 1.62
diff -u -r1.62 mod_rewrite.c
--- mod_rewrite.c	1998/01/20 00:27:20	1.62
+++ mod_rewrite.c	1998/01/24 20:51:50
@@ -908,13 +908,13 @@
          var = pstrcat(r->pool, "REDIRECT_", ENVVAR_SCRIPT_URL, NULL);
          var = table_get(r->subprocess_env, var);
          if (var == NULL) 
-             table_set(r->subprocess_env, ENVVAR_SCRIPT_URL, r->uri);
+             table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, r->uri);
          else 
-             table_set(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
+             table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
     } 
     else {
          var = table_get(r->main->subprocess_env, ENVVAR_SCRIPT_URL);
-         table_set(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
+         table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
     }
 
     /*
@@ -943,7 +943,7 @@
 #else
     var = pstrcat(r->pool, "http://", thisserver, thisport, thisurl, NULL);
 #endif
-    table_set(r->subprocess_env, ENVVAR_SCRIPT_URI, var);
+    table_setn(r->subprocess_env, ENVVAR_SCRIPT_URI, var);
 
 
     /* if filename was not initially set,
@@ -1028,7 +1028,7 @@
                 n = REDIRECT;
 
             /* now do the redirection */
-            table_set(r->headers_out, "Location", r->filename);
+            table_setn(r->headers_out, "Location", r->filename);
             rewritelog(r, 1, "redirect to %s [REDIRECT/%d]", r->filename, n);
             return n;
         }
@@ -1298,7 +1298,7 @@
                 n = REDIRECT;
 
             /* now do the redirection */
-            table_set(r->headers_out, "Location", r->filename);
+            table_setn(r->headers_out, "Location", r->filename);
             rewritelog(r, 1, "[per-dir %s] redirect to %s [REDIRECT/%d]",
                        dconf->directory, r->filename, n);
             return n;
@@ -1873,7 +1873,7 @@
      *  MIME API-hook function.
      */
     if (p->forced_mimetype != NULL) {
-        table_set(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR,
+        table_setn(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR,
                   p->forced_mimetype);
         if (perdir == NULL)
             rewritelog(r, 2, "remember %s to have MIME-type '%s'",
Index: modules/standard/mod_setenvif.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_setenvif.c,v
retrieving revision 1.12
diff -u -r1.12 mod_setenvif.c
--- mod_setenvif.c	1998/01/24 19:00:26	1.12
+++ mod_setenvif.c	1998/01/24 20:51:50
@@ -310,7 +310,7 @@
                     table_unset(r->subprocess_env, elts[j].key);
                 }
                 else {
-                    table_set(r->subprocess_env, elts[j].key, elts[j].val);
+                    table_setn(r->subprocess_env, elts[j].key, elts[j].val);
                 }
             }
         }
Index: modules/standard/mod_speling.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_speling.c,v
retrieving revision 1.11
diff -u -r1.11 mod_speling.c
--- mod_speling.c	1998/01/13 23:29:13	1.11
+++ mod_speling.c	1998/01/24 20:51:50
@@ -338,7 +338,7 @@
             nuri = pstrcat(r->pool, url, variant[0].name,
                            r->path_info, NULL);
 
-            table_set(r->headers_out, "Location",
+            table_setn(r->headers_out, "Location",
                       construct_url(r->pool, nuri, r->server));
 
             aplog_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, r->server,
@@ -408,7 +408,7 @@
                 ref, "\">referring page</a> about the broken link.\n", NULL);
 
             /* Pass our table to http_protocol.c (see mod_negotiation): */
-            table_set(notes, "variant-list", t);
+            table_setn(notes, "variant-list", t);
 
             aplog_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, r->server,
                         ref ? "Spelling fix: %s: %d candidates from %s"
Index: modules/standard/mod_unique_id.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_unique_id.c,v
retrieving revision 1.9
diff -u -r1.9 mod_unique_id.c
--- mod_unique_id.c	1998/01/07 16:46:58	1.9
+++ mod_unique_id.c	1998/01/24 20:51:50
@@ -311,7 +311,7 @@
     str[18] = uuencoder[((x[1] & 0x0f) << 2) | ((0 & 0xc0) >> 6)];
     str[19] = '\0';
 
-    table_set(r->subprocess_env, "UNIQUE_ID", str);
+    table_setn(r->subprocess_env, "UNIQUE_ID", pstrdup(r->pool, str));
 
     /* and increment the identifier for the next call */
     counter = ntohs(cur_unique_id.counter) + 1;
Index: modules/standard/mod_userdir.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_userdir.c,v
retrieving revision 1.25
diff -u -r1.25 mod_userdir.c
--- mod_userdir.c	1998/01/07 16:46:58	1.25
+++ mod_userdir.c	1998/01/24 20:51:50
@@ -194,7 +194,8 @@
     (userdir_config *) get_module_config(server_conf, &userdir_module);
     char *name = r->uri;
     const char *userdirs = pstrdup(r->pool, s_cfg->userdir);
-    const char *w, *dname, *redirect;
+    const char *w, *dname;
+    char *redirect;
     char *x = NULL;
     struct stat statbuf;
 
@@ -277,7 +278,7 @@
                 if (strchr(x, ':')) {
 #endif                          /* WIN32 */
                     redirect = pstrcat(r->pool, x, w, userdir, dname, NULL);
-                    table_set(r->headers_out, "Location", redirect);
+                    table_setn(r->headers_out, "Location", redirect);
                     return REDIRECT;
                 }
                 else
@@ -288,7 +289,7 @@
         }
         else if (strchr(userdir, ':')) {
             redirect = pstrcat(r->pool, userdir, "/", w, dname, NULL);
-            table_set(r->headers_out, "Location", redirect);
+            table_setn(r->headers_out, "Location", redirect);
             return REDIRECT;
         }
         else {
Index: modules/standard/mod_usertrack.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_usertrack.c,v
retrieving revision 1.24
diff -u -r1.24 mod_usertrack.c
--- mod_usertrack.c	1998/01/07 16:46:59	1.24
+++ mod_usertrack.c	1998/01/24 20:51:51
@@ -135,8 +135,8 @@
     struct timezone tz = {0, 0};
 #endif
     /* 1024 == hardcoded constants */
-    char *new_cookie = palloc(r->pool, 1024);
-    char *cookiebuf = palloc(r->pool, 1024);
+    char new_cookie[1024];
+    char cookiebuf[1024];
     char *dot;
     const char *rname = pstrdup(r->pool,
                            get_remote_host(r->connection, r->per_dir_config,
@@ -200,8 +200,8 @@
     else
         ap_snprintf(new_cookie, 1024, "%s%s; path=/", COOKIE_NAME, cookiebuf);
 
-    table_set(r->headers_out, "Set-Cookie", new_cookie);
-    table_set(r->notes, "cookie", cookiebuf);   /* log first time */
+    table_setn(r->headers_out, "Set-Cookie", pstrdup(r->pool, new_cookie));
+    table_setn(r->notes, "cookie", pstrdup(r->pool, cookiebuf));   /* log first time */
     return;
 }
 
@@ -226,7 +226,7 @@
                 *cookieend = '\0';      /* Ignore anything after a ; */
 
             /* Set the cookie in a note, for logging */
-            table_set(r->notes, "cookie", cookiebuf);
+            table_setn(r->notes, "cookie", cookiebuf);
 
             return DECLINED;    /* Theres already a cookie, no new one */
         }


Re: [PATCH] 7% to 25% performance improvement

Posted by Dean Gaudet <dg...@arctic.org>.
I made other changes to table stuff this aft and you'll need to refresh in
order for the patch to apply cleanly.  The anon tree may just be a bit
behind. 

Dean

On Sat, 24 Jan 1998, Igor Tatarinov wrote:

> Dean,
> 
> I tried to apply this patch to the current anon CVS tree and it shows a
> lot of rejections. 
> 
> A dumb question: do I need to wait for the tree to be updated or am I
> doing something wrong (I am just running patch from src)?
> 
> thanks,
> igor
> 


Re: [PATCH] 7% to 25% performance improvement

Posted by Igor Tatarinov <ta...@prairie.NoDak.edu>.
Dean,

I tried to apply this patch to the current anon CVS tree and it shows a
lot of rejections. 

A dumb question: do I need to wait for the tree to be updated or am I
doing something wrong (I am just running patch from src)?

thanks,
igor

Re: [PATCH] 7% to 25% performance improvement

Posted by "Dirk-Willem van Gulik(mda00)" <di...@mda00.jrc.it>.

On Mon, 26 Jan 1998, Dean Gaudet wrote:

> But yeah I'm having a hard time getting numbers I believe from my current
> perf testing setup, I think I've reached one of various limits in my
> systems.  So I'm going back into maintenance mode for a bit...  the more I
> look at the mod_include problem that I just ran into the more I'm
> concerned with our sub_request, internal_redirect, and the "fast redirect"
> stuff that happens in mod_negotiation.  It's taken us a bunch of
> iterations to get where we are, and I think there may be a wee bit more to

The internal redirect is certainly highly placed on my list of suspects;
but on systems where speed really matters; zapping things like stat() and
access check does wonders too if you just need raw speed right away.

Dw.


Re: [PATCH] 7% to 25% performance improvement

Posted by Dean Gaudet <dg...@arctic.org>.

On Mon, 26 Jan 1998, Dirk-Willem van Gulik(mda00) wrote:

> Tried it, does not break things on FreeBSD. But 25% speedup,
> nay; not really :-) But an increase of 108 to 129 req/seq
> I would dare to contribute to it.

Well hey that's 19%. 

But yeah I'm having a hard time getting numbers I believe from my current
perf testing setup, I think I've reached one of various limits in my
systems.  So I'm going back into maintenance mode for a bit...  the more I
look at the mod_include problem that I just ran into the more I'm
concerned with our sub_request, internal_redirect, and the "fast redirect"
stuff that happens in mod_negotiation.  It's taken us a bunch of
iterations to get where we are, and I think there may be a wee bit more to
go. 

Dean



Re: [PATCH] 7% to 25% performance improvement

Posted by "Dirk-Willem van Gulik(mda00)" <di...@mda00.jrc.it>.
Tried it, does not break things on FreeBSD. But 25% speedup,
nay; not really :-) But an increase of 108 to 129 req/seq
I would dare to contribute to it.

Dw.

On Sat, 24 Jan 1998, Dean Gaudet wrote:

> This patch is based on code submitted by Dmitry Khrustalev
> <di...@bog.msu.su>.  The table_(set|add|merge) routines all do pstrdup() 
> of their key and val.  But this isn't always required... in fact it's
> almost never required.  In many of the cases where these routines are
> called the key is a constant string, and the value has been constructed by
> pstrcat() or getword() or something else which has already allocated in
> the appropriate pool. 
> 
> Dmitry's patch adds three routines table_(set|add|merge)n, which do not
> pstrdup() their args at all.  He made all the necessary changes in the
> core of the code to use these routines effectively.  I went on and made
> similar changes in the modules. 
> 
> One issue with this patch is erroneously passing a pointer from the wrong
> pool to the table_*n routines.  For any string placed in table t, the
> "right" pools are t->pool, any ancestor of t->pool, or a string which is
> constant.  To detect this I wrote some code which is enabled by defining
> POOL_DEBUG.  It currently only works on the unix hosts, but someone
> knowing the right WIN32 foo could probably get it to work there too.  When
> enabled, the server will abort() when it detects the wrong pool being
> used. 
> 
> This patch shows a 7% to 25% speedup depending on the request type and the
> machine it's done on and stuff like that. 
> 
> I'm posting for votes 'cause it extends the API... but hey, for 25%
> performance improvement on a static server I'm pretty keen on getting this
> in.
> 
> Dean
> 
> Index: main/alloc.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/main/alloc.c,v
> retrieving revision 1.68
> diff -u -r1.68 alloc.c
> --- alloc.c	1998/01/24 19:00:21	1.68
> +++ alloc.c	1998/01/24 20:51:33
> @@ -88,6 +88,24 @@
>   */
>  /* #define ALLOC_USE_MALLOC */
>  
> +/* Pool debugging support.  This is intended to detect cases where the
> + * wrong pool is used when assigning data to an object in another pool.
> + * In particular, it causes the table_{set,add,merge}n routines to check
> + * that their arguments are safe for the table they're being placed in.
> + * It currently only works with the unix multiprocess model, but could
> + * be extended to others.
> + */
> +/* #define POOL_DEBUG */
> +
> +#ifdef POOL_DEBUG
> +#ifdef ALLOC_USE_MALLOC
> +# error "sorry, no support for ALLOC_USE_MALLOC and POOL_DEBUG at the same time"
> +#endif
> +#ifdef MULTITHREAD
> +# error "sorry, no support for MULTITHREAD and POOL_DEBUG at the same time"
> +#endif
> +#endif
> +
>  #ifdef ALLOC_USE_MALLOC
>  #undef BLOCK_MINFREE
>  #undef BLOCK_MINALLOC
> @@ -124,12 +142,22 @@
>  	char *endp;
>  	union block_hdr *next;
>  	char *first_avail;
> +#ifdef POOL_DEBUG
> +	union block_hdr *global_next;
> +	struct pool *owning_pool;
> +#endif
>      } h;
>  };
>  
>  union block_hdr *block_freelist = NULL;
>  mutex *alloc_mutex = NULL;
>  mutex *spawn_mutex = NULL;
> +#ifdef POOL_DEBUG
> +static char *known_stack_point;
> +static int stack_direction;
> +static union block_hdr *global_block_list;
> +#define FREE_POOL	((struct pool *)(-1))
> +#endif
>  
>  #ifdef ALLOC_DEBUG
>  #define FILL_BYTE	((char)(0xa5))
> @@ -170,16 +198,20 @@
>      blok->h.next = NULL;
>      blok->h.first_avail = (char *) (blok + 1);
>      blok->h.endp = size + blok->h.first_avail;
> +#ifdef POOL_DEBUG
> +    blok->h.global_next = global_block_list;
> +    global_block_list = blok;
> +    blok->h.owning_pool = NULL;
> +#endif
>  
>      return blok;
>  }
>  
>  
>  
> -void chk_on_blk_list(union block_hdr *blok, union block_hdr *free_blk)
> +#ifdef ALLOC_DEBUG
> +static void chk_on_blk_list(union block_hdr *blok, union block_hdr *free_blk)
>  {
> -    /* Debugging code.  Left in for the moment. */
> -
>      while (free_blk) {
>  	if (free_blk == blok) {
>  	    fprintf(stderr, "Ouch!  Freeing free block\n");
> @@ -189,6 +221,9 @@
>  	free_blk = free_blk->h.next;
>      }
>  }
> +#else
> +#define chk_on_blk_list(_x, _y)
> +#endif
>  
>  /* Free a chain of blocks --- must be called with alarms blocked. */
>  
> @@ -226,12 +261,18 @@
>  	chk_on_blk_list(blok, old_free_list);
>  	blok->h.first_avail = (char *) (blok + 1);
>  	debug_fill(blok->h.first_avail, blok->h.endp - blok->h.first_avail);
> +#ifdef POOL_DEBUG
> +	blok->h.owning_pool = FREE_POOL;
> +#endif
>  	blok = blok->h.next;
>      }
>  
>      chk_on_blk_list(blok, old_free_list);
>      blok->h.first_avail = (char *) (blok + 1);
>      debug_fill(blok->h.first_avail, blok->h.endp - blok->h.first_avail);
> +#ifdef POOL_DEBUG
> +    blok->h.owning_pool = FREE_POOL;
> +#endif
>  
>      /* Finally, reset next pointer to get the old free blocks back */
>  
> @@ -346,6 +387,9 @@
>      blok = new_block(POOL_HDR_BYTES);
>      new_pool = (pool *) blok->h.first_avail;
>      blok->h.first_avail += POOL_HDR_BYTES;
> +#ifdef POOL_DEBUG
> +    blok->h.owning_pool = new_pool;
> +#endif
>  
>      memset((char *) new_pool, '\0', sizeof(struct pool));
>      new_pool->free_first_avail = blok->h.first_avail;
> @@ -365,8 +409,28 @@
>      return new_pool;
>  }
>  
> +#ifdef POOL_DEBUG
> +static void stack_var_init(char *s)
> +{
> +    char t;
> +
> +    if (s < &t) {
> +	stack_direction = 1; /* stack grows up */
> +    }
> +    else {
> +	stack_direction = -1; /* stack grows down */
> +    }
> +}
> +#endif
> +
>  void init_alloc(void)
>  {
> +#ifdef POOL_DEBUG
> +    char s;
> +
> +    known_stack_point = &s;
> +    stack_var_init(&s);
> +#endif
>      alloc_mutex = create_mutex(NULL);
>      spawn_mutex = create_mutex(NULL);
>      permanent_pool = make_sub_pool(NULL);
> @@ -442,6 +506,87 @@
>  }
>  
>  /*****************************************************************
> + * POOL_DEBUG support
> + */
> +#ifdef POOL_DEBUG
> +
> +/* the unix linker defines this symbol as the last byte + 1 of
> + * the executable... so it includes TEXT, BSS, and DATA
> + */
> +extern char _end;
> +
> +/* is ptr in the range [lo,hi) */
> +#define is_ptr_in_range(ptr, lo, hi)	\
> +    (((unsigned long)(ptr) - (unsigned long)(lo)) \
> +	< \
> +	(unsigned long)(hi) - (unsigned long)(lo))
> +
> +/* Find the pool that ts belongs to, return NULL if it doesn't
> + * belong to any pool.
> + */
> +pool *find_pool(const void *ts)
> +{
> +    const char *s = ts;
> +    union block_hdr **pb;
> +    union block_hdr *b;
> +
> +    /* short-circuit stuff which is in TEXT, BSS, or DATA */
> +    if (is_ptr_in_range(s, 0, &_end)) {
> +	return NULL;
> +    }
> +    /* consider stuff on the stack to also be in the NULL pool...
> +     * XXX: there's cases where we don't want to assume this
> +     */
> +    if ((stack_direction == -1 && is_ptr_in_range(s, &ts, known_stack_point))
> +	|| (stack_direction == 1 && is_ptr_in_range(s, known_stack_point, &ts))) {
> +	abort();
> +	return NULL;
> +    }
> +    block_alarms();
> +    /* search the global_block_list */
> +    for (pb = &global_block_list; *pb; pb = &b->h.global_next) {
> +	b = *pb;
> +	if (is_ptr_in_range(s, b, b->h.endp)) {
> +	    if (b->h.owning_pool == FREE_POOL) {
> +		fprintf(stderr,
> +		    "Ouch!  find_pool() called on pointer in a free block\n");
> +		abort();
> +		exit(1);
> +	    }
> +	    if (b != global_block_list) {
> +		/* promote b to front of list, this is a hack to speed
> +		 * up the lookup */
> +		*pb = b->h.global_next;
> +		b->h.global_next = global_block_list;
> +		global_block_list = b;
> +	    }
> +	    unblock_alarms();
> +	    return b->h.owning_pool;
> +	}
> +    }
> +    unblock_alarms();
> +    return NULL;
> +}
> +
> +/* return TRUE iff a is an ancestor of b
> + * NULL is considered an ancestor of all pools
> + */
> +int pool_is_ancestor(pool *a, pool *b)
> +{
> +    if (a == NULL) {
> +	return 1;
> +    }
> +    while (b) {
> +	if (a == b) {
> +	    return 1;
> +	}
> +	b = b->parent;
> +    }
> +    return 0;
> +}
> +#endif
> +
> +/*****************************************************************
>   *
>   * Allocating stuff...
>   */
> @@ -501,6 +646,9 @@
>      blok = new_block(size);
>      a->last->h.next = blok;
>      a->last = blok;
> +#ifdef POOL_DEBUG
> +    blok->h.owning_pool = a;
> +#endif
>  
>      (void) release_mutex(alloc_mutex);
>  
> @@ -523,10 +671,13 @@
>  API_EXPORT(char *) pstrdup(struct pool *a, const char *s)
>  {
>      char *res;
> +    size_t len;
> +
>      if (s == NULL)
>  	return NULL;
> -    res = palloc(a, strlen(s) + 1);
> -    strcpy(res, s);
> +    len = strlen(s) + 1;
> +    res = palloc(a, len);
> +    memcpy(res, s, len);
>      return res;
>  }
>  
> @@ -781,6 +932,52 @@
>      }
>  }
>  
> +API_EXPORT(void) table_setn(table *t, char *key, char *val)
> +{
> +    register int i, j, k;
> +    table_entry *elts = (table_entry *) t->a.elts;
> +    int done = 0;
> +
> +#ifdef POOL_DEBUG
> +    {
> +	if (!pool_is_ancestor(find_pool(key), t->a.pool)) {
> +	    fprintf(stderr, "table_set: key not in ancestor pool of t\n");
> +	    abort();
> +	}
> +	if (!pool_is_ancestor(find_pool(val), t->a.pool)) {
> +	    fprintf(stderr, "table_set: key not in ancestor pool of t\n");
> +	    abort();
> +	}
> +    }
> +#endif
> +
> +    for (i = 0; i < t->a.nelts; ) {
> +	if (!strcasecmp(elts[i].key, key)) {
> +	    if (!done) {
> +		elts[i].val =  val;
> +		done = 1;
> +		++i;
> +	    }
> +	    else {		/* delete an extraneous element */
> +		for (j = i, k = i + 1; k < t->a.nelts; ++j, ++k) {
> +		    elts[j].key = elts[k].key;
> +		    elts[j].val = elts[k].val;
> +		}
> +		--t->a.nelts;
> +	    }
> +	}
> +	else {
> +	    ++i;
> +	}
> +    }
> +
> +    if (!done) {
> +	elts = (table_entry *) push_array(&t->a);
> +	elts->key = key;
> +	elts->val = val;
> +    }
> +}
> +
>  API_EXPORT(void) table_unset(table *t, const char *key)
>  {
>      register int i, j, k;
> @@ -811,18 +1008,47 @@
>      table_entry *elts = (table_entry *) t->a.elts;
>      int i;
>  
> -    for (i = 0; i < t->a.nelts; ++i) {
> +    for (i = 0; i < t->a.nelts; ++i)
>  	if (!strcasecmp(elts[i].key, key)) {
>  	    elts[i].val = pstrcat(t->a.pool, elts[i].val, ", ", val, NULL);
>  	    return;
>  	}
> -    }
>  
>      elts = (table_entry *) push_array(&t->a);
>      elts->key = pstrdup(t->a.pool, key);
>      elts->val = pstrdup(t->a.pool, val);
>  }
>  
> +API_EXPORT(void) table_mergen(table *t, char *key, char *val)
> +{
> +    table_entry *elts = (table_entry *) t->a.elts;
> +    int i;
> +
> +#ifdef POOL_DEBUG
> +    {
> +	if (!pool_is_ancestor(find_pool(key), t->a.pool)) {
> +	    fprintf(stderr, "table_set: key not in ancestor pool of t\n");
> +	    abort();
> +	}
> +	if (!pool_is_ancestor(find_pool(val), t->a.pool)) {
> +	    fprintf(stderr, "table_set: key not in ancestor pool of t\n");
> +	    abort();
> +	}
> +    }
> +#endif
> +
> +    for (i = 0; i < t->a.nelts; ++i) {
> +	if (!strcasecmp(elts[i].key, key)) {
> +	    elts[i].val = pstrcat(t->a.pool, elts[i].val, ", ", val, NULL);
> +	    return;
> +	}
> +    }
> +
> +    elts = (table_entry *) push_array(&t->a);
> +    elts->key = key;
> +    elts->val = val;
> +}
> +
>  API_EXPORT(void) table_add(table *t, const char *key, const char *val)
>  {
>      table_entry *elts = (table_entry *) t->a.elts;
> @@ -830,6 +1056,28 @@
>      elts = (table_entry *) push_array(&t->a);
>      elts->key = pstrdup(t->a.pool, key);
>      elts->val = pstrdup(t->a.pool, val);
> +}
> +
> +API_EXPORT(void) table_addn(table *t, char *key, char *val)
> +{
> +    table_entry *elts = (table_entry *) t->a.elts;
> +
> +#ifdef POOL_DEBUG
> +    {
> +	if (!pool_is_ancestor(find_pool(key), t->a.pool)) {
> +	    fprintf(stderr, "table_set: key not in ancestor pool of t\n");
> +	    abort();
> +	}
> +	if (!pool_is_ancestor(find_pool(val), t->a.pool)) {
> +	    fprintf(stderr, "table_set: key not in ancestor pool of t\n");
> +	    abort();
> +	}
> +    }
> +#endif
> +
> +    elts = (table_entry *) push_array(&t->a);
> +    elts->key = key;
> +    elts->val = val;
>  }
>  
>  API_EXPORT(table *) overlay_tables(pool *p, const table *overlay, const table *base)
> Index: main/alloc.h
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/main/alloc.h,v
> retrieving revision 1.42
> diff -u -r1.42 alloc.h
> --- alloc.h	1998/01/24 20:02:19	1.42
> +++ alloc.h	1998/01/24 20:51:36
> @@ -155,9 +155,12 @@
>  API_EXPORT(void) clear_table(table *);
>  API_EXPORT(char *) table_get(const table *, const char *);
>  API_EXPORT(void) table_set(table *, const char *name, const char *val);
> +API_EXPORT(void) table_setn(table *, char *name, char *val);
>  API_EXPORT(void) table_merge(table *, const char *name, const char *more_val);
> +API_EXPORT(void) table_mergen(table *, char *name, char *more_val);
>  API_EXPORT(void) table_unset(table *, const char *key);
>  API_EXPORT(void) table_add(table *, const char *name, const char *val);
> +API_EXPORT(void) table_addn(table *, char *name, char *val);
>  API_EXPORT(void) table_do(int (*comp) (void *, const char *, const char *), void *rec,
>  			  const table *t,...);
>  
> Index: main/http_core.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/main/http_core.c,v
> retrieving revision 1.147
> diff -u -r1.147 http_core.c
> --- http_core.c	1998/01/21 22:15:56	1.147
> +++ http_core.c	1998/01/24 20:51:40
> @@ -1895,7 +1895,7 @@
>  #endif
>  
>  	if (d->content_md5 & 1) {
> -	    table_set (r->headers_out, "Content-MD5", ap_md5digest(r->pool, f));
> +	    table_setn(r->headers_out, "Content-MD5", ap_md5digest(r->pool, f));
>  	}
>  
>  	rangestatus = set_byterange(r);
> @@ -1956,7 +1956,7 @@
>  	    
>  	    MD5Init(&context);
>  	    MD5Update(&context, (void *)mm, r->finfo.st_size);
> -	    table_set (r->headers_out, "Content-MD5",
> +	    table_setn(r->headers_out, "Content-MD5",
>  		ap_md5contextTo64(r->pool, &context));
>  	}
>  
> Index: main/http_protocol.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/main/http_protocol.c,v
> retrieving revision 1.178
> diff -u -r1.178 http_protocol.c
> --- http_protocol.c	1998/01/23 04:11:32	1.178
> +++ http_protocol.c	1998/01/24 20:51:41
> @@ -138,7 +138,7 @@
>          range = table_get(r->headers_in, "Request-Range");
>  
>      if (!range || strncmp(range, "bytes=", 6)) {
> -        table_set(r->headers_out, "Accept-Ranges", "bytes");
> +        table_setn(r->headers_out, "Accept-Ranges", "bytes");
>          return 0;
>      }
>  
> @@ -165,9 +165,9 @@
>  
>          ap_snprintf(ts, sizeof(ts), "bytes %ld-%ld/%ld",
>                      range_start, range_end, r->clength);
> -        table_set(r->headers_out, "Content-Range", ts);
> +        table_setn(r->headers_out, "Content-Range", pstrdup(r->pool, ts));
>          ap_snprintf(ts, sizeof(ts), "%ld", range_end - range_start + 1);
> -        table_set(r->headers_out, "Content-Length", ts);
> +        table_setn(r->headers_out, "Content-Length", pstrdup(r->pool, ts));
>      }
>      else {
>          /* a multiple range */
> @@ -181,7 +181,7 @@
>          r->boundary = pstrdup(r->pool, boundary);
>          while (internal_byterange(0, &tlength, r, &r_range, NULL, NULL));
>          ap_snprintf(ts, sizeof(ts), "%ld", tlength);
> -        table_set(r->headers_out, "Content-Length", ts);
> +        table_setn(r->headers_out, "Content-Length", pstrdup(r->pool, ts));
>      }
>  
>      r->status = PARTIAL_CONTENT;
> @@ -259,7 +259,7 @@
>      r->clength = clength;
>  
>      ap_snprintf(ts, sizeof(ts), "%ld", clength);
> -    table_set(r->headers_out, "Content-Length", ts);
> +    table_setn(r->headers_out, "Content-Length", pstrdup(r->pool, ts));
>  
>      return 0;
>  }
> @@ -331,8 +331,8 @@
>              else
>                  ap_snprintf(header, sizeof(header), "timeout=%d",
>                              r->server->keep_alive_timeout);
> -            table_set(r->headers_out, "Keep-Alive", header);
> -            table_merge(r->headers_out, "Connection", "Keep-Alive");
> +            table_setn(r->headers_out, "Keep-Alive", pstrdup(r->pool, header));
> +            table_mergen(r->headers_out, "Connection", "Keep-Alive");
>          }
>  
>          return 1;
> @@ -347,7 +347,7 @@
>       * to a HTTP/1.1 client. Better safe than sorry.
>       */
>      if (!wimpy)
> -      table_merge(r->headers_out, "Connection", "close");
> +	table_mergen(r->headers_out, "Connection", "close");
>  
>      r->connection->keepalive = 0;
>  
> @@ -502,7 +502,7 @@
>      }
>  
>      etag = weak_etag + ((r->request_time - r->mtime > 1) ? 2 : 0);
> -    table_set(r->headers_out, "ETag", etag);
> +    table_setn(r->headers_out, "ETag", pstrdup(r->pool, etag));
>  }
>  
>  /*
> @@ -514,7 +514,7 @@
>  {
>      time_t mod_time = rationalize_mtime(r, r->mtime);
>  
> -    table_set(r->headers_out, "Last-Modified",
> +    table_setn(r->headers_out, "Last-Modified",
>                gm_timestr_822(r->pool, mod_time));
>  }
>  
> @@ -753,8 +753,10 @@
>       * overflow (len == MAX_STRING_LEN-1)?
>       */
>      while ((len = getline(field, MAX_STRING_LEN, c->client, 1)) > 0) {
> +        char *copy = palloc(r->pool, len + 1);
> +        memcpy(copy, field, len + 1);
>  
> -        if (!(value = strchr(field, ':')))      /* Find the colon separator */
> +        if (!(value = strchr(copy, ':')))      /* Find the colon separator */
>              continue;           /* or should puke 400 here */
>  
>          *value = '\0';
> @@ -762,7 +764,7 @@
>          while (isspace(*value))
>              ++value;            /* Skip to start of value   */
>  
> -        table_merge(r->headers_in, field, value);
> +        table_mergen(r->headers_in, copy, value);
>      }
>  }
>  
> @@ -906,7 +908,7 @@
>      if (strcasecmp(auth_type(r), "Basic"))
>          note_auth_failure(r);
>      else
> -        table_set(r->err_headers_out,
> +        table_setn(r->err_headers_out,
>                    r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
>                    pstrcat(r->pool, "Basic realm=\"", auth_name(r), "\"",
>                            NULL));
> @@ -917,7 +919,7 @@
>      char nonce[256];
>  
>      ap_snprintf(nonce, sizeof(nonce), "%lu", r->request_time);
> -    table_set(r->err_headers_out,
> +    table_setn(r->err_headers_out,
>                r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
>                pstrcat(r->pool, "Digest realm=\"", auth_name(r),
>                        "\", nonce=\"", nonce, "\"", NULL));
> @@ -1173,8 +1175,8 @@
>  
>      basic_http_header(r);
>  
> -    table_set(r->headers_out, "Content-Length", "0");
> -    table_set(r->headers_out, "Allow", make_allow(r));
> +    table_setn(r->headers_out, "Content-Length", "0");
> +    table_setn(r->headers_out, "Allow", make_allow(r));
>      set_keepalive(r);
>  
>      table_do((int (*) (void *, const char *, const char *)) send_header_field,
> @@ -1231,37 +1233,37 @@
>      set_keepalive(r);
>  
>      if (r->chunked) {
> -        table_merge(r->headers_out, "Transfer-Encoding", "chunked");
> +        table_mergen(r->headers_out, "Transfer-Encoding", "chunked");
>          table_unset(r->headers_out, "Content-Length");
>      }
>  
>      if (r->byterange > 1)
> -        table_set(r->headers_out, "Content-Type",
> +        table_setn(r->headers_out, "Content-Type",
>                    pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/",
>                            "byteranges; boundary=", r->boundary, NULL));
>      else if (r->content_type)
> -        table_set(r->headers_out, "Content-Type", r->content_type);
> +        table_setn(r->headers_out, "Content-Type", r->content_type);
>      else
> -        table_set(r->headers_out, "Content-Type", default_type(r));
> +        table_setn(r->headers_out, "Content-Type", default_type(r));
>  
>      if (r->content_encoding)
> -        table_set(r->headers_out, "Content-Encoding", r->content_encoding);
> +        table_setn(r->headers_out, "Content-Encoding", r->content_encoding);
>  
>      if (r->content_languages && r->content_languages->nelts) {
>          for (i = 0; i < r->content_languages->nelts; ++i) {
> -            table_merge(r->headers_out, "Content-Language",
> +            table_mergen(r->headers_out, "Content-Language",
>                          ((char **) (r->content_languages->elts))[i]);
>          }
>      }
>      else if (r->content_language)
> -        table_set(r->headers_out, "Content-Language", r->content_language);
> +        table_setn(r->headers_out, "Content-Language", r->content_language);
>  
>      /*
>       * Control cachability for non-cachable responses if not already set by
>       * some other part of the server configuration.
>       */
>      if (r->no_cache && !table_get(r->headers_out, "Expires"))
> -        table_add(r->headers_out, "Expires",
> +        table_addn(r->headers_out, "Expires",
>                    gm_timestr_822(r->pool, r->request_time));
>  
>      /* Send the entire table of header fields, terminated by an empty line. */
> @@ -1486,7 +1488,8 @@
>                  get_mime_headers(r);
>                  ap_snprintf(buffer, bufsiz, "%ld", r->read_length);
>                  table_unset(r->headers_in, "Transfer-Encoding");
> -                table_set(r->headers_in, "Content-Length", buffer);
> +                table_setn(r->headers_in, "Content-Length",
> +                    pstrdup(r->pool, buffer));
>                  return 0;
>              }
>              r->remaining = -1;  /* Indicate footers in-progress */
> @@ -1992,7 +1995,7 @@
>  
>          if (location && *location
>              && (is_HTTP_REDIRECT(status) || status == HTTP_CREATED))
> -            table_set(r->headers_out, "Location", location);
> +            table_setn(r->headers_out, "Location", location);
>  
>          r->content_language = NULL;
>          r->content_languages = NULL;
> @@ -2001,7 +2004,7 @@
>          r->content_type = "text/html";
>  
>          if ((status == METHOD_NOT_ALLOWED) || (status == NOT_IMPLEMENTED))
> -            table_set(r->headers_out, "Allow", make_allow(r));
> +            table_setn(r->headers_out, "Allow", make_allow(r));
>  
>          send_http_header(r);
>  
> Index: main/http_request.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/main/http_request.c,v
> retrieving revision 1.100
> diff -u -r1.100 http_request.c
> --- http_request.c	1998/01/21 22:48:13	1.100
> +++ http_request.c	1998/01/24 20:51:42
> @@ -903,7 +903,7 @@
>               * status...
>               */
>              r->status = REDIRECT;
> -            table_set(r->headers_out, "Location", custom_response);
> +            table_setn(r->headers_out, "Location", custom_response);
>          }
>          else if (custom_response[0] == '/') {
>              r->no_local_copy = 1;       /* Do NOT send USE_LOCAL_COPY for
> @@ -912,7 +912,7 @@
>               * This redirect needs to be a GET no matter what the original
>               * method was.
>               */
> -            table_set(r->subprocess_env, "REQUEST_METHOD", r->method);
> +            table_setn(r->subprocess_env, "REQUEST_METHOD", r->method);
>              r->method = pstrdup(r->pool, "GET");
>              r->method_number = M_GET;
>              internal_redirect(custom_response, r);
> @@ -1168,7 +1168,7 @@
>      for (i = 0; i < env_arr->nelts; ++i) {
>          if (!elts[i].key)
>              continue;
> -        table_set(new, pstrcat(p, "REDIRECT_", elts[i].key, NULL),
> +        table_setn(new, pstrcat(p, "REDIRECT_", elts[i].key, NULL),
>                    elts[i].val);
>      }
>  
> @@ -1228,7 +1228,7 @@
>      new->read_length     = r->read_length;     /* We can only read it once */
>  
>      ap_snprintf(t, sizeof(t), "%d", r->status);
> -    table_set(new->subprocess_env, "REDIRECT_STATUS", t);
> +    table_setn(new->subprocess_env, "REDIRECT_STATUS", pstrdup(r->pool, t));
>  
>      /*
>       * XXX: hmm.  This is because mod_setenvif and mod_unique_id really need
> Index: main/util_script.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/main/util_script.c,v
> retrieving revision 1.92
> diff -u -r1.92 util_script.c
> --- util_script.c	1998/01/21 22:31:46	1.92
> +++ util_script.c	1998/01/24 20:51:44
> @@ -205,9 +205,9 @@
>  	 */
>  
>  	if (!strcasecmp(hdrs[i].key, "Content-type"))
> -	    table_set(e, "CONTENT_TYPE", hdrs[i].val);
> +	    table_setn(e, "CONTENT_TYPE", hdrs[i].val);
>  	else if (!strcasecmp(hdrs[i].key, "Content-length"))
> -	    table_set(e, "CONTENT_LENGTH", hdrs[i].val);
> +	    table_setn(e, "CONTENT_LENGTH", hdrs[i].val);
>  	/*
>  	 * You really don't want to disable this check, since it leaves you
>  	 * wide open to CGIs stealing passwords and people viewing them
> @@ -218,7 +218,7 @@
>  	    continue;
>  #endif
>  	else
> -	    table_set(e, http2env(r->pool, hdrs[i].key), hdrs[i].val);
> +	    table_setn(e, http2env(r->pool, hdrs[i].key), hdrs[i].val);
>      }
>  
>      ap_snprintf(port, sizeof(port), "%u", s->port);
> @@ -228,42 +228,42 @@
>  
>  #ifdef WIN32
>      if (env_temp = getenv("SystemRoot"))
> -        table_set(e, "SystemRoot", env_temp);         
> +        table_setn(e, "SystemRoot", env_temp);         
>      if (env_temp = getenv("COMSPEC"))
> -        table_set(e, "COMSPEC", env_temp);            
> +        table_setn(e, "COMSPEC", env_temp);            
>      if (env_temp = getenv("WINDIR"))
> -        table_set(e, "WINDIR", env_temp);             
> +        table_setn(e, "WINDIR", env_temp);             
>  #endif
>  
> -    table_set(e, "PATH", env_path);
> -    table_set(e, "SERVER_SOFTWARE", SERVER_VERSION);
> -    table_set(e, "SERVER_NAME", s->server_hostname);
> -    table_set(e, "SERVER_PORT", port);
> -    table_set(e, "REMOTE_HOST",
> -	      get_remote_host(c, r->per_dir_config, REMOTE_NAME));
> -    table_set(e, "REMOTE_ADDR", c->remote_ip);
> -    table_set(e, "DOCUMENT_ROOT", document_root(r));	/* Apache */
> -    table_set(e, "SERVER_ADMIN", s->server_admin);	/* Apache */
> -    table_set(e, "SCRIPT_FILENAME", r->filename);	/* Apache */
> +    table_setn(e, "PATH", env_path);
> +    table_setn(e, "SERVER_SOFTWARE", SERVER_VERSION);
> +    table_setn(e, "SERVER_NAME", s->server_hostname);
> +    table_setn(e, "SERVER_PORT", pstrdup(r->pool,port));
> +    table_setn(e, "REMOTE_HOST",
> +        pstrdup(r->pool, get_remote_host(c, r->per_dir_config, REMOTE_NAME)));
> +    table_setn(e, "REMOTE_ADDR", c->remote_ip);
> +    table_setn(e, "DOCUMENT_ROOT", document_root(r));	/* Apache */
> +    table_setn(e, "SERVER_ADMIN", s->server_admin);	/* Apache */
> +    table_setn(e, "SCRIPT_FILENAME", r->filename);	/* Apache */
>  
>      ap_snprintf(port, sizeof(port), "%d", ntohs(c->remote_addr.sin_port));
> -    table_set(e, "REMOTE_PORT", port);	/* Apache */
> +    table_setn(e, "REMOTE_PORT", pstrdup(r->pool, port)); /* Apache */
>  
>      if (c->user)
> -	table_set(e, "REMOTE_USER", c->user);
> +	table_setn(e, "REMOTE_USER", c->user);
>      if (c->auth_type)
> -	table_set(e, "AUTH_TYPE", c->auth_type);
> +	table_setn(e, "AUTH_TYPE", c->auth_type);
>      rem_logname = get_remote_logname(r);
>      if (rem_logname)
> -	table_set(e, "REMOTE_IDENT", rem_logname);
> +	table_setn(e, "REMOTE_IDENT", pstrdup(r->pool, rem_logname));
>  
>      /* Apache custom error responses. If we have redirected set two new vars */
>  
>      if (r->prev) {
>  	if (r->prev->args)
> -	    table_set(e, "REDIRECT_QUERY_STRING", r->prev->args);
> +	    table_setn(e, "REDIRECT_QUERY_STRING", r->prev->args);
>  	if (r->prev->uri)
> -	    table_set(e, "REDIRECT_URL", r->prev->uri);
> +	    table_setn(e, "REDIRECT_URL", r->prev->uri);
>      }
>  }
>  
> @@ -316,11 +316,11 @@
>  {
>      table *e = r->subprocess_env;
>  
> -    table_set(e, "GATEWAY_INTERFACE", "CGI/1.1");
> -    table_set(e, "SERVER_PROTOCOL", r->protocol);
> -    table_set(e, "REQUEST_METHOD", r->method);
> -    table_set(e, "QUERY_STRING", r->args ? r->args : "");
> -    table_set(e, "REQUEST_URI", original_uri(r));
> +    table_setn(e, "GATEWAY_INTERFACE", "CGI/1.1");
> +    table_setn(e, "SERVER_PROTOCOL", r->protocol);
> +    table_setn(e, "REQUEST_METHOD", r->method);
> +    table_setn(e, "QUERY_STRING", r->args ? r->args : "");
> +    table_setn(e, "REQUEST_URI", original_uri(r));
>  
>      /* Note that the code below special-cases scripts run from includes,
>       * because it "knows" that the sub_request has been hacked to have the
> @@ -329,20 +329,20 @@
>       */
>  
>      if (!strcmp(r->protocol, "INCLUDED")) {
> -	table_set(e, "SCRIPT_NAME", r->uri);
> +	table_setn(e, "SCRIPT_NAME", r->uri);
>  	if (r->path_info && *r->path_info)
> -	    table_set(e, "PATH_INFO", r->path_info);
> +	    table_setn(e, "PATH_INFO", r->path_info);
>      }
>      else if (!r->path_info || !*r->path_info) {
> -	table_set(e, "SCRIPT_NAME", r->uri);
> +	table_setn(e, "SCRIPT_NAME", r->uri);
>      }
>      else {
>  	int path_info_start = find_path_info(r->uri, r->path_info);
>  
> -	table_set(e, "SCRIPT_NAME", pstrndup(r->pool, r->uri,
> +	table_setn(e, "SCRIPT_NAME", pstrndup(r->pool, r->uri,
>  					     path_info_start));
>  
> -	table_set(e, "PATH_INFO", r->path_info);
> +	table_setn(e, "PATH_INFO", r->path_info);
>      }
>  
>      if (r->path_info && r->path_info[0]) {
> @@ -370,9 +370,9 @@
>  #ifdef WIN32
>  	    /* We need to make this a real Windows path name */
>  	    GetFullPathName(pt, HUGE_STRING_LEN, buffer, NULL);
> -	    table_set(e, "PATH_TRANSLATED", buffer);
> +	    table_setn(e, "PATH_TRANSLATED", pstrdup(r->pool, buffer));
>  #else
> -	    table_set(e, "PATH_TRANSLATED", pt);
> +	    table_setn(e, "PATH_TRANSLATED", pt);
>  #endif
>  	}
>      }
> @@ -471,13 +471,13 @@
>  	    r->status_line = pstrdup(r->pool, l);
>  	}
>  	else if (!strcasecmp(w, "Location")) {
> -	    table_set(r->headers_out, w, l);
> +	    table_setn(r->headers_out, pstrdup(r->pool,w), pstrdup(r->pool,l));
>  	}
>  	else if (!strcasecmp(w, "Content-Length")) {
> -	    table_set(r->headers_out, w, l);
> +	    table_setn(r->headers_out, pstrdup(r->pool,w), pstrdup(r->pool,l));
>  	}
>  	else if (!strcasecmp(w, "Transfer-Encoding")) {
> -	    table_set(r->headers_out, w, l);
> +	    table_setn(r->headers_out, pstrdup(r->pool,w), pstrdup(r->pool,l));
>  	}
>  	/*
>  	 * If the script gave us a Last-Modified header, we can't just
> @@ -494,7 +494,7 @@
>  	 * we'll use - otherwise we assume 200 OK.
>  	 */
>  	else if (!strcasecmp(w, "Status")) {
> -	    table_set(r->headers_out, w, l);
> +	    table_setn(r->headers_out, pstrdup(r->pool,w), pstrdup(r->pool,l));
>  	    cgi_status = atoi(l);
>  	}
>  
> @@ -504,10 +504,10 @@
>  	 * separately.  Lets humour those browsers.
>  	 */
>  	else if (!strcasecmp(w, "Set-Cookie")) {
> -	    table_add(r->err_headers_out, w, l);
> +	    table_addn(r->err_headers_out, pstrdup(r->pool,w), pstrdup(r->pool,l));
>  	}
>  	else {
> -	    table_merge(r->err_headers_out, w, l);
> +	    table_mergen(r->err_headers_out, pstrdup(r->pool,w), pstrdup(r->pool,l));
>  	}
>      }
>  }
> Index: modules/proxy/mod_proxy.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/modules/proxy/mod_proxy.c,v
> retrieving revision 1.32
> diff -u -r1.32 mod_proxy.c
> --- mod_proxy.c	1998/01/24 20:30:07	1.32
> +++ mod_proxy.c	1998/01/24 20:51:44
> @@ -251,7 +251,7 @@
>  		       host, domain, strport, "/", path,
>  		       NULL);
>  
> -	table_set(r->headers_out, "Location", nuri);
> +	table_setn(r->headers_out, "Location", nuri);
>  	aplog_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r->server,
>  	    "Domain missing: %s sent to %s from %s", r->uri, nuri,
>  	    ref ? ref : "-");
> Index: modules/standard/mod_alias.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/modules/standard/mod_alias.c,v
> retrieving revision 1.29
> diff -u -r1.29 mod_alias.c
> --- mod_alias.c	1998/01/07 16:46:43	1.29
> +++ mod_alias.c	1998/01/24 20:51:44
> @@ -328,7 +328,7 @@
>  	if (found) {
>  	    if (p->handler) {	/* Set handler, and leave a note for mod_cgi */
>  		r->handler = pstrdup(r->pool, p->handler);
> -		table_set(r->notes, "alias-forced-type", p->handler);
> +		table_setn(r->notes, "alias-forced-type", r->handler);
>  	    }
>  
>  	    *status = p->redir_status;
> @@ -363,7 +363,7 @@
>  	    if (r->args) {
>  		ret = pstrcat(r->pool, ret, "?", r->args, NULL);
>  	    }
> -	    table_set(r->headers_out, "Location", ret);
> +	    table_setn(r->headers_out, "Location", ret);
>  	}
>  	return status;
>      }
> @@ -388,7 +388,7 @@
>  
>      if ((ret = try_alias_list(r, dirconf->redirects, 1, &status)) != NULL) {
>  	if (is_HTTP_REDIRECT(status))
> -	    table_set(r->headers_out, "Location", ret);
> +	    table_setn(r->headers_out, "Location", ret);
>  	return status;
>      }
>  
> Index: modules/standard/mod_dir.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/modules/standard/mod_dir.c,v
> retrieving revision 1.44
> diff -u -r1.44 mod_dir.c
> --- mod_dir.c	1998/01/07 16:46:47	1.44
> +++ mod_dir.c	1998/01/24 20:51:44
> @@ -117,7 +117,7 @@
>              ifile = pstrcat(r->pool, escape_uri(r->pool, r->uri),
>                              "/", NULL);
>  
> -        table_set(r->headers_out, "Location",
> +        table_setn(r->headers_out, "Location",
>                    construct_url(r->pool, ifile, r->server));
>          return HTTP_MOVED_PERMANENTLY;
>      }
> Index: modules/standard/mod_expires.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/modules/standard/mod_expires.c,v
> retrieving revision 1.21
> diff -u -r1.21 mod_expires.c
> --- mod_expires.c	1998/01/07 16:46:49	1.21
> +++ mod_expires.c	1998/01/24 20:51:45
> @@ -473,11 +473,11 @@
>  
>      expires = base + additional;
>      ap_snprintf(age, sizeof(age), "max-age=%d", (int) expires - (int) r->request_time);
> -    table_set(r->headers_out, "Cache-Control", age);
> +    table_setn(r->headers_out, "Cache-Control", pstrdup(r->pool, age));
>      tzset();                    /* redundant? called implicitly by localtime, at least 
>                                   * under FreeBSD
>                                   */
> -    table_set(r->headers_out, "Expires", gm_timestr_822(r->pool, expires));
> +    table_setn(r->headers_out, "Expires", gm_timestr_822(r->pool, expires));
>      return OK;
>  }
>  
> Index: modules/standard/mod_imap.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/modules/standard/mod_imap.c,v
> retrieving revision 1.39
> diff -u -r1.39 mod_imap.c
> --- mod_imap.c	1998/01/22 23:18:07	1.39
> +++ mod_imap.c	1998/01/24 20:51:45
> @@ -543,7 +543,7 @@
>          return HTTP_NO_CONTENT; /* tell the client to keep the page it has */
>      }
>      if (redirect && *redirect) {
> -        table_set(r->headers_out, "Location", redirect);
> +        table_setn(r->headers_out, "Location", redirect);
>          return REDIRECT;        /* must be a URL, so redirect to it */
>      }
>      return SERVER_ERROR;
> Index: modules/standard/mod_include.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/modules/standard/mod_include.c,v
> retrieving revision 1.65
> diff -u -r1.65 mod_include.c
> --- mod_include.c	1998/01/24 19:00:24	1.65
> +++ mod_include.c	1998/01/24 20:51:46
> @@ -112,36 +112,36 @@
>      char *t;
>      time_t date = r->request_time;
>  
> -    table_set(e, "DATE_LOCAL", ht_time(r->pool, date, timefmt, 0));
> -    table_set(e, "DATE_GMT", ht_time(r->pool, date, timefmt, 1));
> -    table_set(e, "LAST_MODIFIED",
> +    table_setn(e, "DATE_LOCAL", ht_time(r->pool, date, timefmt, 0));
> +    table_setn(e, "DATE_GMT", ht_time(r->pool, date, timefmt, 1));
> +    table_setn(e, "LAST_MODIFIED",
>                ht_time(r->pool, r->finfo.st_mtime, timefmt, 0));
> -    table_set(e, "DOCUMENT_URI", r->uri);
> -    table_set(e, "DOCUMENT_PATH_INFO", r->path_info);
> +    table_setn(e, "DOCUMENT_URI", r->uri);
> +    table_setn(e, "DOCUMENT_PATH_INFO", r->path_info);
>  #ifndef WIN32
>      pw = getpwuid(r->finfo.st_uid);
>      if (pw) {
> -        table_set(e, "USER_NAME", pw->pw_name);
> +        table_setn(e, "USER_NAME", pstrdup(r->pool, pw->pw_name));
>      }
>      else {
>          char uid[16];
>          ap_snprintf(uid, sizeof(uid), "user#%lu",
>                      (unsigned long) r->finfo.st_uid);
> -        table_set(e, "USER_NAME", uid);
> +        table_setn(e, "USER_NAME", pstrdup(r->pool, uid));
>      }
>  #endif /* ndef WIN32 */
>  
>      if ((t = strrchr(r->filename, '/'))) {
> -        table_set(e, "DOCUMENT_NAME", ++t);
> +        table_setn(e, "DOCUMENT_NAME", ++t);
>      }
>      else {
> -        table_set(e, "DOCUMENT_NAME", r->uri);
> +        table_setn(e, "DOCUMENT_NAME", r->uri);
>      }
>      if (r->args) {
>          char *arg_copy = pstrdup(r->pool, r->args);
>  
>          unescape_url(arg_copy);
> -        table_set(e, "QUERY_STRING_UNESCAPED",
> +        table_setn(e, "QUERY_STRING_UNESCAPED",
>                    escape_shell_cmd(r->pool, arg_copy));
>      }
>  }
> @@ -746,11 +746,11 @@
>      if (r->path_info && r->path_info[0] != '\0') {
>          request_rec *pa_req;
>  
> -        table_set(env, "PATH_INFO", escape_shell_cmd(r->pool, r->path_info));
> +        table_setn(env, "PATH_INFO", escape_shell_cmd(r->pool, r->path_info));
>  
>          pa_req = sub_req_lookup_uri(escape_uri(r->pool, r->path_info), r);
>          if (pa_req->filename) {
> -            table_set(env, "PATH_TRANSLATED",
> +            table_setn(env, "PATH_TRANSLATED",
>                        pstrcat(r->pool, pa_req->filename, pa_req->path_info,
>                                NULL));
>          }
> @@ -759,9 +759,9 @@
>      if (r->args) {
>          char *arg_copy = pstrdup(r->pool, r->args);
>  
> -        table_set(env, "QUERY_STRING", r->args);
> +        table_setn(env, "QUERY_STRING", r->args);
>          unescape_url(arg_copy);
> -        table_set(env, "QUERY_STRING_UNESCAPED",
> +        table_setn(env, "QUERY_STRING_UNESCAPED",
>                    escape_shell_cmd(r->pool, arg_copy));
>      }
>  
> @@ -951,9 +951,9 @@
>              time_t date = r->request_time;
>  
>              parse_string(r, tag_val, tf, MAX_STRING_LEN, 0);
> -            table_set(env, "DATE_LOCAL", ht_time(r->pool, date, tf, 0));
> -            table_set(env, "DATE_GMT", ht_time(r->pool, date, tf, 1));
> -            table_set(env, "LAST_MODIFIED",
> +            table_setn(env, "DATE_LOCAL", ht_time(r->pool, date, tf, 0));
> +            table_setn(env, "DATE_GMT", ht_time(r->pool, date, tf, 1));
> +            table_setn(env, "LAST_MODIFIED",
>                        ht_time(r->pool, r->finfo.st_mtime, tf, 0));
>          }
>          else if (!strcmp(tag, "sizefmt")) {
> @@ -2015,7 +2015,7 @@
>                  return -1;
>              }
>              parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
> -            table_set(r->subprocess_env, var, parsed_string);
> +            table_setn(r->subprocess_env, var, pstrdup(r->pool, parsed_string));
>          }
>          else {
>              aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
> @@ -2080,9 +2080,9 @@
>      if (r->args) {              /* add QUERY stuff to env cause it ain't yet */
>          char *arg_copy = pstrdup(r->pool, r->args);
>  
> -        table_set(r->subprocess_env, "QUERY_STRING", r->args);
> +        table_setn(r->subprocess_env, "QUERY_STRING", r->args);
>          unescape_url(arg_copy);
> -        table_set(r->subprocess_env, "QUERY_STRING_UNESCAPED",
> +        table_setn(r->subprocess_env, "QUERY_STRING_UNESCAPED",
>                    escape_shell_cmd(r->pool, arg_copy));
>      }
>  
> Index: modules/standard/mod_log_config.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/modules/standard/mod_log_config.c,v
> retrieving revision 1.42
> diff -u -r1.42 mod_log_config.c
> --- mod_log_config.c	1998/01/07 16:46:52	1.42
> +++ mod_log_config.c	1998/01/24 20:51:47
> @@ -764,7 +764,7 @@
>      mls->default_format = NULL;
>      mls->server_config_logs = NULL;
>      mls->formats = make_table(p, 4);
> -    table_set(mls->formats, "CLF", DEFAULT_LOG_FORMAT);
> +    table_setn(mls->formats, "CLF", DEFAULT_LOG_FORMAT);
>  
>      return mls;
>  }
> Index: modules/standard/mod_negotiation.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/modules/standard/mod_negotiation.c,v
> retrieving revision 1.64
> diff -u -r1.64 mod_negotiation.c
> --- mod_negotiation.c	1998/01/24 19:00:25	1.64
> +++ mod_negotiation.c	1998/01/24 20:51:48
> @@ -1889,24 +1889,24 @@
>          rec = pstrcat(r->pool, rec, "}", NULL);
>  
>          if (na_result != na_not_applied) {
> -            table_merge(hdrs, "Alternates", rec);
> +            table_mergen(hdrs, "Alternates", rec);
>          }
>      }
>  
>      if (na_result != na_not_applied) {
> -        table_merge(hdrs, "Vary", "negotiate");
> +        table_mergen(hdrs, "Vary", "negotiate");
>      }
>      if (vary_by_type) {
> -        table_merge(hdrs, "Vary", "accept");
> +        table_mergen(hdrs, "Vary", "accept");
>      }
>      if (vary_by_language) {
> -        table_merge(hdrs, "Vary", "accept-language");
> +        table_mergen(hdrs, "Vary", "accept-language");
>      }
>      if (vary_by_charset) {
> -        table_merge(hdrs, "Vary", "accept-charset");
> +        table_mergen(hdrs, "Vary", "accept-charset");
>      }
>      if (vary_by_encoding && na_result == na_not_applied) {
> -        table_merge(hdrs, "Vary", "accept-encoding");
> +        table_mergen(hdrs, "Vary", "accept-encoding");
>      }
>  }
>  
> @@ -1955,10 +1955,10 @@
>  static void store_variant_list(request_rec *r, negotiation_state *neg)
>  {
>      if (r->main == NULL) {
> -        table_set(r->notes, "variant-list", make_variant_list(r, neg));
> +        table_setn(r->notes, "variant-list", make_variant_list(r, neg));
>      }
>      else {
> -        table_set(r->main->notes, "variant-list",
> +        table_setn(r->main->notes, "variant-list",
>                    make_variant_list(r->main, neg));
>      }
>  }
> @@ -2004,9 +2004,10 @@
>      }
>  
>      if ((sub_vary = table_get(sub_req->err_headers_out, "Vary")) != NULL) {
> -        table_set(r->err_headers_out, "Variant-Vary", sub_vary);
> +        table_setn(r->err_headers_out, "Variant-Vary", sub_vary);
>      }
> -    table_set(r->err_headers_out, "Content-Location", variant->file_name);
> +    table_setn(r->err_headers_out, "Content-Location",
> +		pstrdup(r->pool, variant->file_name));
>      set_neg_headers(r, neg, na_choice);         /* add Alternates and Vary */
>      /* to do: add Expires */
>  
> Index: modules/standard/mod_rewrite.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/modules/standard/mod_rewrite.c,v
> retrieving revision 1.62
> diff -u -r1.62 mod_rewrite.c
> --- mod_rewrite.c	1998/01/20 00:27:20	1.62
> +++ mod_rewrite.c	1998/01/24 20:51:50
> @@ -908,13 +908,13 @@
>           var = pstrcat(r->pool, "REDIRECT_", ENVVAR_SCRIPT_URL, NULL);
>           var = table_get(r->subprocess_env, var);
>           if (var == NULL) 
> -             table_set(r->subprocess_env, ENVVAR_SCRIPT_URL, r->uri);
> +             table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, r->uri);
>           else 
> -             table_set(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
> +             table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
>      } 
>      else {
>           var = table_get(r->main->subprocess_env, ENVVAR_SCRIPT_URL);
> -         table_set(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
> +         table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
>      }
>  
>      /*
> @@ -943,7 +943,7 @@
>  #else
>      var = pstrcat(r->pool, "http://", thisserver, thisport, thisurl, NULL);
>  #endif
> -    table_set(r->subprocess_env, ENVVAR_SCRIPT_URI, var);
> +    table_setn(r->subprocess_env, ENVVAR_SCRIPT_URI, var);
>  
>  
>      /* if filename was not initially set,
> @@ -1028,7 +1028,7 @@
>                  n = REDIRECT;
>  
>              /* now do the redirection */
> -            table_set(r->headers_out, "Location", r->filename);
> +            table_setn(r->headers_out, "Location", r->filename);
>              rewritelog(r, 1, "redirect to %s [REDIRECT/%d]", r->filename, n);
>              return n;
>          }
> @@ -1298,7 +1298,7 @@
>                  n = REDIRECT;
>  
>              /* now do the redirection */
> -            table_set(r->headers_out, "Location", r->filename);
> +            table_setn(r->headers_out, "Location", r->filename);
>              rewritelog(r, 1, "[per-dir %s] redirect to %s [REDIRECT/%d]",
>                         dconf->directory, r->filename, n);
>              return n;
> @@ -1873,7 +1873,7 @@
>       *  MIME API-hook function.
>       */
>      if (p->forced_mimetype != NULL) {
> -        table_set(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR,
> +        table_setn(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR,
>                    p->forced_mimetype);
>          if (perdir == NULL)
>              rewritelog(r, 2, "remember %s to have MIME-type '%s'",
> Index: modules/standard/mod_setenvif.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/modules/standard/mod_setenvif.c,v
> retrieving revision 1.12
> diff -u -r1.12 mod_setenvif.c
> --- mod_setenvif.c	1998/01/24 19:00:26	1.12
> +++ mod_setenvif.c	1998/01/24 20:51:50
> @@ -310,7 +310,7 @@
>                      table_unset(r->subprocess_env, elts[j].key);
>                  }
>                  else {
> -                    table_set(r->subprocess_env, elts[j].key, elts[j].val);
> +                    table_setn(r->subprocess_env, elts[j].key, elts[j].val);
>                  }
>              }
>          }
> Index: modules/standard/mod_speling.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/modules/standard/mod_speling.c,v
> retrieving revision 1.11
> diff -u -r1.11 mod_speling.c
> --- mod_speling.c	1998/01/13 23:29:13	1.11
> +++ mod_speling.c	1998/01/24 20:51:50
> @@ -338,7 +338,7 @@
>              nuri = pstrcat(r->pool, url, variant[0].name,
>                             r->path_info, NULL);
>  
> -            table_set(r->headers_out, "Location",
> +            table_setn(r->headers_out, "Location",
>                        construct_url(r->pool, nuri, r->server));
>  
>              aplog_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, r->server,
> @@ -408,7 +408,7 @@
>                  ref, "\">referring page</a> about the broken link.\n", NULL);
>  
>              /* Pass our table to http_protocol.c (see mod_negotiation): */
> -            table_set(notes, "variant-list", t);
> +            table_setn(notes, "variant-list", t);
>  
>              aplog_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, r->server,
>                          ref ? "Spelling fix: %s: %d candidates from %s"
> Index: modules/standard/mod_unique_id.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/modules/standard/mod_unique_id.c,v
> retrieving revision 1.9
> diff -u -r1.9 mod_unique_id.c
> --- mod_unique_id.c	1998/01/07 16:46:58	1.9
> +++ mod_unique_id.c	1998/01/24 20:51:50
> @@ -311,7 +311,7 @@
>      str[18] = uuencoder[((x[1] & 0x0f) << 2) | ((0 & 0xc0) >> 6)];
>      str[19] = '\0';
>  
> -    table_set(r->subprocess_env, "UNIQUE_ID", str);
> +    table_setn(r->subprocess_env, "UNIQUE_ID", pstrdup(r->pool, str));
>  
>      /* and increment the identifier for the next call */
>      counter = ntohs(cur_unique_id.counter) + 1;
> Index: modules/standard/mod_userdir.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/modules/standard/mod_userdir.c,v
> retrieving revision 1.25
> diff -u -r1.25 mod_userdir.c
> --- mod_userdir.c	1998/01/07 16:46:58	1.25
> +++ mod_userdir.c	1998/01/24 20:51:50
> @@ -194,7 +194,8 @@
>      (userdir_config *) get_module_config(server_conf, &userdir_module);
>      char *name = r->uri;
>      const char *userdirs = pstrdup(r->pool, s_cfg->userdir);
> -    const char *w, *dname, *redirect;
> +    const char *w, *dname;
> +    char *redirect;
>      char *x = NULL;
>      struct stat statbuf;
>  
> @@ -277,7 +278,7 @@
>                  if (strchr(x, ':')) {
>  #endif                          /* WIN32 */
>                      redirect = pstrcat(r->pool, x, w, userdir, dname, NULL);
> -                    table_set(r->headers_out, "Location", redirect);
> +                    table_setn(r->headers_out, "Location", redirect);
>                      return REDIRECT;
>                  }
>                  else
> @@ -288,7 +289,7 @@
>          }
>          else if (strchr(userdir, ':')) {
>              redirect = pstrcat(r->pool, userdir, "/", w, dname, NULL);
> -            table_set(r->headers_out, "Location", redirect);
> +            table_setn(r->headers_out, "Location", redirect);
>              return REDIRECT;
>          }
>          else {
> Index: modules/standard/mod_usertrack.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/modules/standard/mod_usertrack.c,v
> retrieving revision 1.24
> diff -u -r1.24 mod_usertrack.c
> --- mod_usertrack.c	1998/01/07 16:46:59	1.24
> +++ mod_usertrack.c	1998/01/24 20:51:51
> @@ -135,8 +135,8 @@
>      struct timezone tz = {0, 0};
>  #endif
>      /* 1024 == hardcoded constants */
> -    char *new_cookie = palloc(r->pool, 1024);
> -    char *cookiebuf = palloc(r->pool, 1024);
> +    char new_cookie[1024];
> +    char cookiebuf[1024];
>      char *dot;
>      const char *rname = pstrdup(r->pool,
>                             get_remote_host(r->connection, r->per_dir_config,
> @@ -200,8 +200,8 @@
>      else
>          ap_snprintf(new_cookie, 1024, "%s%s; path=/", COOKIE_NAME, cookiebuf);
>  
> -    table_set(r->headers_out, "Set-Cookie", new_cookie);
> -    table_set(r->notes, "cookie", cookiebuf);   /* log first time */
> +    table_setn(r->headers_out, "Set-Cookie", pstrdup(r->pool, new_cookie));
> +    table_setn(r->notes, "cookie", pstrdup(r->pool, cookiebuf));   /* log first time */
>      return;
>  }
>  
> @@ -226,7 +226,7 @@
>                  *cookieend = '\0';      /* Ignore anything after a ; */
>  
>              /* Set the cookie in a note, for logging */
> -            table_set(r->notes, "cookie", cookiebuf);
> +            table_setn(r->notes, "cookie", cookiebuf);
>  
>              return DECLINED;    /* Theres already a cookie, no new one */
>          }
> 
> 

Re: [PATCH] 7% to 25% performance improvement

Posted by Doug MacEachern <do...@telebusiness.co.nz>.
Dean Gaudet wrote:

> On Sat, 24 Jan 1998, Dean Gaudet wrote:
>
> > -     table_entry *elts = (table_entry *)cld->env->elts;
> > +     array_header *elts = table_elts(cld->env);
> >
> >
> > -     for (i = 0; i < cld->env->nelts; ++i) {
> > +     for (i = 0; i < elts->nelts; ++i) {
> >
> > That's been the "correct" way to do it forever... and will work with
> > all versions of Apache.
>
> Err, excuse me, my brain is in a burrito-coma at the moment.  What
> I really meant to say was:
>
> -     table_entry *elts = (table_entry *)cld->env->elts;
> +     array_header *arr = table_elts(cld->env);
> +     table_entry *elts = (table_entry *)arr->elts;
>
> -     for (i = 0; i < cld->env->nelts; ++i) {
> +     for (i = 0; i < arr->nelts; ++i) {

Thanks Dean.  I see there's a bunch more of these in mod_perl, I'll fix
these up Rob.

-Doug


Re: [PATCH] 7% to 25% performance improvement

Posted by Dean Gaudet <dg...@arctic.org>.

On Sat, 24 Jan 1998, Dean Gaudet wrote:

> -     table_entry *elts = (table_entry *)cld->env->elts;
> +     array_header *elts = table_elts(cld->env);
> 
> 
> -     for (i = 0; i < cld->env->nelts; ++i) {
> +     for (i = 0; i < elts->nelts; ++i) {
> 
> That's been the "correct" way to do it forever... and will work with
> all versions of Apache.

Err, excuse me, my brain is in a burrito-coma at the moment.  What
I really meant to say was:

-     table_entry *elts = (table_entry *)cld->env->elts;
+     array_header *arr = table_elts(cld->env);
+     table_entry *elts = (table_entry *)arr->elts;

-     for (i = 0; i < cld->env->nelts; ++i) {
+     for (i = 0; i < arr->nelts; ++i) {

Dean


Re: [PATCH] 7% to 25% performance improvement

Posted by Dean Gaudet <dg...@arctic.org>.
On Sun, 25 Jan 1998, Rob Hartill wrote:

> On Sat, 24 Jan 1998, Dean Gaudet wrote:
> 
> > This patch is based on code submitted by Dmitry Khrustalev
> > <di...@bog.msu.su>.  The table_(set|add|merge) routines all do pstrdup() 
> > of their key and val.  But this isn't always required... in fact it's
> > almost never required.  In many of the cases where these routines are
> > called the key is a constant string, and the value has been constructed by
> > pstrcat() or getword() or something else which has already allocated in
> > the appropriate pool. 
> 
> bummer. It patched clean but mod_perl needs to be changed too:
> 
> perl_config.c: In function `mod_perl_dir_env':
> perl_config.c:167: dereferencing pointer to incomplete type
> perl_config.c:169: dereferencing pointer to incomplete type
> *** Error code 1

This would be due to the patch I put into the tree to clean up the table
API.

> =====
>   void mod_perl_dir_env(perl_dir_config *cld)
>   { 
>       if(MP_HASENV(cld)) { 
> *     table_entry *elts = (table_entry *)cld->env->elts;
>       int i;
> *     for (i = 0; i < cld->env->nelts; ++i) {
>           MP_TRACE(fprintf(stderr, "mod_perl_dir_env: %s=`%s'",
>                    elts[i].key, elts[i].val));
>           mp_setenv(elts[i].key, elts[i].val);
>       } 
>       MP_HASENV_off(cld); /* just doit once per-request */
>       }
>   }
> =====
> 
> what do I change it to ?

-     table_entry *elts = (table_entry *)cld->env->elts;
+     array_header *elts = table_elts(cld->env);


-     for (i = 0; i < cld->env->nelts; ++i) {
+     for (i = 0; i < elts->nelts; ++i) {

That's been the "correct" way to do it forever... and will work with
all versions of Apache.

Dean


Re: [PATCH] 7% to 25% performance improvement

Posted by Rob Hartill <ro...@imdb.com>.
On Sat, 24 Jan 1998, Dean Gaudet wrote:

> This patch is based on code submitted by Dmitry Khrustalev
> <di...@bog.msu.su>.  The table_(set|add|merge) routines all do pstrdup() 
> of their key and val.  But this isn't always required... in fact it's
> almost never required.  In many of the cases where these routines are
> called the key is a constant string, and the value has been constructed by
> pstrcat() or getword() or something else which has already allocated in
> the appropriate pool. 

bummer. It patched clean but mod_perl needs to be changed too:

perl_config.c: In function `mod_perl_dir_env':
perl_config.c:167: dereferencing pointer to incomplete type
perl_config.c:169: dereferencing pointer to incomplete type
*** Error code 1

=====
  void mod_perl_dir_env(perl_dir_config *cld)
  { 
      if(MP_HASENV(cld)) { 
*     table_entry *elts = (table_entry *)cld->env->elts;
      int i;
*     for (i = 0; i < cld->env->nelts; ++i) {
          MP_TRACE(fprintf(stderr, "mod_perl_dir_env: %s=`%s'",
                   elts[i].key, elts[i].val));
          mp_setenv(elts[i].key, elts[i].val);
      } 
      MP_HASENV_off(cld); /* just doit once per-request */
      }
  }
=====

what do I change it to ?

--
Rob Hartill                              Internet Movie Database (Ltd)
http://www.moviedatabase.com/   .. a site for sore eyes.


Re: [PATCH] 7% to 25% performance improvement

Posted by Dean Gaudet <dg...@arctic.org>.
Folks trying to test POOL_DEBUG will find that they need the following
patch.  The "fast redirect" stuff which happens inside mod_negotiation
plays some liberties with the normal pool ancestry relationship.  It
essentially "promotes" a sub pool into its parent... which is somewhat
questionable.  (Actually the whole "fast redirect" thing is somewhat
questionable... but I can't remember all the gory details at the moment.) 

Note this doesn't change the behaviour at all, it just helps the
POOL_DEBUG code do its job.

Dean

Index: modules/standard/mod_negotiation.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_negotiation.c,v
retrieving revision 1.64
diff -u -r1.64 mod_negotiation.c
--- mod_negotiation.c	1998/01/24 19:00:25	1.64
+++ mod_negotiation.c	1998/01/25 20:36:44
@@ -2189,13 +2190,19 @@
     r->content_language = sub_req->content_language;
     r->finfo = sub_req->finfo;
     r->per_dir_config = sub_req->per_dir_config;
+    /* You may wonder why we're using the sub_req->pool here.  It's to support
+     * POOL_DEBUG in alloc.c.  sub_req->pool will have the same lifetime as
+     * r->pool, we guarantee that by not destroying the sub request below.
+     * We have to guarantee that because we've "promoted" some of the values
+     * from sub_req->pool to r->foobar, like r->filename.
+     */
     /* copy output headers from subrequest, but leave negotiation headers */
-    r->notes = overlay_tables(r->pool, sub_req->notes, r->notes);
-    r->headers_out = overlay_tables(r->pool, sub_req->headers_out,
+    r->notes = overlay_tables(sub_req->pool, sub_req->notes, r->notes);
+    r->headers_out = overlay_tables(sub_req->pool, sub_req->headers_out,
                                     r->headers_out);
-    r->err_headers_out = overlay_tables(r->pool, sub_req->err_headers_out,
+    r->err_headers_out = overlay_tables(sub_req->pool, sub_req->err_headers_out,
                                         r->err_headers_out);
-    r->subprocess_env = overlay_tables(r->pool, sub_req->subprocess_env,
+    r->subprocess_env = overlay_tables(sub_req->pool, sub_req->subprocess_env,
                                        r->subprocess_env);
     avail_recs = (var_rec *) neg->avail_vars->elts;
     for (j = 0; j < neg->avail_vars->nelts; ++j) {