1 #include "first.h"
2
3 #include "array.h"
4 #include "buffer.h"
5 #include "ck.h"
6
7 #include <string.h>
8 #include <stdlib.h>
9 #include <limits.h>
10
11
12 __attribute_cold__
array_data_string_copy(const data_unset * s)13 static data_unset *array_data_string_copy(const data_unset *s) {
14 data_string *src = (data_string *)s;
15 data_string *ds = array_data_string_init();
16 if (!buffer_is_unset(&src->key)) buffer_copy_buffer(&ds->key, &src->key);
17 buffer_copy_buffer(&ds->value, &src->value);
18 return (data_unset *)ds;
19 }
20
21 __attribute_cold__
array_data_string_insert_dup(data_unset * dst,data_unset * src)22 static void array_data_string_insert_dup(data_unset *dst, data_unset *src) {
23 data_string *ds_dst = (data_string *)dst;
24 data_string *ds_src = (data_string *)src;
25 if (!buffer_is_blank(&ds_dst->value))
26 buffer_append_str2(&ds_dst->value, CONST_STR_LEN(", "),
27 BUF_PTR_LEN(&ds_src->value));
28 else
29 buffer_copy_buffer(&ds_dst->value, &ds_src->value);
30 }
31
array_data_string_free(data_unset * du)32 static void array_data_string_free(data_unset *du) {
33 data_string *ds = (data_string *)du;
34 free(ds->key.ptr);
35 free(ds->value.ptr);
36 free(ds);
37 }
38
39 __attribute_noinline__
array_data_string_init(void)40 data_string *array_data_string_init(void) {
41 static const struct data_methods string_fn = {
42 array_data_string_copy,
43 array_data_string_free,
44 array_data_string_insert_dup,
45 };
46 data_string *ds = ck_calloc(1, sizeof(*ds));
47 ds->type = TYPE_STRING;
48 ds->fn = &string_fn;
49 return ds;
50 }
51
52
53 __attribute_cold__
array_data_integer_copy(const data_unset * s)54 static data_unset *array_data_integer_copy(const data_unset *s) {
55 data_integer *src = (data_integer *)s;
56 data_integer *di = array_data_integer_init();
57 if (!buffer_is_unset(&src->key)) buffer_copy_buffer(&di->key, &src->key);
58 di->value = src->value;
59 return (data_unset *)di;
60 }
61
array_data_integer_free(data_unset * du)62 static void array_data_integer_free(data_unset *du) {
63 data_integer *di = (data_integer *)du;
64 free(di->key.ptr);
65 free(di);
66 }
67
68 __attribute_noinline__
array_data_integer_init(void)69 data_integer *array_data_integer_init(void) {
70 static const struct data_methods integer_fn = {
71 array_data_integer_copy,
72 array_data_integer_free,
73 NULL
74 };
75 data_integer *di = ck_calloc(1, sizeof(*di));
76 di->type = TYPE_INTEGER;
77 di->fn = &integer_fn;
78 return di;
79 }
80
81
82 __attribute_cold__
array_data_array_copy(const data_unset * s)83 static data_unset *array_data_array_copy(const data_unset *s) {
84 data_array *src = (data_array *)s;
85 data_array *da = array_data_array_init();
86 if (!buffer_is_unset(&src->key)) buffer_copy_buffer(&da->key, &src->key);
87 array_copy_array(&da->value, &src->value);
88 return (data_unset *)da;
89 }
90
array_data_array_free(data_unset * du)91 static void array_data_array_free(data_unset *du) {
92 data_array *da = (data_array *)du;
93 free(da->key.ptr);
94 array_free_data(&da->value);
95 free(da);
96 }
97
98 __attribute_noinline__
array_data_array_init(void)99 data_array *array_data_array_init(void) {
100 static const struct data_methods array_fn = {
101 array_data_array_copy,
102 array_data_array_free,
103 NULL
104 };
105 data_array *da = ck_calloc(1, sizeof(*da));
106 da->type = TYPE_ARRAY;
107 da->fn = &array_fn;
108 return da;
109 }
110
111
112 __attribute_cold__
array_extend(array * const a,uint32_t n)113 static void array_extend(array * const a, uint32_t n) {
114 /* This data structure should not be used for nearly so many entries */
115 force_assert(a->size <= INT32_MAX-n);
116 a->size += n;
117 a->data = ck_realloc_u32((void**)&a->data, a->size,0,sizeof(*a->data));
118 a->sorted = ck_realloc_u32((void**)&a->sorted,a->size,0,sizeof(*a->sorted));
119 memset(a->data+a->used, 0, (a->size-a->used)*sizeof(*a->data));
120 }
121
array_init(uint32_t n)122 array *array_init(uint32_t n) {
123 array *a = ck_calloc(1, sizeof(*a));
124 if (n) array_extend(a, n);
125 return a;
126 }
127
array_free_data(array * const a)128 void array_free_data(array * const a) {
129 if (a->sorted) free(a->sorted);
130 data_unset ** const data = a->data;
131 const uint32_t sz = a->size;
132 for (uint32_t i = 0; i < sz; ++i) {
133 if (data[i]) data[i]->fn->free(data[i]);
134 }
135 free(data);
136 a->data = NULL;
137 a->sorted = NULL;
138 a->used = 0;
139 a->size = 0;
140 }
141
array_copy_array(array * const dst,const array * const src)142 void array_copy_array(array * const dst, const array * const src) {
143 array_free_data(dst);
144 if (0 == src->size) return;
145
146 array_extend(dst, src->size);
147 for (uint32_t i = 0; i < src->used; ++i) {
148 array_insert_unique(dst, src->data[i]->fn->copy(src->data[i]));
149 }
150 }
151
array_free(array * const a)152 void array_free(array * const a) {
153 if (!a) return;
154 array_free_data(a);
155 free(a);
156 }
157
array_reset_data_strings(array * const a)158 void array_reset_data_strings(array * const a) {
159 if (!a) return;
160
161 data_string ** const data = (data_string **)a->data;
162 const uint32_t used = a->used;
163 a->used = 0;
164 for (uint32_t i = 0; i < used; ++i) {
165 data_string * const ds = data[i];
166 /*force_assert(ds->type == TYPE_STRING);*/
167 buffer_reset(&ds->key);
168 buffer_reset(&ds->value);
169 }
170 }
171
172 #if 0 /*(unused; see array_extract_element_klen())*/
173 data_unset *array_pop(array * const a) {
174 data_unset *du;
175
176 force_assert(a->used != 0);
177
178 a->used --;
179 du = a->data[a->used];
180 force_assert(a->sorted[a->used] == du); /* only works on "simple" lists */
181 a->data[a->used] = NULL;
182
183 return du;
184 }
185 #endif
186
187 __attribute_pure__
array_caseless_compare(const char * const a,const char * const b,const uint32_t len)188 static int array_caseless_compare(const char * const a, const char * const b, const uint32_t len) {
189 for (uint32_t i = 0; i < len; ++i) {
190 unsigned int ca = ((unsigned char *)a)[i];
191 unsigned int cb = ((unsigned char *)b)[i];
192 if (ca == cb) continue;
193
194 /* always lowercase for transitive results */
195 if (light_isupper(ca)) ca |= 0x20;
196 if (light_isupper(cb)) cb |= 0x20;
197
198 if (ca == cb) continue;
199 return (int)(ca - cb);
200 }
201 return 0;
202 }
203
204 __attribute_pure__
array_keycmp(const char * const a,const uint32_t alen,const char * const b,const uint32_t blen)205 static int array_keycmp(const char * const a, const uint32_t alen, const char * const b, const uint32_t blen) {
206 return alen < blen ? -1 : alen > blen ? 1 : array_caseless_compare(a, b, blen);
207 }
208
209 __attribute_cold__
210 __attribute_pure__
array_keycmpb(const char * const k,const uint32_t klen,const buffer * const b)211 static int array_keycmpb(const char * const k, const uint32_t klen, const buffer * const b) {
212 /* key is non-empty (0==b->used), though possibly blank (1==b->used)
213 * if inserted into key-value array */
214 /*force_assert(b && b->used);*/
215 return array_keycmp(k, klen, b->ptr, b->used-1);
216 /*return array_keycmp(k, klen, BUF_PTR_LEN(b));*/
217 }
218
219 /* returns pos into a->sorted[] which contains copy of data (ptr) in a->data[]
220 * if pos >= 0, or returns -pos-1 if that is the position-1 in a->sorted[]
221 * where the key needs to be inserted (-1 to avoid -0)
222 */
223 __attribute_hot__
224 __attribute_pure__
array_get_index_ext(const array * const a,const int ext,const char * const k,const uint32_t klen)225 static int32_t array_get_index_ext(const array * const a, const int ext, const char * const k, const uint32_t klen) {
226 /* invariant: [lower-1] < probe < [upper]
227 * invariant: 0 <= lower <= upper <= a->used
228 */
229 uint_fast32_t lower = 0, upper = a->used;
230 while (lower != upper) {
231 const uint_fast32_t probe = (lower + upper) / 2;
232 const int x = ((data_string *)a->sorted[probe])->ext;
233 /* (compare strings only if ext is 0 for both)*/
234 const int e = (ext|x)
235 ? ext
236 : array_keycmpb(k, klen, &a->sorted[probe]->key);
237 if (e < x) /* e < [probe] */
238 upper = probe; /* still: lower <= upper */
239 else if (e > x) /* e > [probe] */
240 lower = probe + 1; /* still: lower <= upper */
241 else /*(e == x)*/ /* found */
242 return (int32_t)probe;
243 }
244 /* not found: [lower-1] < key < [upper] = [lower] ==> insert at [lower] */
245 return -(int)lower - 1;
246 }
247
array_get_element_klen_ext(const array * const a,const int ext,const char * key,const uint32_t klen)248 data_unset *array_get_element_klen_ext(const array * const a, const int ext, const char *key, const uint32_t klen) {
249 const int32_t ipos = array_get_index_ext(a, ext, key, klen);
250 return ipos >= 0 ? a->sorted[ipos] : NULL;
251 }
252
253 /* returns pos into a->sorted[] which contains copy of data (ptr) in a->data[]
254 * if pos >= 0, or returns -pos-1 if that is the position-1 in a->sorted[]
255 * where the key needs to be inserted (-1 to avoid -0)
256 */
257 __attribute_hot__
258 __attribute_pure__
array_get_index(const array * const a,const char * const k,const uint32_t klen)259 static int32_t array_get_index(const array * const a, const char * const k, const uint32_t klen) {
260 /* invariant: [lower-1] < probe < [upper]
261 * invariant: 0 <= lower <= upper <= a->used
262 */
263 uint_fast32_t lower = 0, upper = a->used;
264 while (lower != upper) {
265 uint_fast32_t probe = (lower + upper) / 2;
266 const buffer * const b = &a->sorted[probe]->key;
267 /* key is non-empty (0==b->used), though possibly blank (1==b->used),
268 * if inserted into key-value array */
269 /*force_assert(b && b->used);*/
270 int cmp = array_keycmp(k, klen, b->ptr, b->used-1);
271 /*int cmp = array_keycmp(k, klen, BUF_PTR_LEN(b));*/
272 if (cmp < 0) /* key < [probe] */
273 upper = probe; /* still: lower <= upper */
274 else if (cmp > 0) /* key > [probe] */
275 lower = probe + 1; /* still: lower <= upper */
276 else /*(cmp == 0)*/ /* found */
277 return (int32_t)probe;
278 }
279 /* not found: [lower-1] < key < [upper] = [lower] ==> insert at [lower] */
280 return -(int)lower - 1;
281 }
282
283 __attribute_hot__
array_get_element_klen(const array * const a,const char * key,const uint32_t klen)284 const data_unset *array_get_element_klen(const array * const a, const char *key, const uint32_t klen) {
285 const int32_t ipos = array_get_index(a, key, klen);
286 return ipos >= 0 ? a->sorted[ipos] : NULL;
287 }
288
289 /* non-const (data_config *) for configparser.y (not array_get_element_klen())*/
array_get_data_unset(const array * const a,const char * key,const uint32_t klen)290 data_unset *array_get_data_unset(const array * const a, const char *key, const uint32_t klen) {
291 const int32_t ipos = array_get_index(a, key, klen);
292 return ipos >= 0 ? a->sorted[ipos] : NULL;
293 }
294
array_extract_element_klen(array * const a,const char * key,const uint32_t klen)295 data_unset *array_extract_element_klen(array * const a, const char *key, const uint32_t klen) {
296 const int32_t ipos = array_get_index(a, key, klen);
297 if (ipos < 0) return NULL;
298
299 /* remove entry from a->sorted: move everything after pos one step left */
300 data_unset * const entry = a->sorted[ipos];
301 const uint32_t last_ndx = --a->used;
302 if (last_ndx != (uint32_t)ipos) {
303 data_unset ** const d = a->sorted + ipos;
304 memmove(d, d+1, (last_ndx - (uint32_t)ipos) * sizeof(*d));
305 }
306
307 if (entry != a->data[last_ndx]) {
308 /* walk a->data[] to find data ptr */
309 /* (not checking (ndx <= last_ndx) since entry must be in a->data[]) */
310 uint32_t ndx = 0;
311 while (entry != a->data[ndx]) ++ndx;
312 a->data[ndx] = a->data[last_ndx]; /* swap with last element */
313 }
314 a->data[last_ndx] = NULL;
315 return entry;
316 }
317
array_get_unused_element(array * const a,const data_type_t t)318 static data_unset *array_get_unused_element(array * const a, const data_type_t t) {
319 /* After initial startup and config, most array usage is of homogeneous types
320 * and arrays are cleared once per request, so check only the first unused
321 * element to see if it can be reused */
322 #if 1
323 data_unset * const du = (a->used < a->size) ? a->data[a->used] : NULL;
324 if (NULL != du && du->type == t) {
325 a->data[a->used] = NULL;/* make empty slot at a->used for next insert */
326 return du;
327 }
328 return NULL;
329 #else
330 data_unset ** const data = a->data;
331 for (uint32_t i = a->used, sz = a->size; i < sz; ++i) {
332 if (data[i] && data[i]->type == t) {
333 data_unset * const ds = data[i];
334
335 /* make empty slot at a->used for next insert */
336 data[i] = data[a->used];
337 data[a->used] = NULL;
338
339 return ds;
340 }
341 }
342
343 return NULL;
344 #endif
345 }
346
347 __attribute_hot__
array_insert_data_at_pos(array * const a,data_unset * const entry,const uint_fast32_t pos)348 static data_unset * array_insert_data_at_pos(array * const a, data_unset * const entry, const uint_fast32_t pos) {
349 if (a->used < a->size) {
350 data_unset * const prev = a->data[a->used];
351 if (__builtin_expect( (prev != NULL), 0))
352 prev->fn->free(prev); /* free prior data, if any, from slot */
353 }
354 else {
355 array_extend(a, 16);
356 }
357
358 uint_fast32_t ndx = a->used++;
359 a->data[ndx] = entry;
360
361 /* move everything one step to the right */
362 ndx -= pos;
363 data_unset ** const d = a->sorted + pos;
364 if (__builtin_expect( (ndx), 1))
365 memmove(d+1, d, ndx * sizeof(*a->sorted));
366 *d = entry;
367 return entry;
368 }
369
array_insert_integer_at_pos(array * const a,const uint_fast32_t pos)370 static data_integer * array_insert_integer_at_pos(array * const a, const uint_fast32_t pos) {
371 #if 0 /*(not currently used by lighttpd in way that reuse would occur)*/
372 data_integer *di = (data_integer *)array_get_unused_element(a,TYPE_INTEGER);
373 if (NULL == di) di = array_data_integer_init();
374 #else
375 data_integer * const di = array_data_integer_init();
376 #endif
377 return (data_integer *)array_insert_data_at_pos(a, (data_unset *)di, pos);
378 }
379
380 __attribute_hot__
array_insert_string_at_pos(array * const a,const uint_fast32_t pos)381 static data_string * array_insert_string_at_pos(array * const a, const uint_fast32_t pos) {
382 data_string *ds = (data_string *)array_get_unused_element(a, TYPE_STRING);
383 if (NULL == ds) ds = array_data_string_init();
384 return (data_string *)array_insert_data_at_pos(a, (data_unset *)ds, pos);
385 }
386
387 __attribute_hot__
array_get_buf_ptr_ext(array * const a,const int ext,const char * const k,const uint32_t klen)388 buffer * array_get_buf_ptr_ext(array * const a, const int ext, const char * const k, const uint32_t klen) {
389 int32_t ipos = array_get_index_ext(a, ext, k, klen);
390 if (ipos >= 0) return &((data_string *)a->sorted[ipos])->value;
391
392 data_string * const ds = array_insert_string_at_pos(a, (uint32_t)(-ipos-1));
393 ds->ext = ext;
394 buffer_copy_string_len(&ds->key, k, klen);
395 buffer_clear(&ds->value);
396 return &ds->value;
397 }
398
array_get_int_ptr(array * const a,const char * const k,const uint32_t klen)399 int * array_get_int_ptr(array * const a, const char * const k, const uint32_t klen) {
400 int32_t ipos = array_get_index(a, k, klen);
401 if (ipos >= 0) return &((data_integer *)a->sorted[ipos])->value;
402
403 data_integer * const di =array_insert_integer_at_pos(a,(uint32_t)(-ipos-1));
404 buffer_copy_string_len(&di->key, k, klen);
405 di->value = 0;
406 return &di->value;
407 }
408
array_get_buf_ptr(array * const a,const char * const k,const uint32_t klen)409 buffer * array_get_buf_ptr(array * const a, const char * const k, const uint32_t klen) {
410 int32_t ipos = array_get_index(a, k, klen);
411 if (ipos >= 0) return &((data_string *)a->sorted[ipos])->value;
412
413 data_string * const ds = array_insert_string_at_pos(a, (uint32_t)(-ipos-1));
414 buffer_copy_string_len(&ds->key, k, klen);
415 buffer_clear(&ds->value);
416 return &ds->value;
417 }
418
array_insert_value(array * const a,const char * const v,const uint32_t vlen)419 void array_insert_value(array * const a, const char * const v, const uint32_t vlen) {
420 data_string * const ds = array_insert_string_at_pos(a, a->used);
421 buffer_clear(&ds->key);
422 buffer_copy_string_len(&ds->value, v, vlen);
423 }
424
425 /* if entry already exists return pointer to existing entry, otherwise insert entry and return NULL */
426 __attribute_cold__
array_find_or_insert(array * const a,data_unset * const entry)427 static data_unset **array_find_or_insert(array * const a, data_unset * const entry) {
428 force_assert(NULL != entry);
429
430 /* push value onto end of array if there is no key */
431 if (buffer_is_unset(&entry->key)) {
432 array_insert_data_at_pos(a, entry, a->used);
433 return NULL;
434 }
435
436 /* try to find the entry */
437 const int32_t ipos = array_get_index(a, BUF_PTR_LEN(&entry->key));
438 if (ipos >= 0) return &a->sorted[ipos];
439
440 array_insert_data_at_pos(a, entry, (uint32_t)(-ipos - 1));
441 return NULL;
442 }
443
444 /* replace or insert data (free existing entry) */
array_replace(array * const a,data_unset * const entry)445 void array_replace(array * const a, data_unset * const entry) {
446 if (NULL == array_find_or_insert(a, entry)) return;
447
448 /* find the entry (array_find_or_insert() returned non-NULL) */
449 const int32_t ipos = array_get_index(a, BUF_PTR_LEN(&entry->key));
450 force_assert(ipos >= 0);
451 data_unset *old = a->sorted[ipos];
452 force_assert(old != entry);
453 a->sorted[ipos] = entry;
454
455 uint32_t i = 0;
456 while (i < a->used && a->data[i] != old) ++i;
457 force_assert(i != a->used);
458 a->data[i] = entry;
459
460 old->fn->free(old);
461 }
462
array_insert_unique(array * const a,data_unset * const entry)463 void array_insert_unique(array * const a, data_unset * const entry) {
464 data_unset **old;
465
466 if (NULL != (old = array_find_or_insert(a, entry))) {
467 if (entry->fn->insert_dup) {
468 force_assert((*old)->type == entry->type);
469 entry->fn->insert_dup(*old, entry);
470 }
471 entry->fn->free(entry);
472 }
473 }
474
array_is_vlist(const array * const a)475 int array_is_vlist(const array * const a) {
476 for (uint32_t i = 0; i < a->used; ++i) {
477 data_unset *du = a->data[i];
478 if (!buffer_is_unset(&du->key) || du->type != TYPE_STRING) return 0;
479 }
480 return 1;
481 }
482
array_is_kvany(const array * const a)483 int array_is_kvany(const array * const a) {
484 for (uint32_t i = 0; i < a->used; ++i) {
485 data_unset *du = a->data[i];
486 if (buffer_is_unset(&du->key)) return 0;
487 }
488 return 1;
489 }
490
array_is_kvarray(const array * const a)491 int array_is_kvarray(const array * const a) {
492 for (uint32_t i = 0; i < a->used; ++i) {
493 data_unset *du = a->data[i];
494 if (buffer_is_unset(&du->key) || du->type != TYPE_ARRAY) return 0;
495 }
496 return 1;
497 }
498
array_is_kvstring(const array * const a)499 int array_is_kvstring(const array * const a) {
500 for (uint32_t i = 0; i < a->used; ++i) {
501 data_unset *du = a->data[i];
502 if (buffer_is_unset(&du->key) || du->type != TYPE_STRING) return 0;
503 }
504 return 1;
505 }
506
507 /* array_match_*() routines follow very similar pattern, but operate on slightly
508 * different data: array key/value, prefix/suffix match, case-insensitive or not
509 * While these could be combined into fewer routines with flags to modify the
510 * behavior, the interface distinctions are useful to add clarity to the code,
511 * and the specialized routines run slightly faster */
512
513 data_unset *
array_match_key_prefix_klen(const array * const a,const char * const s,const uint32_t slen)514 array_match_key_prefix_klen (const array * const a, const char * const s, const uint32_t slen)
515 {
516 for (uint32_t i = 0; i < a->used; ++i) {
517 const buffer * const key = &a->data[i]->key;
518 const uint32_t klen = buffer_clen(key);
519 if (klen <= slen && 0 == memcmp(s, key->ptr, klen))
520 return a->data[i];
521 }
522 return NULL;
523 }
524
525 data_unset *
array_match_key_prefix_nc_klen(const array * const a,const char * const s,const uint32_t slen)526 array_match_key_prefix_nc_klen (const array * const a, const char * const s, const uint32_t slen)
527 {
528 for (uint32_t i = 0; i < a->used; ++i) {
529 const buffer * const key = &a->data[i]->key;
530 const uint32_t klen = buffer_clen(key);
531 if (klen <= slen && buffer_eq_icase_ssn(s, key->ptr, klen))
532 return a->data[i];
533 }
534 return NULL;
535 }
536
537 data_unset *
array_match_key_prefix(const array * const a,const buffer * const b)538 array_match_key_prefix (const array * const a, const buffer * const b)
539 {
540 #ifdef __clang_analyzer__
541 force_assert(b);
542 #endif
543 return array_match_key_prefix_klen(a, BUF_PTR_LEN(b));
544 }
545
546 data_unset *
array_match_key_prefix_nc(const array * const a,const buffer * const b)547 array_match_key_prefix_nc (const array * const a, const buffer * const b)
548 {
549 return array_match_key_prefix_nc_klen(a, BUF_PTR_LEN(b));
550 }
551
552 const buffer *
array_match_value_prefix(const array * const a,const buffer * const b)553 array_match_value_prefix (const array * const a, const buffer * const b)
554 {
555 const uint32_t blen = buffer_clen(b);
556
557 for (uint32_t i = 0; i < a->used; ++i) {
558 const buffer * const value = &((data_string *)a->data[i])->value;
559 const uint32_t vlen = buffer_clen(value);
560 if (vlen <= blen && 0 == memcmp(b->ptr, value->ptr, vlen))
561 return value;
562 }
563 return NULL;
564 }
565
566 const buffer *
array_match_value_prefix_nc(const array * const a,const buffer * const b)567 array_match_value_prefix_nc (const array * const a, const buffer * const b)
568 {
569 const uint32_t blen = buffer_clen(b);
570
571 for (uint32_t i = 0; i < a->used; ++i) {
572 const buffer * const value = &((data_string *)a->data[i])->value;
573 const uint32_t vlen = buffer_clen(value);
574 if (vlen <= blen && buffer_eq_icase_ssn(b->ptr, value->ptr, vlen))
575 return value;
576 }
577 return NULL;
578 }
579
580 data_unset *
array_match_key_suffix(const array * const a,const buffer * const b)581 array_match_key_suffix (const array * const a, const buffer * const b)
582 {
583 const uint32_t blen = buffer_clen(b);
584 const char * const end = b->ptr + blen;
585
586 for (uint32_t i = 0; i < a->used; ++i) {
587 const buffer * const key = &a->data[i]->key;
588 const uint32_t klen = buffer_clen(key);
589 if (klen <= blen && 0 == memcmp(end - klen, key->ptr, klen))
590 return a->data[i];
591 }
592 return NULL;
593 }
594
595 data_unset *
array_match_key_suffix_nc(const array * const a,const buffer * const b)596 array_match_key_suffix_nc (const array * const a, const buffer * const b)
597 {
598 const uint32_t blen = buffer_clen(b);
599 const char * const end = b->ptr + blen;
600
601 for (uint32_t i = 0; i < a->used; ++i) {
602 const buffer * const key = &a->data[i]->key;
603 const uint32_t klen = buffer_clen(key);
604 if (klen <= blen && buffer_eq_icase_ssn(end - klen, key->ptr, klen))
605 return a->data[i];
606 }
607 return NULL;
608 }
609
610 const buffer *
array_match_value_suffix(const array * const a,const buffer * const b)611 array_match_value_suffix (const array * const a, const buffer * const b)
612 {
613 const uint32_t blen = buffer_clen(b);
614 const char * const end = b->ptr + blen;
615
616 for (uint32_t i = 0; i < a->used; ++i) {
617 const buffer * const value = &((data_string *)a->data[i])->value;
618 const uint32_t vlen = buffer_clen(value);
619 if (vlen <= blen && 0 == memcmp(end - vlen, value->ptr, vlen))
620 return value;
621 }
622 return NULL;
623 }
624
625 const buffer *
array_match_value_suffix_nc(const array * const a,const buffer * const b)626 array_match_value_suffix_nc (const array * const a, const buffer * const b)
627 {
628 const uint32_t blen = buffer_clen(b);
629 const char * const end = b->ptr + blen;
630
631 for (uint32_t i = 0; i < a->used; ++i) {
632 const buffer * const value = &((data_string *)a->data[i])->value;
633 const uint32_t vlen = buffer_clen(value);
634 if (vlen <= blen && buffer_eq_icase_ssn(end - vlen, value->ptr, vlen))
635 return value;
636 }
637 return NULL;
638 }
639
640 data_unset *
array_match_path_or_ext(const array * const a,const buffer * const b)641 array_match_path_or_ext (const array * const a, const buffer * const b)
642 {
643 const uint32_t blen = buffer_clen(b);
644
645 for (uint32_t i = 0; i < a->used; ++i) {
646 /* check extension in the form "^/path" or ".ext$" */
647 const buffer * const key = &a->data[i]->key;
648 const uint32_t klen = buffer_clen(key);
649 if (klen <= blen
650 && 0 == memcmp((*(key->ptr) == '/' ? b->ptr : b->ptr + blen - klen),
651 key->ptr, klen))
652 return a->data[i];
653 }
654 return NULL;
655 }
656