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