apr_tables.c
来自「Apache 2.0.63 is the current stable vers」· C语言 代码 · 共 1,208 行 · 第 1/3 页
C
1,208 行
apr_table_t *res;
#ifdef POOL_DEBUG
/* we don't copy keys and values, so it's necessary that
* overlay->a.pool and base->a.pool have a life span at least
* as long as p
*/
if (!apr_pool_is_ancestor(overlay->a.pool, p)) {
fprintf(stderr,
"overlay_tables: overlay's pool is not an ancestor of p\n");
abort();
}
if (!apr_pool_is_ancestor(base->a.pool, p)) {
fprintf(stderr,
"overlay_tables: base's pool is not an ancestor of p\n");
abort();
}
#endif
res = apr_palloc(p, sizeof(apr_table_t));
/* behave like append_arrays */
res->a.pool = p;
copy_array_hdr_core(&res->a, &overlay->a);
apr_array_cat(&res->a, &base->a);
table_reindex(res);
return res;
}
/* And now for something completely abstract ...
* For each key value given as a vararg:
* run the function pointed to as
* int comp(void *r, char *key, char *value);
* on each valid key-value pair in the apr_table_t t that matches the vararg key,
* or once for every valid key-value pair if the vararg list is empty,
* until the function returns false (0) or we finish the table.
*
* Note that we restart the traversal for each vararg, which means that
* duplicate varargs will result in multiple executions of the function
* for each matching key. Note also that if the vararg list is empty,
* only one traversal will be made and will cut short if comp returns 0.
*
* Note that the table_get and table_merge functions assume that each key in
* the apr_table_t is unique (i.e., no multiple entries with the same key). This
* function does not make that assumption, since it (unfortunately) isn't
* true for some of Apache's tables.
*
* Note that rec is simply passed-on to the comp function, so that the
* caller can pass additional info for the task.
*
* ADDENDUM for apr_table_vdo():
*
* The caching api will allow a user to walk the header values:
*
* apr_status_t apr_cache_el_header_walk(apr_cache_el *el,
* int (*comp)(void *, const char *, const char *), void *rec, ...);
*
* So it can be ..., however from there I use a callback that use a va_list:
*
* apr_status_t (*cache_el_header_walk)(apr_cache_el *el,
* int (*comp)(void *, const char *, const char *), void *rec, va_list);
*
* To pass those ...'s on down to the actual module that will handle walking
* their headers, in the file case this is actually just an apr_table - and
* rather than reimplementing apr_table_do (which IMHO would be bad) I just
* called it with the va_list. For mod_shmem_cache I don't need it since I
* can't use apr_table's, but mod_file_cache should (though a good hash would
* be better, but that's a different issue :).
*
* So to make mod_file_cache easier to maintain, it's a good thing
*/
APR_DECLARE_NONSTD(int) apr_table_do(apr_table_do_callback_fn_t *comp,
void *rec, const apr_table_t *t, ...)
{
int rv;
va_list vp;
va_start(vp, t);
rv = apr_table_vdo(comp, rec, t, vp);
va_end(vp);
return rv;
}
/* XXX: do the semantics of this routine make any sense? Right now,
* if the caller passed in a non-empty va_list of keys to search for,
* the "early termination" facility only terminates on *that* key; other
* keys will continue to process. Note that this only has any effect
* at all if there are multiple entries in the table with the same key,
* otherwise the called function can never effectively early-terminate
* this function, as the zero return value is effectively ignored.
*
* Note also that this behavior is at odds with the behavior seen if an
* empty va_list is passed in -- in that case, a zero return value terminates
* the entire apr_table_vdo (which is what I think should happen in
* both cases).
*
* If nobody objects soon, I'm going to change the order of the nested
* loops in this function so that any zero return value from the (*comp)
* function will cause a full termination of apr_table_vdo. I'm hesitant
* at the moment because these (funky) semantics have been around for a
* very long time, and although Apache doesn't seem to use them at all,
* some third-party vendor might. I can only think of one possible reason
* the existing semantics would make any sense, and it's very Apache-centric,
* which is this: if (*comp) is looking for matches of a particular
* substring in request headers (let's say it's looking for a particular
* cookie name in the Set-Cookie headers), then maybe it wants to be
* able to stop searching early as soon as it finds that one and move
* on to the next key. That's only an optimization of course, but changing
* the behavior of this function would mean that any code that tried
* to do that would stop working right.
*
* Sigh. --JCW, 06/28/02
*/
APR_DECLARE(int) apr_table_vdo(apr_table_do_callback_fn_t *comp,
void *rec, const apr_table_t *t, va_list vp)
{
char *argp;
apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts;
int vdorv = 1;
argp = va_arg(vp, char *);
do {
int rv = 1, i;
if (argp) {
/* Scan for entries that match the next key */
int hash = TABLE_HASH(argp);
if (TABLE_INDEX_IS_INITIALIZED(t, hash)) {
apr_uint32_t checksum;
COMPUTE_KEY_CHECKSUM(argp, checksum);
for (i = t->index_first[hash];
rv && (i <= t->index_last[hash]); ++i) {
if (elts[i].key && (checksum == elts[i].key_checksum) &&
!strcasecmp(elts[i].key, argp)) {
rv = (*comp) (rec, elts[i].key, elts[i].val);
}
}
}
}
else {
/* Scan the entire table */
for (i = 0; rv && (i < t->a.nelts); ++i) {
if (elts[i].key) {
rv = (*comp) (rec, elts[i].key, elts[i].val);
}
}
}
if (rv == 0) {
vdorv = 0;
}
} while (argp && ((argp = va_arg(vp, char *)) != NULL));
return vdorv;
}
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;
}
APR_DECLARE(void) apr_table_compress(apr_table_t *t, unsigned flags)
{
apr_table_entry_t **sort_array;
apr_table_entry_t **sort_next;
apr_table_entry_t **sort_end;
apr_table_entry_t *table_next;
apr_table_entry_t **last;
int i;
int dups_found;
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);
}
table_reindex(t);
}
static void apr_table_cat(apr_table_t *t, const apr_table_t *s)
{
const int n = t->a.nelts;
register int idx;
apr_array_cat(&t->a,&s->a);
if (n == 0) {
memcpy(t->index_first,s->index_first,sizeof(int) * TABLE_HASH_SIZE);
memcpy(t->index_last, s->index_last, sizeof(int) * TABLE_HASH_SIZE);
t->index_initialized = s->index_initialized;
return;
}
for (idx = 0; idx < TABLE_HASH_SIZE; ++idx) {
if (TABLE_INDEX_IS_INITIALIZED(s, idx)) {
t->index_last[idx] = s->index_last[idx] + n;
if (!TABLE_INDEX_IS_INITIALIZED(t, idx)) {
t->index_first[idx] = s->index_first[idx] + n;
}
}
}
t->index_initialized |= s->index_initialized;
}
APR_DECLARE(void) apr_table_overlap(apr_table_t *a, const apr_table_t *b,
unsigned flags)
{
const int m = a->a.nelts;
const int n = b->a.nelts;
apr_pool_t *p = b->a.pool;
if (m + n == 0) {
return;
}
/* copy (extend) a using b's pool */
if (a->a.pool != p) {
make_array_core(&a->a, p, m+n, sizeof(apr_table_entry_t), 0);
}
apr_table_cat(a, b);
apr_table_compress(a, flags);
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?