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