/src/httpd/srclib/apr/tables/apr_tables.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Licensed to the Apache Software Foundation (ASF) under one or more |
2 | | * contributor license agreements. See the NOTICE file distributed with |
3 | | * this work for additional information regarding copyright ownership. |
4 | | * The ASF licenses this file to You under the Apache License, Version 2.0 |
5 | | * (the "License"); you may not use this file except in compliance with |
6 | | * the License. You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | /* |
18 | | * Resource allocation code... the code here is responsible for making |
19 | | * sure that nothing leaks. |
20 | | * |
21 | | * rst --- 4/95 --- 6/95 |
22 | | */ |
23 | | |
24 | | #include "apr_private.h" |
25 | | |
26 | | #include "apr_general.h" |
27 | | #include "apr_pools.h" |
28 | | #include "apr_tables.h" |
29 | | #include "apr_strings.h" |
30 | | #include "apr_lib.h" |
31 | | #if APR_HAVE_STDLIB_H |
32 | | #include <stdlib.h> |
33 | | #endif |
34 | | #if APR_HAVE_STRING_H |
35 | | #include <string.h> |
36 | | #endif |
37 | | #if APR_HAVE_STRINGS_H |
38 | | #include <strings.h> |
39 | | #endif |
40 | | |
41 | | #ifndef APR_TABLE_POOL_DEBUG |
42 | | #define APR_TABLE_POOL_DEBUG 0 |
43 | | #endif |
44 | | |
45 | | #if (APR_TABLE_POOL_DEBUG || defined(MAKE_TABLE_PROFILE)) && APR_HAVE_STDIO_H |
46 | | #include <stdio.h> |
47 | | #endif |
48 | | |
49 | | /***************************************************************** |
50 | | * This file contains array and apr_table_t functions only. |
51 | | */ |
52 | | |
53 | | /***************************************************************** |
54 | | * |
55 | | * The 'array' functions... |
56 | | */ |
57 | | |
58 | | static void make_array_core(apr_array_header_t *res, apr_pool_t *p, |
59 | | int nelts, int elt_size, int clear) |
60 | 5.31k | { |
61 | | /* |
62 | | * Assure sanity if someone asks for |
63 | | * array of zero elts. |
64 | | */ |
65 | 5.31k | if (nelts < 1) { |
66 | 0 | nelts = 1; |
67 | 0 | } |
68 | | |
69 | 5.31k | if (clear) { |
70 | 654 | res->elts = apr_pcalloc(p, nelts * elt_size); |
71 | 654 | } |
72 | 4.66k | else { |
73 | 4.66k | res->elts = apr_palloc(p, nelts * elt_size); |
74 | 4.66k | } |
75 | | |
76 | 5.31k | res->pool = p; |
77 | 5.31k | res->elt_size = elt_size; |
78 | 5.31k | res->nelts = 0; /* No active elements yet... */ |
79 | 5.31k | res->nalloc = nelts; /* ...but this many allocated */ |
80 | 5.31k | } |
81 | | |
82 | | APR_DECLARE(int) apr_is_empty_array(const apr_array_header_t *a) |
83 | 0 | { |
84 | 0 | return ((a == NULL) || (a->nelts == 0)); |
85 | 0 | } |
86 | | |
87 | | APR_DECLARE(apr_array_header_t *) apr_array_make(apr_pool_t *p, |
88 | | int nelts, int elt_size) |
89 | 654 | { |
90 | 654 | apr_array_header_t *res; |
91 | | |
92 | 654 | res = (apr_array_header_t *) apr_palloc(p, sizeof(apr_array_header_t)); |
93 | 654 | make_array_core(res, p, nelts, elt_size, 1); |
94 | 654 | return res; |
95 | 654 | } |
96 | | |
97 | | APR_DECLARE(void) apr_array_clear(apr_array_header_t *arr) |
98 | 0 | { |
99 | 0 | arr->nelts = 0; |
100 | 0 | } |
101 | | |
102 | | APR_DECLARE(void *) apr_array_pop(apr_array_header_t *arr) |
103 | 0 | { |
104 | 0 | if (apr_is_empty_array(arr)) { |
105 | 0 | return NULL; |
106 | 0 | } |
107 | | |
108 | 0 | return arr->elts + (arr->elt_size * (--arr->nelts)); |
109 | 0 | } |
110 | | |
111 | | APR_DECLARE(void *) apr_array_push(apr_array_header_t *arr) |
112 | 3.12k | { |
113 | 3.12k | if (arr->nelts == arr->nalloc) { |
114 | 59 | int new_size = (arr->nalloc <= 0) ? 1 : arr->nalloc * 2; |
115 | 59 | char *new_data; |
116 | | |
117 | 59 | new_data = apr_palloc(arr->pool, arr->elt_size * new_size); |
118 | | |
119 | 59 | memcpy(new_data, arr->elts, arr->nalloc * arr->elt_size); |
120 | 59 | memset(new_data + arr->nalloc * arr->elt_size, 0, |
121 | 59 | arr->elt_size * (new_size - arr->nalloc)); |
122 | 59 | arr->elts = new_data; |
123 | 59 | arr->nalloc = new_size; |
124 | 59 | } |
125 | | |
126 | 3.12k | ++arr->nelts; |
127 | 3.12k | return arr->elts + (arr->elt_size * (arr->nelts - 1)); |
128 | 3.12k | } |
129 | | |
130 | | static void *apr_array_push_noclear(apr_array_header_t *arr) |
131 | 1.93k | { |
132 | 1.93k | if (arr->nelts == arr->nalloc) { |
133 | 37 | int new_size = (arr->nalloc <= 0) ? 1 : arr->nalloc * 2; |
134 | 37 | char *new_data; |
135 | | |
136 | 37 | new_data = apr_palloc(arr->pool, arr->elt_size * new_size); |
137 | | |
138 | 37 | memcpy(new_data, arr->elts, arr->nalloc * arr->elt_size); |
139 | 37 | arr->elts = new_data; |
140 | 37 | arr->nalloc = new_size; |
141 | 37 | } |
142 | | |
143 | 1.93k | ++arr->nelts; |
144 | 1.93k | return arr->elts + (arr->elt_size * (arr->nelts - 1)); |
145 | 1.93k | } |
146 | | |
147 | | APR_DECLARE(void) apr_array_cat(apr_array_header_t *dst, |
148 | | const apr_array_header_t *src) |
149 | 0 | { |
150 | 0 | int elt_size = dst->elt_size; |
151 | |
|
152 | 0 | if (dst->nelts + src->nelts > dst->nalloc) { |
153 | 0 | int new_size = (dst->nalloc <= 0) ? 1 : dst->nalloc * 2; |
154 | 0 | char *new_data; |
155 | |
|
156 | 0 | while (dst->nelts + src->nelts > new_size) { |
157 | 0 | new_size *= 2; |
158 | 0 | } |
159 | |
|
160 | 0 | new_data = apr_pcalloc(dst->pool, elt_size * new_size); |
161 | 0 | memcpy(new_data, dst->elts, dst->nalloc * elt_size); |
162 | |
|
163 | 0 | dst->elts = new_data; |
164 | 0 | dst->nalloc = new_size; |
165 | 0 | } |
166 | |
|
167 | 0 | memcpy(dst->elts + dst->nelts * elt_size, src->elts, |
168 | 0 | elt_size * src->nelts); |
169 | 0 | dst->nelts += src->nelts; |
170 | 0 | } |
171 | | |
172 | | APR_DECLARE(apr_array_header_t *) apr_array_copy(apr_pool_t *p, |
173 | | const apr_array_header_t *arr) |
174 | 0 | { |
175 | 0 | apr_array_header_t *res = |
176 | 0 | (apr_array_header_t *) apr_palloc(p, sizeof(apr_array_header_t)); |
177 | 0 | make_array_core(res, p, arr->nalloc, arr->elt_size, 0); |
178 | |
|
179 | 0 | memcpy(res->elts, arr->elts, arr->elt_size * arr->nelts); |
180 | 0 | res->nelts = arr->nelts; |
181 | 0 | memset(res->elts + res->elt_size * res->nelts, 0, |
182 | 0 | res->elt_size * (res->nalloc - res->nelts)); |
183 | 0 | return res; |
184 | 0 | } |
185 | | |
186 | | /* This cute function copies the array header *only*, but arranges |
187 | | * for the data section to be copied on the first push or arraycat. |
188 | | * It's useful when the elements of the array being copied are |
189 | | * read only, but new stuff *might* get added on the end; we have the |
190 | | * overhead of the full copy only where it is really needed. |
191 | | */ |
192 | | |
193 | | static APR_INLINE void copy_array_hdr_core(apr_array_header_t *res, |
194 | | const apr_array_header_t *arr) |
195 | 0 | { |
196 | 0 | res->elts = arr->elts; |
197 | 0 | res->elt_size = arr->elt_size; |
198 | 0 | res->nelts = arr->nelts; |
199 | 0 | res->nalloc = arr->nelts; /* Force overflow on push */ |
200 | 0 | } |
201 | | |
202 | | APR_DECLARE(apr_array_header_t *) |
203 | | apr_array_copy_hdr(apr_pool_t *p, |
204 | | const apr_array_header_t *arr) |
205 | 0 | { |
206 | 0 | apr_array_header_t *res; |
207 | |
|
208 | 0 | res = (apr_array_header_t *) apr_palloc(p, sizeof(apr_array_header_t)); |
209 | 0 | res->pool = p; |
210 | 0 | copy_array_hdr_core(res, arr); |
211 | 0 | return res; |
212 | 0 | } |
213 | | |
214 | | /* The above is used here to avoid consing multiple new array bodies... */ |
215 | | |
216 | | APR_DECLARE(apr_array_header_t *) |
217 | | apr_array_append(apr_pool_t *p, |
218 | | const apr_array_header_t *first, |
219 | | const apr_array_header_t *second) |
220 | 0 | { |
221 | 0 | apr_array_header_t *res = apr_array_copy_hdr(p, first); |
222 | |
|
223 | 0 | apr_array_cat(res, second); |
224 | 0 | return res; |
225 | 0 | } |
226 | | |
227 | | /* apr_array_pstrcat generates a new string from the apr_pool_t containing |
228 | | * the concatenated sequence of substrings referenced as elements within |
229 | | * the array. The string will be empty if all substrings are empty or null, |
230 | | * or if there are no elements in the array. |
231 | | * If sep is non-NUL, it will be inserted between elements as a separator. |
232 | | */ |
233 | | APR_DECLARE(char *) apr_array_pstrcat(apr_pool_t *p, |
234 | | const apr_array_header_t *arr, |
235 | | const char sep) |
236 | 0 | { |
237 | 0 | char *cp, *res, **strpp; |
238 | 0 | apr_size_t len; |
239 | 0 | int i; |
240 | |
|
241 | 0 | if (arr->nelts <= 0 || arr->elts == NULL) { /* Empty table? */ |
242 | 0 | return (char *) apr_pcalloc(p, 1); |
243 | 0 | } |
244 | | |
245 | | /* Pass one --- find length of required string */ |
246 | | |
247 | 0 | len = 0; |
248 | 0 | for (i = 0, strpp = (char **) arr->elts; ; ++strpp) { |
249 | 0 | if (strpp && *strpp != NULL) { |
250 | 0 | len += strlen(*strpp); |
251 | 0 | } |
252 | 0 | if (++i >= arr->nelts) { |
253 | 0 | break; |
254 | 0 | } |
255 | 0 | if (sep) { |
256 | 0 | ++len; |
257 | 0 | } |
258 | 0 | } |
259 | | |
260 | | /* Allocate the required string */ |
261 | |
|
262 | 0 | res = (char *) apr_palloc(p, len + 1); |
263 | 0 | cp = res; |
264 | | |
265 | | /* Pass two --- copy the argument strings into the result space */ |
266 | |
|
267 | 0 | for (i = 0, strpp = (char **) arr->elts; ; ++strpp) { |
268 | 0 | if (strpp && *strpp != NULL) { |
269 | 0 | len = strlen(*strpp); |
270 | 0 | memcpy(cp, *strpp, len); |
271 | 0 | cp += len; |
272 | 0 | } |
273 | 0 | if (++i >= arr->nelts) { |
274 | 0 | break; |
275 | 0 | } |
276 | 0 | if (sep) { |
277 | 0 | *cp++ = sep; |
278 | 0 | } |
279 | 0 | } |
280 | |
|
281 | 0 | *cp = '\0'; |
282 | | |
283 | | /* Return the result string */ |
284 | |
|
285 | 0 | return res; |
286 | 0 | } |
287 | | |
288 | | |
289 | | /***************************************************************** |
290 | | * |
291 | | * The "table" functions. |
292 | | */ |
293 | | |
294 | | #if APR_CHARSET_EBCDIC |
295 | | #define CASE_MASK 0xbfbfbfbf |
296 | | #else |
297 | 3.47k | #define CASE_MASK 0xdfdfdfdf |
298 | | #endif |
299 | | |
300 | 0 | #define TABLE_HASH_SIZE 32 |
301 | 3.97k | #define TABLE_INDEX_MASK 0x1f |
302 | 3.97k | #define TABLE_HASH(key) (TABLE_INDEX_MASK & *(unsigned char *)(key)) |
303 | 3.97k | #define TABLE_INDEX_IS_INITIALIZED(t, i) ((t)->index_initialized & (1u << (i))) |
304 | 1.29k | #define TABLE_SET_INDEX_INITIALIZED(t, i) ((t)->index_initialized |= (1u << (i))) |
305 | | |
306 | | /* Compute the "checksum" for a key, consisting of the first |
307 | | * 4 bytes, normalized for case-insensitivity and packed into |
308 | | * an int...this checksum allows us to do a single integer |
309 | | * comparison as a fast check to determine whether we can |
310 | | * skip a strcasecmp |
311 | | */ |
312 | 3.47k | #define COMPUTE_KEY_CHECKSUM(key, checksum) \ |
313 | 3.47k | { \ |
314 | 3.47k | const char *k = (key); \ |
315 | 3.47k | apr_uint32_t c = (apr_uint32_t)*k; \ |
316 | 3.47k | (checksum) = c; \ |
317 | 3.47k | (checksum) <<= 8; \ |
318 | 3.47k | if (c) { \ |
319 | 3.32k | c = (apr_uint32_t)*++k; \ |
320 | 3.32k | checksum |= c; \ |
321 | 3.32k | } \ |
322 | 3.47k | (checksum) <<= 8; \ |
323 | 3.47k | if (c) { \ |
324 | 2.63k | c = (apr_uint32_t)*++k; \ |
325 | 2.63k | checksum |= c; \ |
326 | 2.63k | } \ |
327 | 3.47k | (checksum) <<= 8; \ |
328 | 3.47k | if (c) { \ |
329 | 2.39k | c = (apr_uint32_t)*++k; \ |
330 | 2.39k | checksum |= c; \ |
331 | 2.39k | } \ |
332 | 3.47k | checksum &= CASE_MASK; \ |
333 | 3.47k | } |
334 | | |
335 | | /** The opaque string-content table type */ |
336 | | struct apr_table_t { |
337 | | /* This has to be first to promote backwards compatibility with |
338 | | * older modules which cast a apr_table_t * to an apr_array_header_t *... |
339 | | * they should use the apr_table_elts() function for most of the |
340 | | * cases they do this for. |
341 | | */ |
342 | | /** The underlying array for the table */ |
343 | | apr_array_header_t a; |
344 | | #ifdef MAKE_TABLE_PROFILE |
345 | | /** Who created the array. */ |
346 | | void *creator; |
347 | | #endif |
348 | | /* An index to speed up table lookups. The way this works is: |
349 | | * - Hash the key into the index: |
350 | | * - index_first[TABLE_HASH(key)] is the offset within |
351 | | * the table of the first entry with that key |
352 | | * - index_last[TABLE_HASH(key)] is the offset within |
353 | | * the table of the last entry with that key |
354 | | * - If (and only if) there is no entry in the table whose |
355 | | * key hashes to index element i, then the i'th bit |
356 | | * of index_initialized will be zero. (Check this before |
357 | | * trying to use index_first[i] or index_last[i]!) |
358 | | */ |
359 | | apr_uint32_t index_initialized; |
360 | | int index_first[TABLE_HASH_SIZE]; |
361 | | int index_last[TABLE_HASH_SIZE]; |
362 | | }; |
363 | | |
364 | | /* keep state for apr_table_getm() */ |
365 | | typedef struct |
366 | | { |
367 | | apr_pool_t *p; |
368 | | const char *first; |
369 | | apr_array_header_t *merged; |
370 | | } table_getm_t; |
371 | | |
372 | | /* |
373 | | * NOTICE: if you tweak this you should look at is_empty_table() |
374 | | * and table_elts() in alloc.h |
375 | | */ |
376 | | #ifdef MAKE_TABLE_PROFILE |
377 | | static apr_table_entry_t *do_table_push(const char *func, apr_table_t *t) |
378 | | { |
379 | | if (t->a.nelts == t->a.nalloc) { |
380 | | fprintf(stderr, "%s: table created by %p hit limit of %u\n", |
381 | | func ? func : "table_push", t->creator, t->a.nalloc); |
382 | | } |
383 | | return (apr_table_entry_t *) apr_array_push_noclear(&t->a); |
384 | | } |
385 | | #if defined(__GNUC__) && __GNUC__ >= 2 |
386 | | #define table_push(t) do_table_push(__FUNCTION__, t) |
387 | | #else |
388 | | #define table_push(t) do_table_push(NULL, t) |
389 | | #endif |
390 | | #else /* MAKE_TABLE_PROFILE */ |
391 | 1.93k | #define table_push(t) ((apr_table_entry_t *) apr_array_push_noclear(&(t)->a)) |
392 | | #endif /* MAKE_TABLE_PROFILE */ |
393 | | |
394 | | APR_DECLARE(const apr_array_header_t *) apr_table_elts(const apr_table_t *t) |
395 | 0 | { |
396 | 0 | return (const apr_array_header_t *)t; |
397 | 0 | } |
398 | | |
399 | | APR_DECLARE(int) apr_is_empty_table(const apr_table_t *t) |
400 | 0 | { |
401 | 0 | return ((t == NULL) || (t->a.nelts == 0)); |
402 | 0 | } |
403 | | |
404 | | APR_DECLARE(apr_table_t *) apr_table_make(apr_pool_t *p, int nelts) |
405 | 4.66k | { |
406 | 4.66k | apr_table_t *t = apr_palloc(p, sizeof(apr_table_t)); |
407 | | |
408 | 4.66k | make_array_core(&t->a, p, nelts, sizeof(apr_table_entry_t), 0); |
409 | | #ifdef MAKE_TABLE_PROFILE |
410 | | t->creator = __builtin_return_address(0); |
411 | | #endif |
412 | 4.66k | t->index_initialized = 0; |
413 | 4.66k | return t; |
414 | 4.66k | } |
415 | | |
416 | | APR_DECLARE(apr_table_t *) apr_table_copy(apr_pool_t *p, const apr_table_t *t) |
417 | 0 | { |
418 | 0 | apr_table_t *new = apr_palloc(p, sizeof(apr_table_t)); |
419 | |
|
420 | | #if APR_TABLE_POOL_DEBUG |
421 | | /* we don't copy keys and values, so it's necessary that t->a.pool |
422 | | * have a life span at least as long as p |
423 | | */ |
424 | | if (!apr_pool_is_ancestor(t->a.pool, p)) { |
425 | | fprintf(stderr, "apr_table_copy: t's pool is not an ancestor of p\n"); |
426 | | abort(); |
427 | | } |
428 | | #endif |
429 | 0 | make_array_core(&new->a, p, t->a.nalloc, sizeof(apr_table_entry_t), 0); |
430 | 0 | memcpy(new->a.elts, t->a.elts, t->a.nelts * sizeof(apr_table_entry_t)); |
431 | 0 | new->a.nelts = t->a.nelts; |
432 | 0 | memcpy(new->index_first, t->index_first, sizeof(int) * TABLE_HASH_SIZE); |
433 | 0 | memcpy(new->index_last, t->index_last, sizeof(int) * TABLE_HASH_SIZE); |
434 | 0 | new->index_initialized = t->index_initialized; |
435 | 0 | return new; |
436 | 0 | } |
437 | | |
438 | | APR_DECLARE(apr_table_t *) apr_table_clone(apr_pool_t *p, const apr_table_t *t) |
439 | 0 | { |
440 | 0 | const apr_array_header_t *array = apr_table_elts(t); |
441 | 0 | apr_table_entry_t *elts = (apr_table_entry_t *) array->elts; |
442 | 0 | apr_table_t *new = apr_table_make(p, array->nelts); |
443 | 0 | int i; |
444 | |
|
445 | 0 | for (i = 0; i < array->nelts; i++) { |
446 | 0 | apr_table_add(new, elts[i].key, elts[i].val); |
447 | 0 | } |
448 | |
|
449 | 0 | return new; |
450 | 0 | } |
451 | | |
452 | | static void table_reindex(apr_table_t *t) |
453 | 0 | { |
454 | 0 | int i; |
455 | 0 | int hash; |
456 | 0 | apr_table_entry_t *next_elt = (apr_table_entry_t *) t->a.elts; |
457 | |
|
458 | 0 | t->index_initialized = 0; |
459 | 0 | for (i = 0; i < t->a.nelts; i++, next_elt++) { |
460 | 0 | hash = TABLE_HASH(next_elt->key); |
461 | 0 | t->index_last[hash] = i; |
462 | 0 | if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) { |
463 | 0 | t->index_first[hash] = i; |
464 | 0 | TABLE_SET_INDEX_INITIALIZED(t, hash); |
465 | 0 | } |
466 | 0 | } |
467 | 0 | } |
468 | | |
469 | | APR_DECLARE(void) apr_table_clear(apr_table_t *t) |
470 | 0 | { |
471 | 0 | t->a.nelts = 0; |
472 | 0 | t->index_initialized = 0; |
473 | 0 | } |
474 | | |
475 | | APR_DECLARE(const char *) apr_table_get(const apr_table_t *t, const char *key) |
476 | 2.03k | { |
477 | 2.03k | apr_table_entry_t *next_elt; |
478 | 2.03k | apr_table_entry_t *end_elt; |
479 | 2.03k | apr_uint32_t checksum; |
480 | 2.03k | int hash; |
481 | | |
482 | 2.03k | if (key == NULL) { |
483 | 0 | return NULL; |
484 | 0 | } |
485 | | |
486 | 2.03k | hash = TABLE_HASH(key); |
487 | 2.03k | if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) { |
488 | 504 | return NULL; |
489 | 504 | } |
490 | 1.53k | COMPUTE_KEY_CHECKSUM(key, checksum); |
491 | 1.53k | next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash];; |
492 | 1.53k | end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash]; |
493 | | |
494 | 3.29k | for (; next_elt <= end_elt; next_elt++) { |
495 | 2.15k | if ((checksum == next_elt->key_checksum) && |
496 | 2.15k | !strcasecmp(next_elt->key, key)) { |
497 | 397 | return next_elt->val; |
498 | 397 | } |
499 | 2.15k | } |
500 | | |
501 | 1.13k | return NULL; |
502 | 1.53k | } |
503 | | |
504 | | APR_DECLARE(void) apr_table_set(apr_table_t *t, const char *key, |
505 | | const char *val) |
506 | 0 | { |
507 | 0 | apr_table_entry_t *next_elt; |
508 | 0 | apr_table_entry_t *end_elt; |
509 | 0 | apr_table_entry_t *table_end; |
510 | 0 | apr_uint32_t checksum; |
511 | 0 | int hash; |
512 | |
|
513 | 0 | COMPUTE_KEY_CHECKSUM(key, checksum); |
514 | 0 | hash = TABLE_HASH(key); |
515 | 0 | if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) { |
516 | 0 | t->index_first[hash] = t->a.nelts; |
517 | 0 | TABLE_SET_INDEX_INITIALIZED(t, hash); |
518 | 0 | goto add_new_elt; |
519 | 0 | } |
520 | 0 | next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash];; |
521 | 0 | end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash]; |
522 | 0 | table_end =((apr_table_entry_t *) t->a.elts) + t->a.nelts; |
523 | |
|
524 | 0 | for (; next_elt <= end_elt; next_elt++) { |
525 | 0 | if ((checksum == next_elt->key_checksum) && |
526 | 0 | !strcasecmp(next_elt->key, key)) { |
527 | | |
528 | | /* Found an existing entry with the same key, so overwrite it */ |
529 | |
|
530 | 0 | int must_reindex = 0; |
531 | 0 | apr_table_entry_t *dst_elt = NULL; |
532 | |
|
533 | 0 | next_elt->val = apr_pstrdup(t->a.pool, val); |
534 | | |
535 | | /* Remove any other instances of this key */ |
536 | 0 | for (next_elt++; next_elt <= end_elt; next_elt++) { |
537 | 0 | if ((checksum == next_elt->key_checksum) && |
538 | 0 | !strcasecmp(next_elt->key, key)) { |
539 | 0 | t->a.nelts--; |
540 | 0 | if (!dst_elt) { |
541 | 0 | dst_elt = next_elt; |
542 | 0 | } |
543 | 0 | } |
544 | 0 | else if (dst_elt) { |
545 | 0 | *dst_elt++ = *next_elt; |
546 | 0 | must_reindex = 1; |
547 | 0 | } |
548 | 0 | } |
549 | | |
550 | | /* If we've removed anything, shift over the remainder |
551 | | * of the table (note that the previous loop didn't |
552 | | * run to the end of the table, just to the last match |
553 | | * for the index) |
554 | | */ |
555 | 0 | if (dst_elt) { |
556 | 0 | for (; next_elt < table_end; next_elt++) { |
557 | 0 | *dst_elt++ = *next_elt; |
558 | 0 | } |
559 | 0 | must_reindex = 1; |
560 | 0 | } |
561 | 0 | if (must_reindex) { |
562 | 0 | table_reindex(t); |
563 | 0 | } |
564 | 0 | return; |
565 | 0 | } |
566 | 0 | } |
567 | | |
568 | 0 | add_new_elt: |
569 | 0 | t->index_last[hash] = t->a.nelts; |
570 | 0 | next_elt = (apr_table_entry_t *) table_push(t); |
571 | 0 | next_elt->key = apr_pstrdup(t->a.pool, key); |
572 | 0 | next_elt->val = apr_pstrdup(t->a.pool, val); |
573 | 0 | next_elt->key_checksum = checksum; |
574 | 0 | } |
575 | | |
576 | | APR_DECLARE(void) apr_table_setn(apr_table_t *t, const char *key, |
577 | | const char *val) |
578 | 0 | { |
579 | 0 | apr_table_entry_t *next_elt; |
580 | 0 | apr_table_entry_t *end_elt; |
581 | 0 | apr_table_entry_t *table_end; |
582 | 0 | apr_uint32_t checksum; |
583 | 0 | int hash; |
584 | |
|
585 | 0 | COMPUTE_KEY_CHECKSUM(key, checksum); |
586 | 0 | hash = TABLE_HASH(key); |
587 | 0 | if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) { |
588 | 0 | t->index_first[hash] = t->a.nelts; |
589 | 0 | TABLE_SET_INDEX_INITIALIZED(t, hash); |
590 | 0 | goto add_new_elt; |
591 | 0 | } |
592 | 0 | next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash];; |
593 | 0 | end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash]; |
594 | 0 | table_end =((apr_table_entry_t *) t->a.elts) + t->a.nelts; |
595 | |
|
596 | 0 | for (; next_elt <= end_elt; next_elt++) { |
597 | 0 | if ((checksum == next_elt->key_checksum) && |
598 | 0 | !strcasecmp(next_elt->key, key)) { |
599 | | |
600 | | /* Found an existing entry with the same key, so overwrite it */ |
601 | |
|
602 | 0 | int must_reindex = 0; |
603 | 0 | apr_table_entry_t *dst_elt = NULL; |
604 | |
|
605 | 0 | next_elt->val = (char *)val; |
606 | | |
607 | | /* Remove any other instances of this key */ |
608 | 0 | for (next_elt++; next_elt <= end_elt; next_elt++) { |
609 | 0 | if ((checksum == next_elt->key_checksum) && |
610 | 0 | !strcasecmp(next_elt->key, key)) { |
611 | 0 | t->a.nelts--; |
612 | 0 | if (!dst_elt) { |
613 | 0 | dst_elt = next_elt; |
614 | 0 | } |
615 | 0 | } |
616 | 0 | else if (dst_elt) { |
617 | 0 | *dst_elt++ = *next_elt; |
618 | 0 | must_reindex = 1; |
619 | 0 | } |
620 | 0 | } |
621 | | |
622 | | /* If we've removed anything, shift over the remainder |
623 | | * of the table (note that the previous loop didn't |
624 | | * run to the end of the table, just to the last match |
625 | | * for the index) |
626 | | */ |
627 | 0 | if (dst_elt) { |
628 | 0 | for (; next_elt < table_end; next_elt++) { |
629 | 0 | *dst_elt++ = *next_elt; |
630 | 0 | } |
631 | 0 | must_reindex = 1; |
632 | 0 | } |
633 | 0 | if (must_reindex) { |
634 | 0 | table_reindex(t); |
635 | 0 | } |
636 | 0 | return; |
637 | 0 | } |
638 | 0 | } |
639 | | |
640 | 0 | add_new_elt: |
641 | 0 | t->index_last[hash] = t->a.nelts; |
642 | 0 | next_elt = (apr_table_entry_t *) table_push(t); |
643 | 0 | next_elt->key = (char *)key; |
644 | 0 | next_elt->val = (char *)val; |
645 | 0 | next_elt->key_checksum = checksum; |
646 | 0 | } |
647 | | |
648 | | APR_DECLARE(void) apr_table_unset(apr_table_t *t, const char *key) |
649 | 0 | { |
650 | 0 | apr_table_entry_t *next_elt; |
651 | 0 | apr_table_entry_t *end_elt; |
652 | 0 | apr_table_entry_t *dst_elt; |
653 | 0 | apr_uint32_t checksum; |
654 | 0 | int hash; |
655 | 0 | int must_reindex; |
656 | |
|
657 | 0 | hash = TABLE_HASH(key); |
658 | 0 | if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) { |
659 | 0 | return; |
660 | 0 | } |
661 | 0 | COMPUTE_KEY_CHECKSUM(key, checksum); |
662 | 0 | next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash]; |
663 | 0 | end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash]; |
664 | 0 | must_reindex = 0; |
665 | 0 | for (; next_elt <= end_elt; next_elt++) { |
666 | 0 | if ((checksum == next_elt->key_checksum) && |
667 | 0 | !strcasecmp(next_elt->key, key)) { |
668 | | |
669 | | /* Found a match: remove this entry, plus any additional |
670 | | * matches for the same key that might follow |
671 | | */ |
672 | 0 | apr_table_entry_t *table_end = ((apr_table_entry_t *) t->a.elts) + |
673 | 0 | t->a.nelts; |
674 | 0 | t->a.nelts--; |
675 | 0 | dst_elt = next_elt; |
676 | 0 | for (next_elt++; next_elt <= end_elt; next_elt++) { |
677 | 0 | if ((checksum == next_elt->key_checksum) && |
678 | 0 | !strcasecmp(next_elt->key, key)) { |
679 | 0 | t->a.nelts--; |
680 | 0 | } |
681 | 0 | else { |
682 | 0 | *dst_elt++ = *next_elt; |
683 | 0 | } |
684 | 0 | } |
685 | | |
686 | | /* Shift over the remainder of the table (note that |
687 | | * the previous loop didn't run to the end of the table, |
688 | | * just to the last match for the index) |
689 | | */ |
690 | 0 | for (; next_elt < table_end; next_elt++) { |
691 | 0 | *dst_elt++ = *next_elt; |
692 | 0 | } |
693 | 0 | must_reindex = 1; |
694 | 0 | break; |
695 | 0 | } |
696 | 0 | } |
697 | 0 | if (must_reindex) { |
698 | 0 | table_reindex(t); |
699 | 0 | } |
700 | 0 | } |
701 | | |
702 | | APR_DECLARE(void) apr_table_merge(apr_table_t *t, const char *key, |
703 | | const char *val) |
704 | 0 | { |
705 | 0 | apr_table_entry_t *next_elt; |
706 | 0 | apr_table_entry_t *end_elt; |
707 | 0 | apr_uint32_t checksum; |
708 | 0 | int hash; |
709 | |
|
710 | 0 | COMPUTE_KEY_CHECKSUM(key, checksum); |
711 | 0 | hash = TABLE_HASH(key); |
712 | 0 | if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) { |
713 | 0 | t->index_first[hash] = t->a.nelts; |
714 | 0 | TABLE_SET_INDEX_INITIALIZED(t, hash); |
715 | 0 | goto add_new_elt; |
716 | 0 | } |
717 | 0 | next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash]; |
718 | 0 | end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash]; |
719 | |
|
720 | 0 | for (; next_elt <= end_elt; next_elt++) { |
721 | 0 | if ((checksum == next_elt->key_checksum) && |
722 | 0 | !strcasecmp(next_elt->key, key)) { |
723 | | |
724 | | /* Found an existing entry with the same key, so merge with it */ |
725 | 0 | next_elt->val = apr_pstrcat(t->a.pool, next_elt->val, ", ", |
726 | 0 | val, NULL); |
727 | 0 | return; |
728 | 0 | } |
729 | 0 | } |
730 | | |
731 | 0 | add_new_elt: |
732 | 0 | t->index_last[hash] = t->a.nelts; |
733 | 0 | next_elt = (apr_table_entry_t *) table_push(t); |
734 | 0 | next_elt->key = apr_pstrdup(t->a.pool, key); |
735 | 0 | next_elt->val = apr_pstrdup(t->a.pool, val); |
736 | 0 | next_elt->key_checksum = checksum; |
737 | 0 | } |
738 | | |
739 | | APR_DECLARE(void) apr_table_mergen(apr_table_t *t, const char *key, |
740 | | const char *val) |
741 | 0 | { |
742 | 0 | apr_table_entry_t *next_elt; |
743 | 0 | apr_table_entry_t *end_elt; |
744 | 0 | apr_uint32_t checksum; |
745 | 0 | int hash; |
746 | |
|
747 | | #if APR_TABLE_POOL_DEBUG |
748 | | { |
749 | | apr_pool_t *pool; |
750 | | pool = apr_pool_find(key); |
751 | | if ((pool != (apr_pool_t *)key) |
752 | | && (!apr_pool_is_ancestor(pool, t->a.pool))) { |
753 | | fprintf(stderr, "apr_table_mergen: key not in ancestor pool of t\n"); |
754 | | abort(); |
755 | | } |
756 | | pool = apr_pool_find(val); |
757 | | if ((pool != (apr_pool_t *)val) |
758 | | && (!apr_pool_is_ancestor(pool, t->a.pool))) { |
759 | | fprintf(stderr, "apr_table_mergen: val not in ancestor pool of t\n"); |
760 | | abort(); |
761 | | } |
762 | | } |
763 | | #endif |
764 | |
|
765 | 0 | COMPUTE_KEY_CHECKSUM(key, checksum); |
766 | 0 | hash = TABLE_HASH(key); |
767 | 0 | if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) { |
768 | 0 | t->index_first[hash] = t->a.nelts; |
769 | 0 | TABLE_SET_INDEX_INITIALIZED(t, hash); |
770 | 0 | goto add_new_elt; |
771 | 0 | } |
772 | 0 | next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash];; |
773 | 0 | end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash]; |
774 | |
|
775 | 0 | for (; next_elt <= end_elt; next_elt++) { |
776 | 0 | if ((checksum == next_elt->key_checksum) && |
777 | 0 | !strcasecmp(next_elt->key, key)) { |
778 | | |
779 | | /* Found an existing entry with the same key, so merge with it */ |
780 | 0 | next_elt->val = apr_pstrcat(t->a.pool, next_elt->val, ", ", |
781 | 0 | val, NULL); |
782 | 0 | return; |
783 | 0 | } |
784 | 0 | } |
785 | | |
786 | 0 | add_new_elt: |
787 | 0 | t->index_last[hash] = t->a.nelts; |
788 | 0 | next_elt = (apr_table_entry_t *) table_push(t); |
789 | 0 | next_elt->key = (char *)key; |
790 | 0 | next_elt->val = (char *)val; |
791 | 0 | next_elt->key_checksum = checksum; |
792 | 0 | } |
793 | | |
794 | | APR_DECLARE(void) apr_table_add(apr_table_t *t, const char *key, |
795 | | const char *val) |
796 | 0 | { |
797 | 0 | apr_table_entry_t *elts; |
798 | 0 | apr_uint32_t checksum; |
799 | 0 | int hash; |
800 | |
|
801 | 0 | hash = TABLE_HASH(key); |
802 | 0 | t->index_last[hash] = t->a.nelts; |
803 | 0 | if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) { |
804 | 0 | t->index_first[hash] = t->a.nelts; |
805 | 0 | TABLE_SET_INDEX_INITIALIZED(t, hash); |
806 | 0 | } |
807 | 0 | COMPUTE_KEY_CHECKSUM(key, checksum); |
808 | 0 | elts = (apr_table_entry_t *) table_push(t); |
809 | 0 | elts->key = apr_pstrdup(t->a.pool, key); |
810 | 0 | elts->val = apr_pstrdup(t->a.pool, val); |
811 | 0 | elts->key_checksum = checksum; |
812 | 0 | } |
813 | | |
814 | | APR_DECLARE(void) apr_table_addn(apr_table_t *t, const char *key, |
815 | | const char *val) |
816 | 1.93k | { |
817 | 1.93k | apr_table_entry_t *elts; |
818 | 1.93k | apr_uint32_t checksum; |
819 | 1.93k | int hash; |
820 | | |
821 | | #if APR_TABLE_POOL_DEBUG |
822 | | { |
823 | | if (!apr_pool_is_ancestor(apr_pool_find(key), t->a.pool)) { |
824 | | fprintf(stderr, "apr_table_addn: key not in ancestor pool of t\n"); |
825 | | abort(); |
826 | | } |
827 | | if (!apr_pool_is_ancestor(apr_pool_find(val), t->a.pool)) { |
828 | | fprintf(stderr, "apr_table_addn: val not in ancestor pool of t\n"); |
829 | | abort(); |
830 | | } |
831 | | } |
832 | | #endif |
833 | | |
834 | 1.93k | hash = TABLE_HASH(key); |
835 | 1.93k | t->index_last[hash] = t->a.nelts; |
836 | 1.93k | if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) { |
837 | 1.29k | t->index_first[hash] = t->a.nelts; |
838 | 1.29k | TABLE_SET_INDEX_INITIALIZED(t, hash); |
839 | 1.29k | } |
840 | 1.93k | COMPUTE_KEY_CHECKSUM(key, checksum); |
841 | 1.93k | elts = (apr_table_entry_t *) table_push(t); |
842 | 1.93k | elts->key = (char *)key; |
843 | 1.93k | elts->val = (char *)val; |
844 | 1.93k | elts->key_checksum = checksum; |
845 | 1.93k | } |
846 | | |
847 | | APR_DECLARE(apr_table_t *) apr_table_overlay(apr_pool_t *p, |
848 | | const apr_table_t *overlay, |
849 | | const apr_table_t *base) |
850 | 0 | { |
851 | 0 | apr_table_t *res; |
852 | |
|
853 | | #if APR_TABLE_POOL_DEBUG |
854 | | /* we don't copy keys and values, so it's necessary that |
855 | | * overlay->a.pool and base->a.pool have a life span at least |
856 | | * as long as p |
857 | | */ |
858 | | if (!apr_pool_is_ancestor(overlay->a.pool, p)) { |
859 | | fprintf(stderr, |
860 | | "apr_table_overlay: overlay's pool is not an ancestor of p\n"); |
861 | | abort(); |
862 | | } |
863 | | if (!apr_pool_is_ancestor(base->a.pool, p)) { |
864 | | fprintf(stderr, |
865 | | "apr_table_overlay: base's pool is not an ancestor of p\n"); |
866 | | abort(); |
867 | | } |
868 | | #endif |
869 | |
|
870 | 0 | res = apr_palloc(p, sizeof(apr_table_t)); |
871 | | /* behave like append_arrays */ |
872 | 0 | res->a.pool = p; |
873 | 0 | copy_array_hdr_core(&res->a, &overlay->a); |
874 | 0 | apr_array_cat(&res->a, &base->a); |
875 | 0 | table_reindex(res); |
876 | 0 | return res; |
877 | 0 | } |
878 | | |
879 | | /* And now for something completely abstract ... |
880 | | |
881 | | * For each key value given as a vararg: |
882 | | * run the function pointed to as |
883 | | * int comp(void *r, char *key, char *value); |
884 | | * on each valid key-value pair in the apr_table_t t that matches the vararg key, |
885 | | * or once for every valid key-value pair if the vararg list is empty, |
886 | | * until the function returns false (0) or we finish the table. |
887 | | * |
888 | | * Note that we restart the traversal for each vararg, which means that |
889 | | * duplicate varargs will result in multiple executions of the function |
890 | | * for each matching key. Note also that if the vararg list is empty, |
891 | | * only one traversal will be made and will cut short if comp returns 0. |
892 | | * |
893 | | * Note that the table_get and table_merge functions assume that each key in |
894 | | * the apr_table_t is unique (i.e., no multiple entries with the same key). This |
895 | | * function does not make that assumption, since it (unfortunately) isn't |
896 | | * true for some of Apache's tables. |
897 | | * |
898 | | * Note that rec is simply passed-on to the comp function, so that the |
899 | | * caller can pass additional info for the task. |
900 | | * |
901 | | * ADDENDUM for apr_table_vdo(): |
902 | | * |
903 | | * The caching api will allow a user to walk the header values: |
904 | | * |
905 | | * apr_status_t apr_cache_el_header_walk(apr_cache_el *el, |
906 | | * int (*comp)(void *, const char *, const char *), void *rec, ...); |
907 | | * |
908 | | * So it can be ..., however from there I use a callback that use a va_list: |
909 | | * |
910 | | * apr_status_t (*cache_el_header_walk)(apr_cache_el *el, |
911 | | * int (*comp)(void *, const char *, const char *), void *rec, va_list); |
912 | | * |
913 | | * To pass those ...'s on down to the actual module that will handle walking |
914 | | * their headers, in the file case this is actually just an apr_table - and |
915 | | * rather than reimplementing apr_table_do (which IMHO would be bad) I just |
916 | | * called it with the va_list. For mod_shmem_cache I don't need it since I |
917 | | * can't use apr_table's, but mod_file_cache should (though a good hash would |
918 | | * be better, but that's a different issue :). |
919 | | * |
920 | | * So to make mod_file_cache easier to maintain, it's a good thing |
921 | | */ |
922 | | APR_DECLARE_NONSTD(int) apr_table_do(apr_table_do_callback_fn_t *comp, |
923 | | void *rec, const apr_table_t *t, ...) |
924 | 0 | { |
925 | 0 | int rv; |
926 | |
|
927 | 0 | va_list vp; |
928 | 0 | va_start(vp, t); |
929 | 0 | rv = apr_table_vdo(comp, rec, t, vp); |
930 | 0 | va_end(vp); |
931 | |
|
932 | 0 | return rv; |
933 | 0 | } |
934 | | |
935 | | /* XXX: do the semantics of this routine make any sense? Right now, |
936 | | * if the caller passed in a non-empty va_list of keys to search for, |
937 | | * the "early termination" facility only terminates on *that* key; other |
938 | | * keys will continue to process. Note that this only has any effect |
939 | | * at all if there are multiple entries in the table with the same key, |
940 | | * otherwise the called function can never effectively early-terminate |
941 | | * this function, as the zero return value is effectively ignored. |
942 | | * |
943 | | * Note also that this behavior is at odds with the behavior seen if an |
944 | | * empty va_list is passed in -- in that case, a zero return value terminates |
945 | | * the entire apr_table_vdo (which is what I think should happen in |
946 | | * both cases). |
947 | | * |
948 | | * If nobody objects soon, I'm going to change the order of the nested |
949 | | * loops in this function so that any zero return value from the (*comp) |
950 | | * function will cause a full termination of apr_table_vdo. I'm hesitant |
951 | | * at the moment because these (funky) semantics have been around for a |
952 | | * very long time, and although Apache doesn't seem to use them at all, |
953 | | * some third-party vendor might. I can only think of one possible reason |
954 | | * the existing semantics would make any sense, and it's very Apache-centric, |
955 | | * which is this: if (*comp) is looking for matches of a particular |
956 | | * substring in request headers (let's say it's looking for a particular |
957 | | * cookie name in the Set-Cookie headers), then maybe it wants to be |
958 | | * able to stop searching early as soon as it finds that one and move |
959 | | * on to the next key. That's only an optimization of course, but changing |
960 | | * the behavior of this function would mean that any code that tried |
961 | | * to do that would stop working right. |
962 | | * |
963 | | * Sigh. --JCW, 06/28/02 |
964 | | */ |
965 | | APR_DECLARE(int) apr_table_vdo(apr_table_do_callback_fn_t *comp, |
966 | | void *rec, const apr_table_t *t, va_list vp) |
967 | 0 | { |
968 | 0 | char *argp; |
969 | 0 | apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts; |
970 | 0 | int vdorv = 1; |
971 | |
|
972 | 0 | argp = va_arg(vp, char *); |
973 | 0 | do { |
974 | 0 | int rv = 1, i; |
975 | 0 | if (argp) { |
976 | | /* Scan for entries that match the next key */ |
977 | 0 | int hash = TABLE_HASH(argp); |
978 | 0 | if (TABLE_INDEX_IS_INITIALIZED(t, hash)) { |
979 | 0 | apr_uint32_t checksum; |
980 | 0 | COMPUTE_KEY_CHECKSUM(argp, checksum); |
981 | 0 | for (i = t->index_first[hash]; |
982 | 0 | rv && (i <= t->index_last[hash]); ++i) { |
983 | 0 | if (elts[i].key && (checksum == elts[i].key_checksum) && |
984 | 0 | !strcasecmp(elts[i].key, argp)) { |
985 | 0 | rv = (*comp) (rec, elts[i].key, elts[i].val); |
986 | 0 | } |
987 | 0 | } |
988 | 0 | } |
989 | 0 | } |
990 | 0 | else { |
991 | | /* Scan the entire table */ |
992 | 0 | for (i = 0; rv && (i < t->a.nelts); ++i) { |
993 | 0 | if (elts[i].key) { |
994 | 0 | rv = (*comp) (rec, elts[i].key, elts[i].val); |
995 | 0 | } |
996 | 0 | } |
997 | 0 | } |
998 | 0 | if (rv == 0) { |
999 | 0 | vdorv = 0; |
1000 | 0 | } |
1001 | 0 | } while (argp && ((argp = va_arg(vp, char *)) != NULL)); |
1002 | |
|
1003 | 0 | return vdorv; |
1004 | 0 | } |
1005 | | |
1006 | | static apr_table_entry_t **table_mergesort(apr_pool_t *pool, |
1007 | | apr_table_entry_t **values, |
1008 | | apr_size_t n) |
1009 | 0 | { |
1010 | | /* Bottom-up mergesort, based on design in Sedgewick's "Algorithms |
1011 | | * in C," chapter 8 |
1012 | | */ |
1013 | 0 | apr_table_entry_t **values_tmp = |
1014 | 0 | (apr_table_entry_t **)apr_palloc(pool, n * sizeof(apr_table_entry_t*)); |
1015 | 0 | apr_size_t i; |
1016 | 0 | apr_size_t blocksize; |
1017 | | |
1018 | | /* First pass: sort pairs of elements (blocksize=1) */ |
1019 | 0 | for (i = 0; i + 1 < n; i += 2) { |
1020 | 0 | if (strcasecmp(values[i]->key, values[i + 1]->key) > 0) { |
1021 | 0 | apr_table_entry_t *swap = values[i]; |
1022 | 0 | values[i] = values[i + 1]; |
1023 | 0 | values[i + 1] = swap; |
1024 | 0 | } |
1025 | 0 | } |
1026 | | |
1027 | | /* Merge successively larger blocks */ |
1028 | 0 | blocksize = 2; |
1029 | 0 | while (blocksize < n) { |
1030 | 0 | apr_table_entry_t **dst = values_tmp; |
1031 | 0 | apr_size_t next_start; |
1032 | 0 | apr_table_entry_t **swap; |
1033 | | |
1034 | | /* Merge consecutive pairs blocks of the next blocksize. |
1035 | | * Within a block, elements are in sorted order due to |
1036 | | * the previous iteration. |
1037 | | */ |
1038 | 0 | for (next_start = 0; next_start + blocksize < n; |
1039 | 0 | next_start += (blocksize + blocksize)) { |
1040 | |
|
1041 | 0 | apr_size_t block1_start = next_start; |
1042 | 0 | apr_size_t block2_start = block1_start + blocksize; |
1043 | 0 | apr_size_t block1_end = block2_start; |
1044 | 0 | apr_size_t block2_end = block2_start + blocksize; |
1045 | 0 | if (block2_end > n) { |
1046 | | /* The last block may be smaller than blocksize */ |
1047 | 0 | block2_end = n; |
1048 | 0 | } |
1049 | 0 | for (;;) { |
1050 | | |
1051 | | /* Merge the next two blocks: |
1052 | | * Pick the smaller of the next element from |
1053 | | * block 1 and the next element from block 2. |
1054 | | * Once either of the blocks is emptied, copy |
1055 | | * over all the remaining elements from the |
1056 | | * other block |
1057 | | */ |
1058 | 0 | if (block1_start == block1_end) { |
1059 | 0 | for (; block2_start < block2_end; block2_start++) { |
1060 | 0 | *dst++ = values[block2_start]; |
1061 | 0 | } |
1062 | 0 | break; |
1063 | 0 | } |
1064 | 0 | else if (block2_start == block2_end) { |
1065 | 0 | for (; block1_start < block1_end; block1_start++) { |
1066 | 0 | *dst++ = values[block1_start]; |
1067 | 0 | } |
1068 | 0 | break; |
1069 | 0 | } |
1070 | 0 | if (strcasecmp(values[block1_start]->key, |
1071 | 0 | values[block2_start]->key) > 0) { |
1072 | 0 | *dst++ = values[block2_start++]; |
1073 | 0 | } |
1074 | 0 | else { |
1075 | 0 | *dst++ = values[block1_start++]; |
1076 | 0 | } |
1077 | 0 | } |
1078 | 0 | } |
1079 | | |
1080 | | /* If n is not a multiple of 2*blocksize, some elements |
1081 | | * will be left over at the end of the array. |
1082 | | */ |
1083 | 0 | for (i = dst - values_tmp; i < n; i++) { |
1084 | 0 | values_tmp[i] = values[i]; |
1085 | 0 | } |
1086 | | |
1087 | | /* The output array of this pass becomes the input |
1088 | | * array of the next pass, and vice versa |
1089 | | */ |
1090 | 0 | swap = values_tmp; |
1091 | 0 | values_tmp = values; |
1092 | 0 | values = swap; |
1093 | |
|
1094 | 0 | blocksize += blocksize; |
1095 | 0 | } |
1096 | |
|
1097 | 0 | return values; |
1098 | 0 | } |
1099 | | |
1100 | | APR_DECLARE(void) apr_table_compress(apr_table_t *t, unsigned flags) |
1101 | 0 | { |
1102 | 0 | apr_table_entry_t **sort_array; |
1103 | 0 | apr_table_entry_t **sort_next; |
1104 | 0 | apr_table_entry_t **sort_end; |
1105 | 0 | apr_table_entry_t *table_next; |
1106 | 0 | apr_table_entry_t **last; |
1107 | 0 | int i; |
1108 | 0 | int dups_found; |
1109 | |
|
1110 | 0 | if (flags == APR_OVERLAP_TABLES_ADD) { |
1111 | 0 | return; |
1112 | 0 | } |
1113 | | |
1114 | 0 | if (t->a.nelts <= 1) { |
1115 | 0 | return; |
1116 | 0 | } |
1117 | | |
1118 | | /* Copy pointers to all the table elements into an |
1119 | | * array and sort to allow for easy detection of |
1120 | | * duplicate keys |
1121 | | */ |
1122 | 0 | sort_array = (apr_table_entry_t **) |
1123 | 0 | apr_palloc(t->a.pool, t->a.nelts * sizeof(apr_table_entry_t*)); |
1124 | 0 | sort_next = sort_array; |
1125 | 0 | table_next = (apr_table_entry_t *)t->a.elts; |
1126 | 0 | i = t->a.nelts; |
1127 | 0 | do { |
1128 | 0 | *sort_next++ = table_next++; |
1129 | 0 | } while (--i); |
1130 | | |
1131 | | /* Note: the merge is done with mergesort instead of quicksort |
1132 | | * because mergesort is a stable sort and runs in n*log(n) |
1133 | | * time regardless of its inputs (quicksort is quadratic in |
1134 | | * the worst case) |
1135 | | */ |
1136 | 0 | sort_array = table_mergesort(t->a.pool, sort_array, t->a.nelts); |
1137 | | |
1138 | | /* Process any duplicate keys */ |
1139 | 0 | dups_found = 0; |
1140 | 0 | sort_next = sort_array; |
1141 | 0 | sort_end = sort_array + t->a.nelts; |
1142 | 0 | last = sort_next++; |
1143 | 0 | while (sort_next < sort_end) { |
1144 | 0 | if (((*sort_next)->key_checksum == (*last)->key_checksum) && |
1145 | 0 | !strcasecmp((*sort_next)->key, (*last)->key)) { |
1146 | 0 | apr_table_entry_t **dup_last = sort_next + 1; |
1147 | 0 | dups_found = 1; |
1148 | 0 | while ((dup_last < sort_end) && |
1149 | 0 | ((*dup_last)->key_checksum == (*last)->key_checksum) && |
1150 | 0 | !strcasecmp((*dup_last)->key, (*last)->key)) { |
1151 | 0 | dup_last++; |
1152 | 0 | } |
1153 | 0 | dup_last--; /* Elements from last through dup_last, inclusive, |
1154 | | * all have the same key |
1155 | | */ |
1156 | 0 | if (flags == APR_OVERLAP_TABLES_MERGE) { |
1157 | 0 | apr_size_t len = 0; |
1158 | 0 | apr_table_entry_t **next = last; |
1159 | 0 | char *new_val; |
1160 | 0 | char *val_dst; |
1161 | 0 | do { |
1162 | 0 | len += strlen((*next)->val); |
1163 | 0 | len += 2; /* for ", " or trailing null */ |
1164 | 0 | } while (++next <= dup_last); |
1165 | 0 | new_val = (char *)apr_palloc(t->a.pool, len); |
1166 | 0 | val_dst = new_val; |
1167 | 0 | next = last; |
1168 | 0 | for (;;) { |
1169 | 0 | strcpy(val_dst, (*next)->val); |
1170 | 0 | val_dst += strlen((*next)->val); |
1171 | 0 | next++; |
1172 | 0 | if (next > dup_last) { |
1173 | 0 | *val_dst = 0; |
1174 | 0 | break; |
1175 | 0 | } |
1176 | 0 | else { |
1177 | 0 | *val_dst++ = ','; |
1178 | 0 | *val_dst++ = ' '; |
1179 | 0 | } |
1180 | 0 | } |
1181 | 0 | (*last)->val = new_val; |
1182 | 0 | } |
1183 | 0 | else { /* overwrite */ |
1184 | 0 | (*last)->val = (*dup_last)->val; |
1185 | 0 | } |
1186 | 0 | do { |
1187 | 0 | (*sort_next)->key = NULL; |
1188 | 0 | } while (++sort_next <= dup_last); |
1189 | 0 | } |
1190 | 0 | else { |
1191 | 0 | last = sort_next++; |
1192 | 0 | } |
1193 | 0 | } |
1194 | | |
1195 | | /* Shift elements to the left to fill holes left by removing duplicates */ |
1196 | 0 | if (dups_found) { |
1197 | 0 | apr_table_entry_t *src = (apr_table_entry_t *)t->a.elts; |
1198 | 0 | apr_table_entry_t *dst = (apr_table_entry_t *)t->a.elts; |
1199 | 0 | apr_table_entry_t *last_elt = src + t->a.nelts; |
1200 | 0 | do { |
1201 | 0 | if (src->key) { |
1202 | 0 | *dst++ = *src; |
1203 | 0 | } |
1204 | 0 | } while (++src < last_elt); |
1205 | 0 | t->a.nelts -= (int)(last_elt - dst); |
1206 | 0 | } |
1207 | |
|
1208 | 0 | table_reindex(t); |
1209 | 0 | } |
1210 | | |
1211 | | static void apr_table_cat(apr_table_t *t, const apr_table_t *s) |
1212 | 0 | { |
1213 | 0 | const int n = t->a.nelts; |
1214 | 0 | register int idx; |
1215 | |
|
1216 | 0 | apr_array_cat(&t->a,&s->a); |
1217 | |
|
1218 | 0 | if (n == 0) { |
1219 | 0 | memcpy(t->index_first,s->index_first,sizeof(int) * TABLE_HASH_SIZE); |
1220 | 0 | memcpy(t->index_last, s->index_last, sizeof(int) * TABLE_HASH_SIZE); |
1221 | 0 | t->index_initialized = s->index_initialized; |
1222 | 0 | return; |
1223 | 0 | } |
1224 | | |
1225 | 0 | for (idx = 0; idx < TABLE_HASH_SIZE; ++idx) { |
1226 | 0 | if (TABLE_INDEX_IS_INITIALIZED(s, idx)) { |
1227 | 0 | t->index_last[idx] = s->index_last[idx] + n; |
1228 | 0 | if (!TABLE_INDEX_IS_INITIALIZED(t, idx)) { |
1229 | 0 | t->index_first[idx] = s->index_first[idx] + n; |
1230 | 0 | } |
1231 | 0 | } |
1232 | 0 | } |
1233 | |
|
1234 | 0 | t->index_initialized |= s->index_initialized; |
1235 | 0 | } |
1236 | | |
1237 | | APR_DECLARE(void) apr_table_overlap(apr_table_t *a, const apr_table_t *b, |
1238 | | unsigned flags) |
1239 | 0 | { |
1240 | 0 | if (a->a.nelts + b->a.nelts == 0) { |
1241 | 0 | return; |
1242 | 0 | } |
1243 | | |
1244 | | #if APR_TABLE_POOL_DEBUG |
1245 | | /* Since the keys and values are not copied, it's required that |
1246 | | * b->a.pool has a lifetime at least as long as a->a.pool. */ |
1247 | | if (!apr_pool_is_ancestor(b->a.pool, a->a.pool)) { |
1248 | | fprintf(stderr, "apr_table_overlap: b's pool is not an ancestor of a's\n"); |
1249 | | abort(); |
1250 | | } |
1251 | | #endif |
1252 | | |
1253 | 0 | apr_table_cat(a, b); |
1254 | |
|
1255 | 0 | apr_table_compress(a, flags); |
1256 | 0 | } |
1257 | | |
1258 | | static int table_getm_do(void *v, const char *key, const char *val) |
1259 | 0 | { |
1260 | 0 | table_getm_t *state = (table_getm_t *) v; |
1261 | |
|
1262 | 0 | if (!state->first) { |
1263 | | /** |
1264 | | * The most common case is a single header, and this is covered by |
1265 | | * a fast path that doesn't allocate any memory. On the second and |
1266 | | * subsequent header, an array is created and the array concatenated |
1267 | | * together to form the final value. |
1268 | | */ |
1269 | 0 | state->first = val; |
1270 | 0 | } |
1271 | 0 | else { |
1272 | 0 | const char **elt; |
1273 | 0 | if (!state->merged) { |
1274 | 0 | state->merged = apr_array_make(state->p, 10, sizeof(const char *)); |
1275 | 0 | elt = apr_array_push(state->merged); |
1276 | 0 | *elt = state->first; |
1277 | 0 | } |
1278 | 0 | elt = apr_array_push(state->merged); |
1279 | 0 | *elt = val; |
1280 | 0 | } |
1281 | 0 | return 1; |
1282 | 0 | } |
1283 | | |
1284 | | APR_DECLARE(const char *) apr_table_getm(apr_pool_t *p, const apr_table_t *t, |
1285 | | const char *key) |
1286 | 0 | { |
1287 | 0 | table_getm_t state; |
1288 | |
|
1289 | 0 | state.p = p; |
1290 | 0 | state.first = NULL; |
1291 | 0 | state.merged = NULL; |
1292 | |
|
1293 | 0 | apr_table_do(table_getm_do, &state, t, key, NULL); |
1294 | |
|
1295 | 0 | if (!state.first) { |
1296 | 0 | return NULL; |
1297 | 0 | } |
1298 | 0 | else if (!state.merged) { |
1299 | 0 | return state.first; |
1300 | 0 | } |
1301 | 0 | else { |
1302 | 0 | return apr_array_pstrcat(p, state.merged, ','); |
1303 | 0 | } |
1304 | 0 | } |