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