geoff 2004/02/16 11:58:18
Modified: . Changes src/modules/perl modperl_apache_compat.c t/response/TestAPR table.pm Log: copy apr_table_compress logic from later httpd versions in case mod_perl is built against 2.0.46, as mod_perl now requires it internally. Revision Changes Path 1.331 +5 -0 modperl-2.0/Changes Index: Changes =================================================================== RCS file: /home/cvs/modperl-2.0/Changes,v retrieving revision 1.330 retrieving revision 1.331 diff -u -r1.330 -r1.331 --- Changes 14 Feb 2004 01:38:05 -0000 1.330 +++ Changes 16 Feb 2004 19:58:18 -0000 1.331 @@ -12,6 +12,11 @@ =item 1.99_13-dev +copy apr_table_compress logic from later httpd versions in case mod_perl +is built against 2.0.46, as mod_perl now requires it internally. users +should be aware that 2.0.47 may become the oldest supported httpd version +in the near future. [Geoffrey Young] + Fix the corruption of the httpd process argv[0], caused by $0 manipulating [Stas] 1.4 +217 -2 modperl-2.0/src/modules/perl/modperl_apache_compat.c Index: modperl_apache_compat.c =================================================================== RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_apache_compat.c,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- modperl_apache_compat.c 22 Aug 2003 19:15:09 -0000 1.3 +++ modperl_apache_compat.c 16 Feb 2004 19:58:18 -0000 1.4 @@ -10,10 +10,225 @@ which is major mmn 20020903, minor mmn 4 */ #if ! AP_MODULE_MAGIC_AT_LEAST(20020903,4) -/* added in APACHE_2_0_47/APR_0_9_4 */ +/* added in APACHE_2_0_47/APR_0_9_4 - duplicated here for 2.0.46. + * I'd rather not duplicate it, but we use apr_table_compress + * in the directive merge routines, so it's either duplicate it + * here, recode the compress logic there, or drop 2.0.46 support + */ + +#define TABLE_HASH_SIZE 32 + +struct apr_table_t { + apr_array_header_t a; +#ifdef MAKE_TABLE_PROFILE + /** Who created the array. */ + void *creator; +#endif + apr_uint32_t index_initialized; + int index_first[TABLE_HASH_SIZE]; + int index_last[TABLE_HASH_SIZE]; +}; + +static apr_table_entry_t **table_mergesort(apr_pool_t *pool, + apr_table_entry_t **values, int n) +{ + /* Bottom-up mergesort, based on design in Sedgewick's "Algorithms + * in C," chapter 8 + */ + apr_table_entry_t **values_tmp = + (apr_table_entry_t **)apr_palloc(pool, n * sizeof(apr_table_entry_t*)); + int i; + int blocksize; + + /* First pass: sort pairs of elements (blocksize=1) */ + for (i = 0; i + 1 < n; i += 2) { + if (strcasecmp(values[i]->key, values[i + 1]->key) > 0) { + apr_table_entry_t *swap = values[i]; + values[i] = values[i + 1]; + values[i + 1] = swap; + } + } + + /* Merge successively larger blocks */ + blocksize = 2; + while (blocksize < n) { + apr_table_entry_t **dst = values_tmp; + int next_start; + apr_table_entry_t **swap; + + /* Merge consecutive pairs blocks of the next blocksize. + * Within a block, elements are in sorted order due to + * the previous iteration. + */ + for (next_start = 0; next_start + blocksize < n; + next_start += (blocksize + blocksize)) { + + int block1_start = next_start; + int block2_start = block1_start + blocksize; + int block1_end = block2_start; + int block2_end = block2_start + blocksize; + if (block2_end > n) { + /* The last block may be smaller than blocksize */ + block2_end = n; + } + for (;;) { + + /* Merge the next two blocks: + * Pick the smaller of the next element from + * block 1 and the next element from block 2. + * Once either of the blocks is emptied, copy + * over all the remaining elements from the + * other block + */ + if (block1_start == block1_end) { + for (; block2_start < block2_end; block2_start++) { + *dst++ = values[block2_start]; + } + break; + } + else if (block2_start == block2_end) { + for (; block1_start < block1_end; block1_start++) { + *dst++ = values[block1_start]; + } + break; + } + if (strcasecmp(values[block1_start]->key, + values[block2_start]->key) > 0) { + *dst++ = values[block2_start++]; + } + else { + *dst++ = values[block1_start++]; + } + } + } + + /* If n is not a multiple of 2*blocksize, some elements + * will be left over at the end of the array. + */ + for (i = dst - values_tmp; i < n; i++) { + values_tmp[i] = values[i]; + } + + /* The output array of this pass becomes the input + * array of the next pass, and vice versa + */ + swap = values_tmp; + values_tmp = values; + values = swap; + + blocksize += blocksize; + } + + return values; +} void apr_table_compress(apr_table_t *t, unsigned flags) { - modperl_apr_func_not_implemented(apr_table_compress, 2.0.47, 0.9.4); + apr_table_entry_t **sort_array; + apr_table_entry_t **sort_next; + apr_table_entry_t **sort_end; + apr_table_entry_t *table_next; + apr_table_entry_t **last; + int i; + int dups_found; + + if (t->a.nelts <= 1) { + return; + } + + /* Copy pointers to all the table elements into an + * array and sort to allow for easy detection of + * duplicate keys + */ + sort_array = (apr_table_entry_t **) + apr_palloc(t->a.pool, t->a.nelts * sizeof(apr_table_entry_t*)); + sort_next = sort_array; + table_next = (apr_table_entry_t *)t->a.elts; + i = t->a.nelts; + do { + *sort_next++ = table_next++; + } while (--i); + + /* Note: the merge is done with mergesort instead of quicksort + * because mergesort is a stable sort and runs in n*log(n) + * time regardless of its inputs (quicksort is quadratic in + * the worst case) + */ + sort_array = table_mergesort(t->a.pool, sort_array, t->a.nelts); + + /* Process any duplicate keys */ + dups_found = 0; + sort_next = sort_array; + sort_end = sort_array + t->a.nelts; + last = sort_next++; + while (sort_next < sort_end) { + if (((*sort_next)->key_checksum == (*last)->key_checksum) && + !strcasecmp((*sort_next)->key, (*last)->key)) { + apr_table_entry_t **dup_last = sort_next + 1; + dups_found = 1; + while ((dup_last < sort_end) && + ((*dup_last)->key_checksum == (*last)->key_checksum) && + !strcasecmp((*dup_last)->key, (*last)->key)) { + dup_last++; + } + dup_last--; /* Elements from last through dup_last, inclusive, + * all have the same key + */ + if (flags == APR_OVERLAP_TABLES_MERGE) { + apr_size_t len = 0; + apr_table_entry_t **next = last; + char *new_val; + char *val_dst; + do { + len += strlen((*next)->val); + len += 2; /* for ", " or trailing null */ + } while (++next <= dup_last); + new_val = (char *)apr_palloc(t->a.pool, len); + val_dst = new_val; + next = last; + for (;;) { + strcpy(val_dst, (*next)->val); + val_dst += strlen((*next)->val); + next++; + if (next > dup_last) { + *val_dst = 0; + break; + } + else { + *val_dst++ = ','; + *val_dst++ = ' '; + } + } + (*last)->val = new_val; + } + else { /* overwrite */ + (*last)->val = (*dup_last)->val; + } + do { + (*sort_next)->key = NULL; + } while (++sort_next <= dup_last); + } + else { + last = sort_next++; + } + } + + /* Shift elements to the left to fill holes left by removing duplicates */ + if (dups_found) { + apr_table_entry_t *src = (apr_table_entry_t *)t->a.elts; + apr_table_entry_t *dst = (apr_table_entry_t *)t->a.elts; + apr_table_entry_t *last_elt = src + t->a.nelts; + do { + if (src->key) { + *dst++ = *src; + } + } while (++src < last_elt); + t->a.nelts -= (last_elt - dst); + } + + /* XXX mod_perl: remove to avoid more code duplication. + * XXX makes us slower on 2.0.46 + */ + /* table_reindex(t); */ } #endif /* pre-APR_0_9_5 (APACHE_2_0_47) */ 1.13 +10 -16 modperl-2.0/t/response/TestAPR/table.pm Index: table.pm =================================================================== RCS file: /home/cvs/modperl-2.0/t/response/TestAPR/table.pm,v retrieving revision 1.12 retrieving revision 1.13 diff -u -r1.12 -r1.13 --- table.pm 25 Nov 2003 20:31:29 -0000 1.12 +++ table.pm 16 Feb 2004 19:58:18 -0000 1.13 @@ -14,13 +14,10 @@ my $filter_count; my $TABLE_SIZE = 20; -use constant HAVE_APACHE_2_0_47 => have_min_apache_version('2.0.47'); - sub handler { my $r = shift; - my $tests = 21; - $tests += 2 if HAVE_APACHE_2_0_47; + my $tests = 23; plan $r, tests => $tests; @@ -133,18 +130,15 @@ ok @foo == 3; ok $bar[0] eq 'beer'; - # BACK_COMPAT_MARKER: make back compat issues easy to find :) - if (HAVE_APACHE_2_0_47) { - $overlay->compress(APR::OVERLAP_TABLES_MERGE); - - # $add first, then $base - ok t_cmp($overlay->get('foo'), - 'three, one, two', - "\$overlay->compress"); - ok t_cmp($overlay->get('bar'), - 'beer', - "\$overlay->compress"); - } + $overlay->compress(APR::OVERLAP_TABLES_MERGE); + + # $add first, then $base + ok t_cmp($overlay->get('foo'), + 'three, one, two', + "\$overlay->compress"); + ok t_cmp($overlay->get('bar'), + 'beer', + "\$overlay->compress"); Apache::OK; }