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