xref: /lighttpd1.4/src/array.c (revision fefdf7f0)
18abd06a7SGlenn Strauss #include "first.h"
28abd06a7SGlenn Strauss 
322e8b456SStefan Bühler #include "array.h"
422e8b456SStefan Bühler #include "buffer.h"
522e8b456SStefan Bühler 
6bcdc6a3bSJan Kneschke #include <string.h>
7bcdc6a3bSJan Kneschke #include <stdlib.h>
8bcdc6a3bSJan Kneschke #include <limits.h>
9bcdc6a3bSJan Kneschke 
10b2991c68SGlenn Strauss __attribute_cold__
1124680a91SGlenn Strauss static void array_extend(array * const a, uint32_t n) {
1224680a91SGlenn Strauss     a->size  += n;
13b2991c68SGlenn Strauss     a->data   = realloc(a->data,   sizeof(*a->data)   * a->size);
14a762402dSGlenn Strauss     a->sorted = realloc(a->sorted, sizeof(*a->sorted) * a->size);
15b2991c68SGlenn Strauss     force_assert(a->data);
16a762402dSGlenn Strauss     force_assert(a->sorted);
17b2991c68SGlenn Strauss     memset(a->data+a->used, 0, (a->size-a->used)*sizeof(*a->data));
18b2991c68SGlenn Strauss }
19224bf545SStefan Bühler 
2024680a91SGlenn Strauss array *array_init(uint32_t n) {
21bcdc6a3bSJan Kneschke 	array *a;
22bcdc6a3bSJan Kneschke 
23bcdc6a3bSJan Kneschke 	a = calloc(1, sizeof(*a));
2407dd0bd0SStefan Bühler 	force_assert(a);
2524680a91SGlenn Strauss 	if (n) array_extend(a, n);
26bcdc6a3bSJan Kneschke 
27bcdc6a3bSJan Kneschke 	return a;
28bcdc6a3bSJan Kneschke }
29bcdc6a3bSJan Kneschke 
30c2238256SGlenn Strauss void array_free_data(array * const a) {
31a762402dSGlenn Strauss 	if (a->sorted) free(a->sorted);
32b2991c68SGlenn Strauss 	data_unset ** const data = a->data;
33b2991c68SGlenn Strauss 	const uint32_t sz = a->size;
34b2991c68SGlenn Strauss 	for (uint32_t i = 0; i < sz; ++i) {
35b2991c68SGlenn Strauss 		if (data[i]) data[i]->fn->free(data[i]);
36bcdc6a3bSJan Kneschke 	}
37c2238256SGlenn Strauss 	free(data);
38f24e6d69SGlenn Strauss 	a->data = NULL;
39f24e6d69SGlenn Strauss 	a->sorted = NULL;
40f24e6d69SGlenn Strauss 	a->used = 0;
41f24e6d69SGlenn Strauss 	a->size = 0;
42c2238256SGlenn Strauss }
43bcdc6a3bSJan Kneschke 
44c2238256SGlenn Strauss void array_copy_array(array * const dst, const array * const src) {
45c2238256SGlenn Strauss 	array_free_data(dst);
46c2238256SGlenn Strauss 	if (0 == src->size) return;
47c2238256SGlenn Strauss 
48*fefdf7f0SGlenn Strauss 	array_extend(dst, src->size);
49c2238256SGlenn Strauss 	for (uint32_t i = 0; i < src->used; ++i) {
50*fefdf7f0SGlenn Strauss 		array_insert_unique(dst, src->data[i]->fn->copy(src->data[i]));
51c2238256SGlenn Strauss 	}
52c2238256SGlenn Strauss }
53c2238256SGlenn Strauss 
54c2238256SGlenn Strauss void array_free(array * const a) {
55c2238256SGlenn Strauss 	if (!a) return;
56c2238256SGlenn Strauss 	array_free_data(a);
57bcdc6a3bSJan Kneschke 	free(a);
58bcdc6a3bSJan Kneschke }
59bcdc6a3bSJan Kneschke 
60b2991c68SGlenn Strauss void array_reset_data_strings(array * const a) {
61062089ffSGlenn Strauss 	if (!a) return;
62062089ffSGlenn Strauss 
63b2991c68SGlenn Strauss 	data_string ** const data = (data_string **)a->data;
64b2991c68SGlenn Strauss 	const uint32_t used = a->used;
65b2991c68SGlenn Strauss 	a->used = 0;
66b2991c68SGlenn Strauss 	for (uint32_t i = 0; i < used; ++i) {
67b2991c68SGlenn Strauss 		data_string * const ds = data[i];
68062089ffSGlenn Strauss 		/*force_assert(ds->type == TYPE_STRING);*/
69b600b75fSGlenn Strauss 		buffer_reset(&ds->key);
70b600b75fSGlenn Strauss 		buffer_reset(&ds->value);
71b2991c68SGlenn Strauss 	}
72062089ffSGlenn Strauss }
73062089ffSGlenn Strauss 
74b2991c68SGlenn Strauss #if 0 /*(unused; see array_extract_element_klen())*/
75b2991c68SGlenn Strauss data_unset *array_pop(array * const a) {
768073d5feSJan Kneschke 	data_unset *du;
778073d5feSJan Kneschke 
7807dd0bd0SStefan Bühler 	force_assert(a->used != 0);
798073d5feSJan Kneschke 
808073d5feSJan Kneschke 	a->used --;
818073d5feSJan Kneschke 	du = a->data[a->used];
82e3dc34d1SGlenn Strauss 	force_assert(a->sorted[a->used] == du); /* only works on "simple" lists */
838073d5feSJan Kneschke 	a->data[a->used] = NULL;
848073d5feSJan Kneschke 
858073d5feSJan Kneschke 	return du;
868073d5feSJan Kneschke }
87b2991c68SGlenn Strauss #endif
888073d5feSJan Kneschke 
89ca059d58SGlenn Strauss __attribute_pure__
9068ec5ad6SGlenn Strauss static int array_caseless_compare(const char * const a, const char * const b, const uint32_t len) {
9168ec5ad6SGlenn Strauss     for (uint32_t i = 0; i < len; ++i) {
92ca059d58SGlenn Strauss         unsigned int ca = ((unsigned char *)a)[i];
93ca059d58SGlenn Strauss         unsigned int cb = ((unsigned char *)b)[i];
94ca059d58SGlenn Strauss         if (ca == cb) continue;
95ca059d58SGlenn Strauss 
96ca059d58SGlenn Strauss         /* always lowercase for transitive results */
97c58b95f2SGlenn Strauss         if (light_isupper(ca)) ca |= 0x20;
98c58b95f2SGlenn Strauss         if (light_isupper(cb)) cb |= 0x20;
99ca059d58SGlenn Strauss 
100ca059d58SGlenn Strauss         if (ca == cb) continue;
101ca059d58SGlenn Strauss         return (int)(ca - cb);
102ca059d58SGlenn Strauss     }
103ca059d58SGlenn Strauss     return 0;
104ca059d58SGlenn Strauss }
105ca059d58SGlenn Strauss 
106ca059d58SGlenn Strauss __attribute_pure__
10768ec5ad6SGlenn Strauss static int array_keycmp(const char * const a, const uint32_t alen, const char * const b, const uint32_t blen) {
108ca059d58SGlenn Strauss     return alen < blen ? -1 : alen > blen ? 1 : array_caseless_compare(a, b, blen);
109758174ecSGlenn Strauss }
110758174ecSGlenn Strauss 
1112e0676fdSGlenn Strauss __attribute_cold__
1122e0676fdSGlenn Strauss __attribute_pure__
1132e0676fdSGlenn Strauss static int array_keycmpb(const char * const k, const uint32_t klen, const buffer * const b) {
1142e0676fdSGlenn Strauss     /* key is non-empty (0==b->used), though possibly blank (1==b->used)
1152e0676fdSGlenn Strauss      * if inserted into key-value array */
1162e0676fdSGlenn Strauss     /*force_assert(b && b->used);*/
1172e0676fdSGlenn Strauss     return array_keycmp(k, klen, b->ptr, b->used-1);
1182e0676fdSGlenn Strauss     /*return array_keycmp(k, klen, CONST_BUF_LEN(b));*/
1192e0676fdSGlenn Strauss }
1202e0676fdSGlenn Strauss 
1212e0676fdSGlenn Strauss /* returns pos into a->sorted[] which contains copy of data (ptr) in a->data[]
1222e0676fdSGlenn Strauss  * if pos >= 0, or returns -pos-1 if that is the position-1 in a->sorted[]
1232e0676fdSGlenn Strauss  * where the key needs to be inserted (-1 to avoid -0)
1242e0676fdSGlenn Strauss  */
1252e0676fdSGlenn Strauss __attribute_hot__
1262e0676fdSGlenn Strauss __attribute_pure__
1272e0676fdSGlenn Strauss static int32_t array_get_index_ext(const array * const a, const int ext, const char * const k, const uint32_t klen) {
1282e0676fdSGlenn Strauss     /* invariant: [lower-1] < probe < [upper]
1292e0676fdSGlenn Strauss      * invariant: 0 <= lower <= upper <= a->used
1302e0676fdSGlenn Strauss      */
1312e0676fdSGlenn Strauss     uint32_t lower = 0, upper = a->used;
1322e0676fdSGlenn Strauss     while (lower != upper) {
1332e0676fdSGlenn Strauss         const uint32_t probe = (lower + upper) / 2;
1342e0676fdSGlenn Strauss         const int x = ((data_string *)a->sorted[probe])->ext;
1352e0676fdSGlenn Strauss         /* (compare strings only if ext is 0 for both)*/
1362e0676fdSGlenn Strauss         const int e = (ext|x)
1372e0676fdSGlenn Strauss           ? ext
1382e0676fdSGlenn Strauss           : array_keycmpb(k, klen, &a->sorted[probe]->key);
1392e0676fdSGlenn Strauss         if (e < x)             /* e < [probe] */
1402e0676fdSGlenn Strauss             upper = probe;     /* still: lower <= upper */
1412e0676fdSGlenn Strauss         else if (e > x)        /* e > [probe] */
1422e0676fdSGlenn Strauss             lower = probe + 1; /* still: lower <= upper */
1432e0676fdSGlenn Strauss         else  /*(e == x)*/     /* found */
1442e0676fdSGlenn Strauss             return (int32_t)probe;
1452e0676fdSGlenn Strauss     }
1462e0676fdSGlenn Strauss     /* not found: [lower-1] < key < [upper] = [lower] ==> insert at [lower] */
1472e0676fdSGlenn Strauss     return -(int)lower - 1;
1482e0676fdSGlenn Strauss }
1492e0676fdSGlenn Strauss 
1502e0676fdSGlenn Strauss data_unset *array_get_element_klen_ext(const array * const a, const int ext, const char *key, const uint32_t klen) {
1512e0676fdSGlenn Strauss     const int32_t ipos = array_get_index_ext(a, ext, key, klen);
1522e0676fdSGlenn Strauss     return ipos >= 0 ? a->sorted[ipos] : NULL;
1532e0676fdSGlenn Strauss }
1542e0676fdSGlenn Strauss 
155e3dc34d1SGlenn Strauss /* returns pos into a->sorted[] which contains copy of data (ptr) in a->data[]
156a762402dSGlenn Strauss  * if pos >= 0, or returns -pos-1 if that is the position-1 in a->sorted[]
157a762402dSGlenn Strauss  * where the key needs to be inserted (-1 to avoid -0)
158224bf545SStefan Bühler  */
159b2991c68SGlenn Strauss __attribute_hot__
160b2991c68SGlenn Strauss __attribute_pure__
16168ec5ad6SGlenn Strauss static int32_t array_get_index(const array * const a, const char * const k, const uint32_t klen) {
162b2991c68SGlenn Strauss     /* invariant: [lower-1] < probe < [upper]
163b2991c68SGlenn Strauss      * invariant: 0 <= lower <= upper <= a->used
164224bf545SStefan Bühler      */
165b2991c68SGlenn Strauss     uint32_t lower = 0, upper = a->used;
166224bf545SStefan Bühler     while (lower != upper) {
167b2991c68SGlenn Strauss         uint32_t probe = (lower + upper) / 2;
168e3dc34d1SGlenn Strauss         const buffer * const b = &a->sorted[probe]->key;
16961785d86SGlenn Strauss         /* key is non-empty (0==b->used), though possibly blank (1==b->used),
17061785d86SGlenn Strauss          * if inserted into key-value array */
17161785d86SGlenn Strauss         /*force_assert(b && b->used);*/
17261785d86SGlenn Strauss         int cmp = array_keycmp(k, klen, b->ptr, b->used-1);
17361785d86SGlenn Strauss         /*int cmp = array_keycmp(k, klen, CONST_BUF_LEN(b));*/
174b2991c68SGlenn Strauss         if (cmp < 0)           /* key < [probe] */
175224bf545SStefan Bühler             upper = probe;     /* still: lower <= upper */
176b2991c68SGlenn Strauss         else if (cmp > 0)      /* key > [probe] */
177224bf545SStefan Bühler             lower = probe + 1; /* still: lower <= upper */
178b2991c68SGlenn Strauss         else  /*(cmp == 0)*/   /* found */
179b2991c68SGlenn Strauss             return (int32_t)probe;
180bcdc6a3bSJan Kneschke     }
181224bf545SStefan Bühler     /* not found: [lower-1] < key < [upper] = [lower] ==> insert at [lower] */
182b2991c68SGlenn Strauss     return -(int)lower - 1;
183bcdc6a3bSJan Kneschke }
184bcdc6a3bSJan Kneschke 
185b2991c68SGlenn Strauss __attribute_hot__
18668ec5ad6SGlenn Strauss data_unset *array_get_element_klen(const array * const a, const char *key, const uint32_t klen) {
18783535bbeSGlenn Strauss     const int32_t ipos = array_get_index(a, key, klen);
188e3dc34d1SGlenn Strauss     return ipos >= 0 ? a->sorted[ipos] : NULL;
18983535bbeSGlenn Strauss }
19083535bbeSGlenn Strauss 
19183535bbeSGlenn Strauss /* non-const (data_config *) for configparser.y (not array_get_element_klen())*/
19268ec5ad6SGlenn Strauss data_unset *array_get_data_unset(const array * const a, const char *key, const uint32_t klen) {
193b2991c68SGlenn Strauss     const int32_t ipos = array_get_index(a, key, klen);
194e3dc34d1SGlenn Strauss     return ipos >= 0 ? a->sorted[ipos] : NULL;
195bcdc6a3bSJan Kneschke }
196bcdc6a3bSJan Kneschke 
19768ec5ad6SGlenn Strauss data_unset *array_extract_element_klen(array * const a, const char *key, const uint32_t klen) {
198b2991c68SGlenn Strauss     const int32_t ipos = array_get_index(a, key, klen);
199b2991c68SGlenn Strauss     if (ipos < 0) return NULL;
200bcdc6a3bSJan Kneschke 
201e3dc34d1SGlenn Strauss     /* remove entry from a->sorted: move everything after pos one step left */
202e3dc34d1SGlenn Strauss     data_unset * const entry = a->sorted[ipos];
203c9f1b612SGlenn Strauss     const uint32_t last_ndx = --a->used;
204e3dc34d1SGlenn Strauss     if (last_ndx != (uint32_t)ipos) {
205e3dc34d1SGlenn Strauss         data_unset ** const d = a->sorted + ipos;
206e3dc34d1SGlenn Strauss         memmove(d, d+1, (last_ndx - (uint32_t)ipos) * sizeof(*d));
207c9f1b612SGlenn Strauss     }
208a762402dSGlenn Strauss 
209e3dc34d1SGlenn Strauss     if (entry != a->data[last_ndx]) {
210e3dc34d1SGlenn Strauss         /* walk a->data[] to find data ptr */
211e3dc34d1SGlenn Strauss         /* (not checking (ndx <= last_ndx) since entry must be in a->data[]) */
212e3dc34d1SGlenn Strauss         uint32_t ndx = 0;
213e3dc34d1SGlenn Strauss         while (entry != a->data[ndx]) ++ndx;
214e3dc34d1SGlenn Strauss         a->data[ndx] = a->data[last_ndx]; /* swap with last element */
215a762402dSGlenn Strauss     }
21668e4a416SStefan Bühler     a->data[last_ndx] = NULL;
21768e4a416SStefan Bühler     return entry;
21868e4a416SStefan Bühler }
21968e4a416SStefan Bühler 
220b2991c68SGlenn Strauss static data_unset *array_get_unused_element(array * const a, const data_type_t t) {
221c752d469SGlenn Strauss     /* After initial startup and config, most array usage is of homogeneous types
222b2991c68SGlenn Strauss      * and arrays are cleared once per request, so check only the first unused
223b2991c68SGlenn Strauss      * element to see if it can be reused */
224b2991c68SGlenn Strauss   #if 1
225b2991c68SGlenn Strauss     data_unset * const du = (a->used < a->size) ? a->data[a->used] : NULL;
226b2991c68SGlenn Strauss     if (NULL != du && du->type == t) {
227b2991c68SGlenn Strauss         a->data[a->used] = NULL;/* make empty slot at a->used for next insert */
228b2991c68SGlenn Strauss         return du;
229b2991c68SGlenn Strauss     }
230b2991c68SGlenn Strauss     return NULL;
231b2991c68SGlenn Strauss   #else
232b2991c68SGlenn Strauss 	data_unset ** const data = a->data;
233b2991c68SGlenn Strauss 	for (uint32_t i = a->used, sz = a->size; i < sz; ++i) {
234b2991c68SGlenn Strauss 		if (data[i] && data[i]->type == t) {
235b2991c68SGlenn Strauss 			data_unset * const ds = data[i];
236bcdc6a3bSJan Kneschke 
23712f375f3SStefan Bühler 			/* make empty slot at a->used for next insert */
238b2991c68SGlenn Strauss 			data[i] = data[a->used];
239b2991c68SGlenn Strauss 			data[a->used] = NULL;
240bcdc6a3bSJan Kneschke 
241bcdc6a3bSJan Kneschke 			return ds;
242bcdc6a3bSJan Kneschke 		}
24312f375f3SStefan Bühler 	}
24412f375f3SStefan Bühler 
24512f375f3SStefan Bühler 	return NULL;
246b2991c68SGlenn Strauss   #endif
24712f375f3SStefan Bühler }
248bcdc6a3bSJan Kneschke 
2492e0676fdSGlenn Strauss __attribute_hot__
250b2991c68SGlenn Strauss static void array_insert_data_at_pos(array * const a, data_unset * const entry, const uint32_t pos) {
251b2991c68SGlenn Strauss     /* This data structure should not be used for nearly so many entries */
252b2991c68SGlenn Strauss     force_assert(a->used + 1 <= INT32_MAX);
253bcdc6a3bSJan Kneschke 
254d28bac32SGlenn Strauss     if (a->size == a->used) {
25524680a91SGlenn Strauss         array_extend(a, 16);
256bcdc6a3bSJan Kneschke     }
257bcdc6a3bSJan Kneschke 
258b2991c68SGlenn Strauss     const uint32_t ndx = a->used++;
259b2991c68SGlenn Strauss     data_unset * const prev = a->data[ndx];
260a762402dSGlenn Strauss     a->data[ndx] = entry;
261bcdc6a3bSJan Kneschke 
2628d8ae9cbSStefan Bühler     /* move everything one step to the right */
263bcdc6a3bSJan Kneschke     if (pos != ndx) {
264e3dc34d1SGlenn Strauss         data_unset ** const d = a->sorted + pos;
265e3dc34d1SGlenn Strauss         memmove(d+1, d, (ndx - pos) * sizeof(*a->sorted));
266bcdc6a3bSJan Kneschke     }
267e3dc34d1SGlenn Strauss     a->sorted[pos] = entry;
268bcdc6a3bSJan Kneschke 
269b2991c68SGlenn Strauss     if (prev) prev->fn->free(prev); /* free prior data, if any, from slot */
270b2991c68SGlenn Strauss }
271b2991c68SGlenn Strauss 
272b2991c68SGlenn Strauss static data_integer * array_insert_integer_at_pos(array * const a, const uint32_t pos) {
273b2991c68SGlenn Strauss   #if 0 /*(not currently used by lighttpd in way that reuse would occur)*/
274b2991c68SGlenn Strauss     data_integer *di = (data_integer *)array_get_unused_element(a,TYPE_INTEGER);
275b2991c68SGlenn Strauss     if (NULL == di) di = data_integer_init();
276b2991c68SGlenn Strauss   #else
277b2991c68SGlenn Strauss     data_integer * const di = data_integer_init();
278b2991c68SGlenn Strauss   #endif
279b2991c68SGlenn Strauss     array_insert_data_at_pos(a, (data_unset *)di, pos);
280b2991c68SGlenn Strauss     return di;
281b2991c68SGlenn Strauss }
282b2991c68SGlenn Strauss 
2832e0676fdSGlenn Strauss __attribute_hot__
284b2991c68SGlenn Strauss static data_string * array_insert_string_at_pos(array * const a, const uint32_t pos) {
285b2991c68SGlenn Strauss     data_string *ds = (data_string *)array_get_unused_element(a, TYPE_STRING);
286b2991c68SGlenn Strauss     if (NULL == ds) ds = data_string_init();
287b2991c68SGlenn Strauss     array_insert_data_at_pos(a, (data_unset *)ds, pos);
288b2991c68SGlenn Strauss     return ds;
289b2991c68SGlenn Strauss }
290b2991c68SGlenn Strauss 
2912e0676fdSGlenn Strauss __attribute_hot__
2922e0676fdSGlenn Strauss buffer * array_get_buf_ptr_ext(array * const a, const int ext, const char * const k, const uint32_t klen) {
2932e0676fdSGlenn Strauss     int32_t ipos = array_get_index_ext(a, ext, k, klen);
2942e0676fdSGlenn Strauss     if (ipos >= 0) return &((data_string *)a->sorted[ipos])->value;
2952e0676fdSGlenn Strauss 
2962e0676fdSGlenn Strauss     data_string * const ds = array_insert_string_at_pos(a, (uint32_t)(-ipos-1));
2972e0676fdSGlenn Strauss     ds->ext = ext;
2982e0676fdSGlenn Strauss     buffer_copy_string_len(&ds->key, k, klen);
2992e0676fdSGlenn Strauss     buffer_clear(&ds->value);
3002e0676fdSGlenn Strauss     return &ds->value;
3012e0676fdSGlenn Strauss }
3022e0676fdSGlenn Strauss 
30368ec5ad6SGlenn Strauss int * array_get_int_ptr(array * const a, const char * const k, const uint32_t klen) {
304b2991c68SGlenn Strauss     int32_t ipos = array_get_index(a, k, klen);
305e3dc34d1SGlenn Strauss     if (ipos >= 0) return &((data_integer *)a->sorted[ipos])->value;
306b2991c68SGlenn Strauss 
307b2991c68SGlenn Strauss     data_integer * const di =array_insert_integer_at_pos(a,(uint32_t)(-ipos-1));
308ad9b7e00SGlenn Strauss     buffer_copy_string_len(&di->key, k, klen);
309db5ff222SGlenn Strauss     di->value = 0;
310b2991c68SGlenn Strauss     return &di->value;
311b2991c68SGlenn Strauss }
312b2991c68SGlenn Strauss 
31368ec5ad6SGlenn Strauss buffer * array_get_buf_ptr(array * const a, const char * const k, const uint32_t klen) {
314b2991c68SGlenn Strauss     int32_t ipos = array_get_index(a, k, klen);
315e3dc34d1SGlenn Strauss     if (ipos >= 0) return &((data_string *)a->sorted[ipos])->value;
316b2991c68SGlenn Strauss 
317b2991c68SGlenn Strauss     data_string * const ds = array_insert_string_at_pos(a, (uint32_t)(-ipos-1));
318ad9b7e00SGlenn Strauss     buffer_copy_string_len(&ds->key, k, klen);
319601c572cSGlenn Strauss     buffer_clear(&ds->value);
320601c572cSGlenn Strauss     return &ds->value;
321b2991c68SGlenn Strauss }
322b2991c68SGlenn Strauss 
32368ec5ad6SGlenn Strauss void array_insert_value(array * const a, const char * const v, const uint32_t vlen) {
324b2991c68SGlenn Strauss     data_string * const ds = array_insert_string_at_pos(a, a->used);
325ad9b7e00SGlenn Strauss     buffer_clear(&ds->key);
326601c572cSGlenn Strauss     buffer_copy_string_len(&ds->value, v, vlen);
327b2991c68SGlenn Strauss }
328b2991c68SGlenn Strauss 
329b2991c68SGlenn Strauss /* if entry already exists return pointer to existing entry, otherwise insert entry and return NULL */
330b2991c68SGlenn Strauss __attribute_cold__
331b2991c68SGlenn Strauss static data_unset **array_find_or_insert(array * const a, data_unset * const entry) {
332b2991c68SGlenn Strauss     force_assert(NULL != entry);
333b2991c68SGlenn Strauss 
334b2991c68SGlenn Strauss     /* push value onto end of array if there is no key */
335ad9b7e00SGlenn Strauss     if (buffer_is_empty(&entry->key)) {
336b2991c68SGlenn Strauss         array_insert_data_at_pos(a, entry, a->used);
337b2991c68SGlenn Strauss         return NULL;
338b2991c68SGlenn Strauss     }
339b2991c68SGlenn Strauss 
340b2991c68SGlenn Strauss     /* try to find the entry */
341ad9b7e00SGlenn Strauss     const int32_t ipos = array_get_index(a, CONST_BUF_LEN(&entry->key));
342e3dc34d1SGlenn Strauss     if (ipos >= 0) return &a->sorted[ipos];
343b2991c68SGlenn Strauss 
344b2991c68SGlenn Strauss     array_insert_data_at_pos(a, entry, (uint32_t)(-ipos - 1));
3458d8ae9cbSStefan Bühler     return NULL;
3468d8ae9cbSStefan Bühler }
3478d8ae9cbSStefan Bühler 
3488d8ae9cbSStefan Bühler /* replace or insert data (free existing entry) */
349b2991c68SGlenn Strauss void array_replace(array * const a, data_unset * const entry) {
350e3dc34d1SGlenn Strauss     if (NULL == array_find_or_insert(a, entry)) return;
3518d8ae9cbSStefan Bühler 
352e3dc34d1SGlenn Strauss     /* find the entry (array_find_or_insert() returned non-NULL) */
353e3dc34d1SGlenn Strauss     const int32_t ipos = array_get_index(a, CONST_BUF_LEN(&entry->key));
354e3dc34d1SGlenn Strauss     force_assert(ipos >= 0);
355e3dc34d1SGlenn Strauss     data_unset *old = a->sorted[ipos];
356e3dc34d1SGlenn Strauss     force_assert(old != entry);
357e3dc34d1SGlenn Strauss     a->sorted[ipos] = entry;
358e3dc34d1SGlenn Strauss 
359e3dc34d1SGlenn Strauss     uint32_t i = 0;
360e3dc34d1SGlenn Strauss     while (i < a->used && a->data[i] != old) ++i;
361e3dc34d1SGlenn Strauss     force_assert(i != a->used);
362e3dc34d1SGlenn Strauss     a->data[i] = entry;
363e3dc34d1SGlenn Strauss 
364e3dc34d1SGlenn Strauss     old->fn->free(old);
3658d8ae9cbSStefan Bühler }
3668d8ae9cbSStefan Bühler 
367b2991c68SGlenn Strauss void array_insert_unique(array * const a, data_unset * const entry) {
3688d8ae9cbSStefan Bühler 	data_unset **old;
3698d8ae9cbSStefan Bühler 
3708d8ae9cbSStefan Bühler 	if (NULL != (old = array_find_or_insert(a, entry))) {
3718d8ae9cbSStefan Bühler 		force_assert((*old)->type == entry->type);
3728c7f1dfbSGlenn Strauss 		entry->fn->insert_dup(*old, entry);
3738d8ae9cbSStefan Bühler 	}
374bcdc6a3bSJan Kneschke }
375bcdc6a3bSJan Kneschke 
376b2991c68SGlenn Strauss int array_is_vlist(const array * const a) {
377b2991c68SGlenn Strauss 	for (uint32_t i = 0; i < a->used; ++i) {
378bd77abe0SGlenn Strauss 		data_unset *du = a->data[i];
379ad9b7e00SGlenn Strauss 		if (!buffer_is_empty(&du->key) || du->type != TYPE_STRING) return 0;
380bd77abe0SGlenn Strauss 	}
381bd77abe0SGlenn Strauss 	return 1;
382bd77abe0SGlenn Strauss }
383bd77abe0SGlenn Strauss 
384b2991c68SGlenn Strauss int array_is_kvany(const array * const a) {
385b2991c68SGlenn Strauss 	for (uint32_t i = 0; i < a->used; ++i) {
386bd77abe0SGlenn Strauss 		data_unset *du = a->data[i];
387ad9b7e00SGlenn Strauss 		if (buffer_is_empty(&du->key)) return 0;
388bd77abe0SGlenn Strauss 	}
389bd77abe0SGlenn Strauss 	return 1;
390bd77abe0SGlenn Strauss }
391bd77abe0SGlenn Strauss 
392b2991c68SGlenn Strauss int array_is_kvarray(const array * const a) {
393b2991c68SGlenn Strauss 	for (uint32_t i = 0; i < a->used; ++i) {
394bd77abe0SGlenn Strauss 		data_unset *du = a->data[i];
395ad9b7e00SGlenn Strauss 		if (buffer_is_empty(&du->key) || du->type != TYPE_ARRAY) return 0;
396bd77abe0SGlenn Strauss 	}
397bd77abe0SGlenn Strauss 	return 1;
398bd77abe0SGlenn Strauss }
399bd77abe0SGlenn Strauss 
400b2991c68SGlenn Strauss int array_is_kvstring(const array * const a) {
401b2991c68SGlenn Strauss 	for (uint32_t i = 0; i < a->used; ++i) {
402bd77abe0SGlenn Strauss 		data_unset *du = a->data[i];
403ad9b7e00SGlenn Strauss 		if (buffer_is_empty(&du->key) || du->type != TYPE_STRING) return 0;
404bd77abe0SGlenn Strauss 	}
405bd77abe0SGlenn Strauss 	return 1;
406bd77abe0SGlenn Strauss }
407bd77abe0SGlenn Strauss 
408e6741acdSGlenn Strauss /* array_match_*() routines follow very similar pattern, but operate on slightly
409e6741acdSGlenn Strauss  * different data: array key/value, prefix/suffix match, case-insensitive or not
410e6741acdSGlenn Strauss  * While these could be combined into fewer routines with flags to modify the
411e6741acdSGlenn Strauss  * behavior, the interface distinctions are useful to add clarity to the code,
412e6741acdSGlenn Strauss  * and the specialized routines run slightly faster */
413e6741acdSGlenn Strauss 
414e6741acdSGlenn Strauss data_unset *
41568ec5ad6SGlenn Strauss array_match_key_prefix_klen (const array * const a, const char * const s, const uint32_t slen)
416e6741acdSGlenn Strauss {
417b2991c68SGlenn Strauss     for (uint32_t i = 0; i < a->used; ++i) {
418ad9b7e00SGlenn Strauss         const buffer * const key = &a->data[i]->key;
41968ec5ad6SGlenn Strauss         const uint32_t klen = buffer_string_length(key);
420e6741acdSGlenn Strauss         if (klen <= slen && 0 == memcmp(s, key->ptr, klen))
421e6741acdSGlenn Strauss             return a->data[i];
422e6741acdSGlenn Strauss     }
423e6741acdSGlenn Strauss     return NULL;
424e6741acdSGlenn Strauss }
425e6741acdSGlenn Strauss 
426e6741acdSGlenn Strauss data_unset *
42768ec5ad6SGlenn Strauss array_match_key_prefix_nc_klen (const array * const a, const char * const s, const uint32_t slen)
428e6741acdSGlenn Strauss {
429b2991c68SGlenn Strauss     for (uint32_t i = 0; i < a->used; ++i) {
430ad9b7e00SGlenn Strauss         const buffer * const key = &a->data[i]->key;
43168ec5ad6SGlenn Strauss         const uint32_t klen = buffer_string_length(key);
432e20b5318SGlenn Strauss         if (klen <= slen && buffer_eq_icase_ssn(s, key->ptr, klen))
433e6741acdSGlenn Strauss             return a->data[i];
434e6741acdSGlenn Strauss     }
435e6741acdSGlenn Strauss     return NULL;
436e6741acdSGlenn Strauss }
437e6741acdSGlenn Strauss 
438e6741acdSGlenn Strauss data_unset *
439e6741acdSGlenn Strauss array_match_key_prefix (const array * const a, const buffer * const b)
440e6741acdSGlenn Strauss {
44128f1867cSGlenn Strauss   #ifdef __clang_analyzer__
44228f1867cSGlenn Strauss     force_assert(b);
44328f1867cSGlenn Strauss   #endif
444e6741acdSGlenn Strauss     return array_match_key_prefix_klen(a, CONST_BUF_LEN(b));
445e6741acdSGlenn Strauss }
446e6741acdSGlenn Strauss 
447e6741acdSGlenn Strauss data_unset *
448e6741acdSGlenn Strauss array_match_key_prefix_nc (const array * const a, const buffer * const b)
449e6741acdSGlenn Strauss {
450e6741acdSGlenn Strauss     return array_match_key_prefix_nc_klen(a, CONST_BUF_LEN(b));
451e6741acdSGlenn Strauss }
452e6741acdSGlenn Strauss 
453e6741acdSGlenn Strauss const buffer *
454e6741acdSGlenn Strauss array_match_value_prefix (const array * const a, const buffer * const b)
455e6741acdSGlenn Strauss {
45668ec5ad6SGlenn Strauss     const uint32_t blen = buffer_string_length(b);
457e6741acdSGlenn Strauss 
458b2991c68SGlenn Strauss     for (uint32_t i = 0; i < a->used; ++i) {
459601c572cSGlenn Strauss         const buffer * const value = &((data_string *)a->data[i])->value;
46068ec5ad6SGlenn Strauss         const uint32_t vlen = buffer_string_length(value);
461e6741acdSGlenn Strauss         if (vlen <= blen && 0 == memcmp(b->ptr, value->ptr, vlen))
462e6741acdSGlenn Strauss             return value;
463e6741acdSGlenn Strauss     }
464e6741acdSGlenn Strauss     return NULL;
465e6741acdSGlenn Strauss }
466e6741acdSGlenn Strauss 
467e6741acdSGlenn Strauss const buffer *
468e6741acdSGlenn Strauss array_match_value_prefix_nc (const array * const a, const buffer * const b)
469e6741acdSGlenn Strauss {
47068ec5ad6SGlenn Strauss     const uint32_t blen = buffer_string_length(b);
471e6741acdSGlenn Strauss 
472b2991c68SGlenn Strauss     for (uint32_t i = 0; i < a->used; ++i) {
473601c572cSGlenn Strauss         const buffer * const value = &((data_string *)a->data[i])->value;
47468ec5ad6SGlenn Strauss         const uint32_t vlen = buffer_string_length(value);
475e20b5318SGlenn Strauss         if (vlen <= blen && buffer_eq_icase_ssn(b->ptr, value->ptr, vlen))
476e6741acdSGlenn Strauss             return value;
477e6741acdSGlenn Strauss     }
478e6741acdSGlenn Strauss     return NULL;
479e6741acdSGlenn Strauss }
480e6741acdSGlenn Strauss 
481e6741acdSGlenn Strauss data_unset *
482e6741acdSGlenn Strauss array_match_key_suffix (const array * const a, const buffer * const b)
483e6741acdSGlenn Strauss {
48468ec5ad6SGlenn Strauss     const uint32_t blen = buffer_string_length(b);
485e6741acdSGlenn Strauss     const char * const end = b->ptr + blen;
486e6741acdSGlenn Strauss 
487b2991c68SGlenn Strauss     for (uint32_t i = 0; i < a->used; ++i) {
488ad9b7e00SGlenn Strauss         const buffer * const key = &a->data[i]->key;
48968ec5ad6SGlenn Strauss         const uint32_t klen = buffer_string_length(key);
490e6741acdSGlenn Strauss         if (klen <= blen && 0 == memcmp(end - klen, key->ptr, klen))
491e6741acdSGlenn Strauss             return a->data[i];
492e6741acdSGlenn Strauss     }
493e6741acdSGlenn Strauss     return NULL;
494e6741acdSGlenn Strauss }
495e6741acdSGlenn Strauss 
496e6741acdSGlenn Strauss data_unset *
497e6741acdSGlenn Strauss array_match_key_suffix_nc (const array * const a, const buffer * const b)
498e6741acdSGlenn Strauss {
49968ec5ad6SGlenn Strauss     const uint32_t blen = buffer_string_length(b);
500e6741acdSGlenn Strauss     const char * const end = b->ptr + blen;
501e6741acdSGlenn Strauss 
502b2991c68SGlenn Strauss     for (uint32_t i = 0; i < a->used; ++i) {
503ad9b7e00SGlenn Strauss         const buffer * const key = &a->data[i]->key;
50468ec5ad6SGlenn Strauss         const uint32_t klen = buffer_string_length(key);
505e20b5318SGlenn Strauss         if (klen <= blen && buffer_eq_icase_ssn(end - klen, key->ptr, klen))
506e6741acdSGlenn Strauss             return a->data[i];
507e6741acdSGlenn Strauss     }
508e6741acdSGlenn Strauss     return NULL;
509e6741acdSGlenn Strauss }
510e6741acdSGlenn Strauss 
511e6741acdSGlenn Strauss const buffer *
512e6741acdSGlenn Strauss array_match_value_suffix (const array * const a, const buffer * const b)
513e6741acdSGlenn Strauss {
51468ec5ad6SGlenn Strauss     const uint32_t blen = buffer_string_length(b);
515e6741acdSGlenn Strauss     const char * const end = b->ptr + blen;
516e6741acdSGlenn Strauss 
517b2991c68SGlenn Strauss     for (uint32_t i = 0; i < a->used; ++i) {
518601c572cSGlenn Strauss         const buffer * const value = &((data_string *)a->data[i])->value;
51968ec5ad6SGlenn Strauss         const uint32_t vlen = buffer_string_length(value);
520e6741acdSGlenn Strauss         if (vlen <= blen && 0 == memcmp(end - vlen, value->ptr, vlen))
521e6741acdSGlenn Strauss             return value;
522e6741acdSGlenn Strauss     }
523e6741acdSGlenn Strauss     return NULL;
524e6741acdSGlenn Strauss }
525e6741acdSGlenn Strauss 
526e6741acdSGlenn Strauss const buffer *
527e6741acdSGlenn Strauss array_match_value_suffix_nc (const array * const a, const buffer * const b)
528e6741acdSGlenn Strauss {
52968ec5ad6SGlenn Strauss     const uint32_t blen = buffer_string_length(b);
530e6741acdSGlenn Strauss     const char * const end = b->ptr + blen;
531e6741acdSGlenn Strauss 
532b2991c68SGlenn Strauss     for (uint32_t i = 0; i < a->used; ++i) {
533601c572cSGlenn Strauss         const buffer * const value = &((data_string *)a->data[i])->value;
53468ec5ad6SGlenn Strauss         const uint32_t vlen = buffer_string_length(value);
535e20b5318SGlenn Strauss         if (vlen <= blen && buffer_eq_icase_ssn(end - vlen, value->ptr, vlen))
536e6741acdSGlenn Strauss             return value;
537e6741acdSGlenn Strauss     }
538e6741acdSGlenn Strauss     return NULL;
539e6741acdSGlenn Strauss }
540e6741acdSGlenn Strauss 
541e6741acdSGlenn Strauss data_unset *
542e6741acdSGlenn Strauss array_match_path_or_ext (const array * const a, const buffer * const b)
543e6741acdSGlenn Strauss {
54468ec5ad6SGlenn Strauss     const uint32_t blen = buffer_string_length(b);
545e6741acdSGlenn Strauss 
546b2991c68SGlenn Strauss     for (uint32_t i = 0; i < a->used; ++i) {
547e6741acdSGlenn Strauss         /* check extension in the form "^/path" or ".ext$" */
548ad9b7e00SGlenn Strauss         const buffer * const key = &a->data[i]->key;
54968ec5ad6SGlenn Strauss         const uint32_t klen = buffer_string_length(key);
550e6741acdSGlenn Strauss         if (klen <= blen
551e6741acdSGlenn Strauss             && 0 == memcmp((*(key->ptr) == '/' ? b->ptr : b->ptr + blen - klen),
552e6741acdSGlenn Strauss                            key->ptr, klen))
553e6741acdSGlenn Strauss             return a->data[i];
554e6741acdSGlenn Strauss     }
555e6741acdSGlenn Strauss     return NULL;
556e6741acdSGlenn Strauss }
557e6741acdSGlenn Strauss 
558e6741acdSGlenn Strauss 
559e6741acdSGlenn Strauss 
560a46bc4f5SGlenn Strauss 
561a46bc4f5SGlenn Strauss 
562a46bc4f5SGlenn Strauss #include <stdio.h>
563a46bc4f5SGlenn Strauss 
5649e75ba00SJan Kneschke void array_print_indent(int depth) {
5659e75ba00SJan Kneschke 	int i;
5669e75ba00SJan Kneschke 	for (i = 0; i < depth; i ++) {
567dd0abe5aSJan Kneschke 		fprintf(stdout, "    ");
5689e75ba00SJan Kneschke 	}
5699e75ba00SJan Kneschke }
5709e75ba00SJan Kneschke 
57168ec5ad6SGlenn Strauss uint32_t array_get_max_key_length(const array * const a) {
57268ec5ad6SGlenn Strauss 	uint32_t maxlen = 0;
573b2991c68SGlenn Strauss 	for (uint32_t i = 0; i < a->used; ++i) {
574ad9b7e00SGlenn Strauss 		const buffer * const k = &a->data[i]->key;
57568ec5ad6SGlenn Strauss 		uint32_t len = buffer_string_length(k);
5766044dbc5SmOo 
5776044dbc5SmOo 		if (len > maxlen) {
5786044dbc5SmOo 			maxlen = len;
5796044dbc5SmOo 		}
5806044dbc5SmOo 	}
5816044dbc5SmOo 	return maxlen;
5826044dbc5SmOo }
5836044dbc5SmOo 
584b2991c68SGlenn Strauss int array_print(const array * const a, int depth) {
585b2991c68SGlenn Strauss 	uint32_t i;
58668ec5ad6SGlenn Strauss 	uint32_t maxlen;
5876044dbc5SmOo 	int oneline = 1;
588bcdc6a3bSJan Kneschke 
5896044dbc5SmOo 	if (a->used > 5) {
5906044dbc5SmOo 		oneline = 0;
5916044dbc5SmOo 	}
5926044dbc5SmOo 	for (i = 0; i < a->used && oneline; i++) {
5936044dbc5SmOo 		data_unset *du = a->data[i];
594ad9b7e00SGlenn Strauss 		if (!buffer_is_empty(&du->key)) {
5956044dbc5SmOo 			oneline = 0;
5966044dbc5SmOo 			break;
5976044dbc5SmOo 		}
5986044dbc5SmOo 		switch (du->type) {
5996044dbc5SmOo 			case TYPE_INTEGER:
6006044dbc5SmOo 			case TYPE_STRING:
6016044dbc5SmOo 				break;
6026044dbc5SmOo 			default:
6036044dbc5SmOo 				oneline = 0;
6046044dbc5SmOo 				break;
6056044dbc5SmOo 		}
6066044dbc5SmOo 	}
6076044dbc5SmOo 	if (oneline) {
608dd0abe5aSJan Kneschke 		fprintf(stdout, "(");
609bcdc6a3bSJan Kneschke 		for (i = 0; i < a->used; i++) {
6106044dbc5SmOo 			data_unset *du = a->data[i];
6116044dbc5SmOo 			if (i != 0) {
612dd0abe5aSJan Kneschke 				fprintf(stdout, ", ");
6136044dbc5SmOo 			}
6148c7f1dfbSGlenn Strauss 			du->fn->print(du, depth + 1);
6156044dbc5SmOo 		}
616dd0abe5aSJan Kneschke 		fprintf(stdout, ")");
6176044dbc5SmOo 		return 0;
6186044dbc5SmOo 	}
6196044dbc5SmOo 
6206044dbc5SmOo 	maxlen = array_get_max_key_length(a);
621dd0abe5aSJan Kneschke 	fprintf(stdout, "(\n");
6226044dbc5SmOo 	for (i = 0; i < a->used; i++) {
6236044dbc5SmOo 		data_unset *du = a->data[i];
6248b07d57dSJan Kneschke 		array_print_indent(depth + 1);
625ad9b7e00SGlenn Strauss 		if (!buffer_is_empty(&du->key)) {
6266044dbc5SmOo 			int j;
6276044dbc5SmOo 
6286044dbc5SmOo 			if (i && (i % 5) == 0) {
629b2991c68SGlenn Strauss 				fprintf(stdout, "# %u\n", i);
6306044dbc5SmOo 				array_print_indent(depth + 1);
6316044dbc5SmOo 			}
632ad9b7e00SGlenn Strauss 			fprintf(stdout, "\"%s\"", du->key.ptr);
633ad9b7e00SGlenn Strauss 			for (j = maxlen - buffer_string_length(&du->key); j > 0; j--) {
634dd0abe5aSJan Kneschke 				fprintf(stdout, " ");
6356044dbc5SmOo 			}
636dd0abe5aSJan Kneschke 			fprintf(stdout, " => ");
6376044dbc5SmOo 		}
6388c7f1dfbSGlenn Strauss 		du->fn->print(du, depth + 1);
639dd0abe5aSJan Kneschke 		fprintf(stdout, ",\n");
6406044dbc5SmOo 	}
6416044dbc5SmOo 	if (!(i && (i - 1 % 5) == 0)) {
6426044dbc5SmOo 		array_print_indent(depth + 1);
643b2991c68SGlenn Strauss 		fprintf(stdout, "# %u\n", i);
644bcdc6a3bSJan Kneschke 	}
6458b07d57dSJan Kneschke 	array_print_indent(depth);
646dd0abe5aSJan Kneschke 	fprintf(stdout, ")");
647bcdc6a3bSJan Kneschke 
648bcdc6a3bSJan Kneschke 	return 0;
649bcdc6a3bSJan Kneschke }
650