📄 apr_tables.c
字号:
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 + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -