[core] perf: array.c performance enhancements

mark array_get_index() as hot, rewrite to be pure and return sorted pos

mark routines as pure, as appropriate

mark routines as cold if used only at startup for config processing

mark params const, as appropriate

array_get_buf_ptr() for modifiable value buffer after insert into array

uint32_t used and size members instead of size_t

remove a->unique_ndx member; simply add to end of array for value lists
remove du->is_index_key member; simply check buffer_is_empty(du->key)

array_insert_key_value() used to be a hint that lookup could be skipped,
but the state from array_get_index() is now saved and reused internally,
so the distinction is no longer needed.  Use array_set_key_value().
personal/stbuehler/ci-build
Glenn Strauss 2019-10-02 01:54:15 -04:00
parent 2fd0faf1bf
commit b2991c686d
11 changed files with 319 additions and 342 deletions

View File

@ -2,6 +2,7 @@
#include "array.h"
#include "buffer.h"
#include "settings.h" /* BUFFER_MAX_REUSE_SIZE */
#include <string.h>
#include <stdlib.h>
@ -10,83 +11,93 @@
#include <errno.h>
#include <assert.h>
#define ARRAY_NOT_FOUND ((size_t)(-1))
__attribute_cold__
static void array_extend(array * const a) {
a->size += 16;
a->data = realloc(a->data, sizeof(*a->data) * a->size);
a->sorted = realloc(a->sorted, sizeof(*a->sorted) * a->size);
force_assert(a->data);
force_assert(a->sorted);
memset(a->data+a->used, 0, (a->size-a->used)*sizeof(*a->data));
}
array *array_init(void) {
array *a;
a = calloc(1, sizeof(*a));
force_assert(a);
array_extend(a);
return a;
}
array *array_init_array(array *src) {
size_t i;
array *array_init_array(const array * const src) {
array *a = array_init();
if (0 == src->size) return a;
a->used = src->used;
a->size = src->size;
a->unique_ndx = src->unique_ndx;
a->data = malloc(sizeof(*src->data) * src->size);
a->data = calloc(src->size, sizeof(*src->data));
force_assert(NULL != a->data);
for (i = 0; i < src->size; i++) {
if (src->data[i]) a->data[i] = src->data[i]->fn->copy(src->data[i]);
else a->data[i] = NULL;
}
a->sorted = malloc(sizeof(*src->sorted) * src->size);
force_assert(NULL != a->sorted);
memcpy(a->sorted, src->sorted, sizeof(*src->sorted) * src->size);
memcpy(a->sorted, src->sorted, sizeof(*src->sorted) * src->used);
for (uint32_t i = 0; i < src->used; ++i) {
a->data[i] = src->data[i]->fn->copy(src->data[i]);
}
return a;
}
void array_free(array *a) {
size_t i;
void array_free(array * const a) {
if (!a) return;
for (i = 0; i < a->size; i++) {
if (a->data[i]) a->data[i]->fn->free(a->data[i]);
data_unset ** const data = a->data;
const uint32_t sz = a->size;
for (uint32_t i = 0; i < sz; ++i) {
if (data[i]) data[i]->fn->free(data[i]);
}
if (a->data) free(a->data);
if (data) free(data);
if (a->sorted) free(a->sorted);
free(a);
}
void array_reset(array *a) {
size_t i;
void array_reset(array * const a) {
if (!a) return;
for (i = 0; i < a->used; i++) {
a->data[i]->fn->reset(a->data[i]);
a->data[i]->is_index_key = 0;
}
data_unset ** const data = a->data;
const uint32_t used = a->used;
a->used = 0;
a->unique_ndx = 0;
for (uint32_t i = 0; i < used; ++i) {
data[i]->fn->reset(data[i]);
data[i]->is_index_key = 0;
}
}
void array_reset_data_strings(array *a) {
void array_reset_data_strings(array * const a) {
if (!a) return;
for (size_t i = 0; i < a->used; ++i) {
data_string * const ds = (data_string *)a->data[i];
data_string ** const data = (data_string **)a->data;
const uint32_t used = a->used;
a->used = 0;
for (uint32_t i = 0; i < used; ++i) {
data_string * const ds = data[i];
/*force_assert(ds->type == TYPE_STRING);*/
ds->is_index_key = 0;
buffer_reset(ds->key);
buffer_reset(ds->value);
buffer * const k = ds->key;
buffer * const v = ds->value;
buffer_clear(k);
buffer_clear(v);
if (k->size > BUFFER_MAX_REUSE_SIZE) buffer_reset(k);
if (v->size > BUFFER_MAX_REUSE_SIZE) buffer_reset(v);
}
a->used = 0;
a->unique_ndx = 0;
}
data_unset *array_pop(array *a) {
#if 0 /*(unused; see array_extract_element_klen())*/
data_unset *array_pop(array * const a) {
data_unset *du;
force_assert(a->used != 0);
@ -98,6 +109,7 @@ data_unset *array_pop(array *a) {
return du;
}
#endif
__attribute_pure__
static int array_caseless_compare(const char * const a, const char * const b, const size_t len) {
@ -117,72 +129,58 @@ static int array_caseless_compare(const char * const a, const char * const b, co
}
__attribute_pure__
static int array_keycmp(const char *a, size_t alen, const char *b, size_t blen) {
static int array_keycmp(const char * const a, const size_t alen, const char * const b, const size_t blen) {
return alen < blen ? -1 : alen > blen ? 1 : array_caseless_compare(a, b, blen);
}
/* returns index of element or ARRAY_NOT_FOUND
* if rndx != NULL it stores the position in a->sorted[] where the key needs
* to be inserted
/* returns pos into a->sorted[] which contains index to data in a->data[]
* if pos >= 0, or returns -pos-1 if that is the position-1 in a->sorted[]
* where the key needs to be inserted (-1 to avoid -0)
*/
static size_t array_get_index(const array *a, const char *key, size_t keylen, size_t *rndx) {
/* invariant: [lower-1] < key < [upper]
* "virtual elements": [-1] = -INFTY, [a->used] = +INFTY
* also an invariant: 0 <= lower <= upper <= a->used
*/
size_t lower = 0, upper = a->used;
force_assert(upper <= SSIZE_MAX); /* (lower + upper) can't overflow */
while (lower != upper) {
size_t probe = (lower + upper) / 2;
const buffer *b = a->data[a->sorted[probe]]->key;
int cmp = array_keycmp(key, keylen, CONST_BUF_LEN(b));
if (cmp == 0) {
/* found */
if (rndx) *rndx = probe;
return a->sorted[probe];
} else if (cmp < 0) {
/* key < [probe] */
upper = probe; /* still: lower <= upper */
} else {
/* key > [probe] */
lower = probe + 1; /* still: lower <= upper */
}
}
/* not found: [lower-1] < key < [upper] = [lower] ==> insert at [lower] */
if (rndx) *rndx = lower;
return ARRAY_NOT_FOUND;
__attribute_hot__
__attribute_pure__
static int32_t array_get_index(const array * const a, const char * const k, const size_t klen) {
/* invariant: [lower-1] < probe < [upper]
* invariant: 0 <= lower <= upper <= a->used
*/
uint32_t lower = 0, upper = a->used;
while (lower != upper) {
uint32_t probe = (lower + upper) / 2;
const buffer * const b = a->data[a->sorted[probe]]->key;
int cmp = array_keycmp(k, klen, CONST_BUF_LEN(b));
if (cmp < 0) /* key < [probe] */
upper = probe; /* still: lower <= upper */
else if (cmp > 0) /* key > [probe] */
lower = probe + 1; /* still: lower <= upper */
else /*(cmp == 0)*/ /* found */
return (int32_t)probe;
}
/* not found: [lower-1] < key < [upper] = [lower] ==> insert at [lower] */
return -(int)lower - 1;
}
data_unset *array_get_element_klen(const array *a, const char *key, size_t klen) {
size_t ndx;
force_assert(NULL != key);
if (ARRAY_NOT_FOUND != (ndx = array_get_index(a, key, klen, NULL))) {
/* found, return it */
return a->data[ndx];
}
return NULL;
__attribute_hot__
data_unset *array_get_element_klen(const array * const a, const char *key, const size_t klen) {
const int32_t ipos = array_get_index(a, key, klen);
return ipos >= 0 ? a->data[a->sorted[ipos]] : NULL;
}
data_unset *array_extract_element_klen(array *a, const char *key, size_t klen) {
size_t ndx, pos;
force_assert(NULL != key);
data_unset *array_extract_element_klen(array * const a, const char *key, const size_t klen) {
const int32_t ipos = array_get_index(a, key, klen);
if (ipos < 0) return NULL;
if (ARRAY_NOT_FOUND != (ndx = array_get_index(a, key, klen, &pos))) {
{
/* found */
const size_t last_ndx = a->used - 1;
data_unset *entry = a->data[ndx];
const uint32_t last_ndx = a->used - 1;
const uint32_t ndx = a->sorted[ipos], pos = (uint32_t)ipos;
data_unset * const entry = a->data[ndx];
/* now we need to swap it with the last element (if it isn't already the last element) */
if (ndx != last_ndx) {
/* to swap we also need to modify the index in a->sorted - find pos of last_elem there */
size_t last_elem_pos;
int32_t last_elem_pos = array_get_index(a, CONST_BUF_LEN(a->data[last_ndx]->key));
/* last element must be present at the expected position */
force_assert(last_ndx == array_get_index(a, CONST_BUF_LEN(a->data[last_ndx]->key), &last_elem_pos));
force_assert(last_ndx == a->sorted[last_elem_pos]);
/* move entry from last_ndx to ndx */
a->data[ndx] = a->data[last_ndx];
@ -198,135 +196,126 @@ data_unset *array_extract_element_klen(array *a, const char *key, size_t klen) {
if (pos != last_ndx) {
memmove(a->sorted + pos, a->sorted + pos + 1, (last_ndx - pos) * sizeof(*a->sorted));
}
a->sorted[last_ndx] = ARRAY_NOT_FOUND;
--a->used;
return entry;
}
return NULL;
}
static data_unset *array_get_unused_element(array *a, data_type_t t) {
data_unset *ds = NULL;
unsigned int i;
for (i = a->used; i < a->size; i++) {
if (a->data[i] && a->data[i]->type == t) {
ds = a->data[i];
static data_unset *array_get_unused_element(array * const a, const data_type_t t) {
/* After initial startup and config, most array usage is of homogenous types
* and arrays are cleared once per request, so check only the first unused
* element to see if it can be reused */
#if 1
data_unset * const du = (a->used < a->size) ? a->data[a->used] : NULL;
if (NULL != du && du->type == t) {
a->data[a->used] = NULL;/* make empty slot at a->used for next insert */
return du;
}
return NULL;
#else
data_unset ** const data = a->data;
for (uint32_t i = a->used, sz = a->size; i < sz; ++i) {
if (data[i] && data[i]->type == t) {
data_unset * const ds = data[i];
/* make empty slot at a->used for next insert */
a->data[i] = a->data[a->used];
a->data[a->used] = NULL;
data[i] = data[a->used];
data[a->used] = NULL;
return ds;
}
}
return NULL;
#endif
}
void array_set_key_value(array *hdrs, const char *key, size_t key_len, const char *value, size_t val_len) {
data_string *ds;
static void array_insert_data_at_pos(array * const a, data_unset * const entry, const uint32_t pos) {
/* This data structure should not be used for nearly so many entries */
force_assert(a->used + 1 <= INT32_MAX);
if (NULL != (ds = (data_string *)array_get_element_klen(hdrs, key, key_len))) {
buffer_copy_string_len(ds->value, value, val_len);
return;
}
if (a->size == a->used) {
array_extend(a);
}
array_insert_key_value(hdrs, key, key_len, value, val_len);
const uint32_t ndx = a->used++;
data_unset * const prev = a->data[ndx];
a->data[ndx] = entry;
/* move everything one step to the right */
if (pos != ndx) {
memmove(a->sorted + (pos + 1), a->sorted + (pos), (ndx - pos) * sizeof(*a->sorted));
}
a->sorted[pos] = ndx;
if (prev) prev->fn->free(prev); /* free prior data, if any, from slot */
}
void array_insert_key_value(array *hdrs, const char *key, size_t key_len, const char *value, size_t val_len) {
data_string *ds;
if (NULL == (ds = (data_string *)array_get_unused_element(hdrs, TYPE_STRING))) {
ds = data_string_init();
}
buffer_copy_string_len(ds->key, key, key_len);
buffer_copy_string_len(ds->value, value, val_len);
array_insert_unique(hdrs, (data_unset *)ds);
static data_integer * array_insert_integer_at_pos(array * const a, const uint32_t pos) {
#if 0 /*(not currently used by lighttpd in way that reuse would occur)*/
data_integer *di = (data_integer *)array_get_unused_element(a,TYPE_INTEGER);
if (NULL == di) di = data_integer_init();
#else
data_integer * const di = data_integer_init();
#endif
array_insert_data_at_pos(a, (data_unset *)di, pos);
return di;
}
void array_insert_value(array *hdrs, const char *value, size_t val_len) {
data_string *ds;
if (NULL == (ds = (data_string *)array_get_unused_element(hdrs, TYPE_STRING))) {
ds = data_string_init();
}
buffer_copy_string_len(ds->value, value, val_len);
array_insert_unique(hdrs, (data_unset *)ds);
static data_string * array_insert_string_at_pos(array * const a, const uint32_t pos) {
data_string *ds = (data_string *)array_get_unused_element(a, TYPE_STRING);
if (NULL == ds) ds = data_string_init();
array_insert_data_at_pos(a, (data_unset *)ds, pos);
return ds;
}
int * array_get_int_ptr(array *a, const char *k, size_t klen) {
data_integer *di = (data_integer *)array_get_element_klen(a, k, klen);
int * array_get_int_ptr(array * const a, const char * const k, const size_t klen) {
int32_t ipos = array_get_index(a, k, klen);
if (ipos >= 0) return &((data_integer *)a->data[a->sorted[ipos]])->value;
if (NULL == di) {
di = (data_integer *)array_get_unused_element(a, TYPE_INTEGER);
if (NULL == di) di = data_integer_init();
buffer_copy_string_len(di->key, k, klen);
array_insert_unique(a, (data_unset *)di);
}
data_integer * const di =array_insert_integer_at_pos(a,(uint32_t)(-ipos-1));
buffer_copy_string_len(di->key, k, klen);
return &di->value;
}
return &di->value;
buffer * array_get_buf_ptr(array * const a, const char * const k, const size_t klen) {
int32_t ipos = array_get_index(a, k, klen);
if (ipos >= 0) return ((data_string *)a->data[a->sorted[ipos]])->value;
data_string * const ds = array_insert_string_at_pos(a, (uint32_t)(-ipos-1));
buffer_copy_string_len(ds->key, k, klen);
return ds->value;
}
void array_insert_value(array * const a, const char * const v, const size_t vlen) {
data_string * const ds = array_insert_string_at_pos(a, a->used);
buffer_copy_string_len(ds->value, v, vlen);
}
/* if entry already exists return pointer to existing entry, otherwise insert entry and return NULL */
static data_unset **array_find_or_insert(array *a, data_unset *entry) {
size_t ndx, pos, j;
__attribute_cold__
static data_unset **array_find_or_insert(array * const a, data_unset * const entry) {
force_assert(NULL != entry);
/* generate unique index if neccesary */
if (buffer_is_empty(entry->key) || entry->is_index_key) {
buffer_copy_int(entry->key, a->unique_ndx++);
entry->is_index_key = 1;
force_assert(0 != a->unique_ndx); /* must not wrap or we'll get problems */
}
/* push value onto end of array if there is no key */
if (buffer_is_empty(entry->key)) {
array_insert_data_at_pos(a, entry, a->used);
return NULL;
}
/* try to find the entry */
if (ARRAY_NOT_FOUND != (ndx = array_get_index(a, CONST_BUF_LEN(entry->key), &pos))) {
/* found collision, return it */
return &a->data[ndx];
}
/* try to find the entry */
const int32_t ipos = array_get_index(a, CONST_BUF_LEN(entry->key));
if (ipos >= 0) return &a->data[a->sorted[ipos]];
/* insert */
/* there couldn't possibly be enough memory to store so many entries */
force_assert(a->used + 1 <= SSIZE_MAX);
if (a->size == a->used) {
a->size += 16;
a->data = realloc(a->data, sizeof(*a->data) * a->size);
a->sorted = realloc(a->sorted, sizeof(*a->sorted) * a->size);
force_assert(a->data);
force_assert(a->sorted);
for (j = a->used; j < a->size; j++) a->data[j] = NULL;
}
ndx = a->used;
/* make sure there is nothing here */
if (a->data[ndx]) a->data[ndx]->fn->free(a->data[ndx]);
a->data[a->used++] = entry;
/* move everything one step to the right */
if (pos != ndx) {
memmove(a->sorted + (pos + 1), a->sorted + (pos), (ndx - pos) * sizeof(*a->sorted));
}
/* insert */
a->sorted[pos] = ndx;
return NULL;
array_insert_data_at_pos(a, entry, (uint32_t)(-ipos - 1));
return NULL;
}
/* replace or insert data (free existing entry) */
void array_replace(array *a, data_unset *entry) {
void array_replace(array * const a, data_unset * const entry) {
data_unset **old;
force_assert(NULL != entry);
if (NULL != (old = array_find_or_insert(a, entry))) {
force_assert(*old != entry);
(*old)->fn->free(*old);
@ -334,44 +323,43 @@ void array_replace(array *a, data_unset *entry) {
}
}
void array_insert_unique(array *a, data_unset *entry) {
void array_insert_unique(array * const a, data_unset * const entry) {
data_unset **old;
force_assert(NULL != entry);
if (NULL != (old = array_find_or_insert(a, entry))) {
force_assert((*old)->type == entry->type);
entry->fn->insert_dup(*old, entry);
}
}
int array_is_vlist(array *a) {
for (size_t i = 0; i < a->used; ++i) {
int array_is_vlist(const array * const a) {
for (uint32_t i = 0; i < a->used; ++i) {
data_unset *du = a->data[i];
if (!du->is_index_key || du->type != TYPE_STRING) return 0;
if (!buffer_is_empty(du->key) || du->type != TYPE_STRING) return 0;
}
return 1;
}
int array_is_kvany(array *a) {
for (size_t i = 0; i < a->used; ++i) {
int array_is_kvany(const array * const a) {
for (uint32_t i = 0; i < a->used; ++i) {
data_unset *du = a->data[i];
if (du->is_index_key) return 0;
if (buffer_is_empty(du->key)) return 0;
}
return 1;
}
int array_is_kvarray(array *a) {
for (size_t i = 0; i < a->used; ++i) {
int array_is_kvarray(const array * const a) {
for (uint32_t i = 0; i < a->used; ++i) {
data_unset *du = a->data[i];
if (du->is_index_key || du->type != TYPE_ARRAY) return 0;
if (buffer_is_empty(du->key) || du->type != TYPE_ARRAY) return 0;
}
return 1;
}
int array_is_kvstring(array *a) {
for (size_t i = 0; i < a->used; ++i) {
int array_is_kvstring(const array * const a) {
for (uint32_t i = 0; i < a->used; ++i) {
data_unset *du = a->data[i];
if (du->is_index_key || du->type != TYPE_STRING) return 0;
if (buffer_is_empty(du->key) || du->type != TYPE_STRING) return 0;
}
return 1;
}
@ -385,7 +373,7 @@ int array_is_kvstring(array *a) {
data_unset *
array_match_key_prefix_klen (const array * const a, const char * const s, const size_t slen)
{
for (size_t i = 0; i < a->used; ++i) {
for (uint32_t i = 0; i < a->used; ++i) {
const buffer * const key = a->data[i]->key;
const size_t klen = buffer_string_length(key);
if (klen <= slen && 0 == memcmp(s, key->ptr, klen))
@ -397,7 +385,7 @@ array_match_key_prefix_klen (const array * const a, const char * const s, const
data_unset *
array_match_key_prefix_nc_klen (const array * const a, const char * const s, const size_t slen)
{
for (size_t i = 0; i < a->used; ++i) {
for (uint32_t i = 0; i < a->used; ++i) {
const buffer * const key = a->data[i]->key;
const size_t klen = buffer_string_length(key);
if (klen <= slen && buffer_eq_icase_ssn(s, key->ptr, klen))
@ -423,7 +411,7 @@ array_match_value_prefix (const array * const a, const buffer * const b)
{
const size_t blen = buffer_string_length(b);
for (size_t i = 0; i < a->used; ++i) {
for (uint32_t i = 0; i < a->used; ++i) {
const buffer * const value = ((data_string *)a->data[i])->value;
const size_t vlen = buffer_string_length(value);
if (vlen <= blen && 0 == memcmp(b->ptr, value->ptr, vlen))
@ -437,7 +425,7 @@ array_match_value_prefix_nc (const array * const a, const buffer * const b)
{
const size_t blen = buffer_string_length(b);
for (size_t i = 0; i < a->used; ++i) {
for (uint32_t i = 0; i < a->used; ++i) {
const buffer * const value = ((data_string *)a->data[i])->value;
const size_t vlen = buffer_string_length(value);
if (vlen <= blen && buffer_eq_icase_ssn(b->ptr, value->ptr, vlen))
@ -452,7 +440,7 @@ array_match_key_suffix (const array * const a, const buffer * const b)
const size_t blen = buffer_string_length(b);
const char * const end = b->ptr + blen;
for (size_t i = 0; i < a->used; ++i) {
for (uint32_t i = 0; i < a->used; ++i) {
const buffer * const key = a->data[i]->key;
const size_t klen = buffer_string_length(key);
if (klen <= blen && 0 == memcmp(end - klen, key->ptr, klen))
@ -467,7 +455,7 @@ array_match_key_suffix_nc (const array * const a, const buffer * const b)
const size_t blen = buffer_string_length(b);
const char * const end = b->ptr + blen;
for (size_t i = 0; i < a->used; ++i) {
for (uint32_t i = 0; i < a->used; ++i) {
const buffer * const key = a->data[i]->key;
const size_t klen = buffer_string_length(key);
if (klen <= blen && buffer_eq_icase_ssn(end - klen, key->ptr, klen))
@ -482,7 +470,7 @@ array_match_value_suffix (const array * const a, const buffer * const b)
const size_t blen = buffer_string_length(b);
const char * const end = b->ptr + blen;
for (size_t i = 0; i < a->used; ++i) {
for (uint32_t i = 0; i < a->used; ++i) {
const buffer * const value = ((data_string *)a->data[i])->value;
const size_t vlen = buffer_string_length(value);
if (vlen <= blen && 0 == memcmp(end - vlen, value->ptr, vlen))
@ -497,7 +485,7 @@ array_match_value_suffix_nc (const array * const a, const buffer * const b)
const size_t blen = buffer_string_length(b);
const char * const end = b->ptr + blen;
for (size_t i = 0; i < a->used; ++i) {
for (uint32_t i = 0; i < a->used; ++i) {
const buffer * const value = ((data_string *)a->data[i])->value;
const size_t vlen = buffer_string_length(value);
if (vlen <= blen && buffer_eq_icase_ssn(end - vlen, value->ptr, vlen))
@ -511,7 +499,7 @@ array_match_path_or_ext (const array * const a, const buffer * const b)
{
const size_t blen = buffer_string_length(b);
for (size_t i = 0; i < a->used; ++i) {
for (uint32_t i = 0; i < a->used; ++i) {
/* check extension in the form "^/path" or ".ext$" */
const buffer * const key = a->data[i]->key;
const size_t klen = buffer_string_length(key);
@ -536,13 +524,11 @@ void array_print_indent(int depth) {
}
}
size_t array_get_max_key_length(array *a) {
size_t maxlen, i;
maxlen = 0;
for (i = 0; i < a->used; i ++) {
data_unset *du = a->data[i];
size_t len = buffer_string_length(du->key);
size_t array_get_max_key_length(const array * const a) {
size_t maxlen = 0;
for (uint32_t i = 0; i < a->used; ++i) {
const buffer * const k = a->data[i]->key;
size_t len = buffer_string_length(k);
if (len > maxlen) {
maxlen = len;
@ -551,8 +537,8 @@ size_t array_get_max_key_length(array *a) {
return maxlen;
}
int array_print(array *a, int depth) {
size_t i;
int array_print(const array * const a, int depth) {
uint32_t i;
size_t maxlen;
int oneline = 1;
@ -561,7 +547,7 @@ int array_print(array *a, int depth) {
}
for (i = 0; i < a->used && oneline; i++) {
data_unset *du = a->data[i];
if (!du->is_index_key) {
if (!buffer_is_empty(du->key)) {
oneline = 0;
break;
}
@ -592,11 +578,11 @@ int array_print(array *a, int depth) {
for (i = 0; i < a->used; i++) {
data_unset *du = a->data[i];
array_print_indent(depth + 1);
if (!du->is_index_key) {
if (!buffer_is_empty(du->key)) {
int j;
if (i && (i % 5) == 0) {
fprintf(stdout, "# %zu\n", i);
fprintf(stdout, "# %u\n", i);
array_print_indent(depth + 1);
}
fprintf(stdout, "\"%s\"", du->key->ptr);
@ -610,7 +596,7 @@ int array_print(array *a, int depth) {
}
if (!(i && (i - 1 % 5) == 0)) {
array_print_indent(depth + 1);
fprintf(stdout, "# %zu\n", i);
fprintf(stdout, "# %u\n", i);
}
array_print_indent(depth);
fprintf(stdout, ")");

View File

@ -18,7 +18,6 @@ typedef enum { TYPE_UNSET, TYPE_STRING, TYPE_OTHER, TYPE_ARRAY, TYPE_INTEGER, TY
#define DATA_UNSET \
buffer *key; \
data_type_t type; \
int is_index_key; /* 1 if key is a array index (autogenerated keys) */ \
const struct data_methods *fn /* function table */
typedef struct data_unset {
@ -28,12 +27,10 @@ typedef struct data_unset {
typedef struct {
data_unset **data;
size_t *sorted;
uint32_t *sorted;
size_t used; /* <= SSIZE_MAX */
size_t size;
size_t unique_ndx;
uint32_t used; /* <= INT32_MAX */
uint32_t size;
} array;
typedef struct {
@ -61,38 +58,99 @@ typedef struct {
data_integer *data_integer_init(void);
array *array_init(void);
array *array_init_array(array *a);
array *array_init_array(const array *a);
void array_free(array *a);
void array_reset(array *a);
void array_reset_data_strings(array *a);
void array_insert_unique(array *a, data_unset *entry);
data_unset *array_pop(array *a); /* only works on "simple" lists with autogenerated keys */
int array_is_vlist(array *a);
int array_is_kvany(array *a);
int array_is_kvarray(array *a);
int array_is_kvstring(array *a);
int array_print(array *a, int depth);
#define array_get_element(a, key) array_get_element_klen((a), (key), sizeof(key)-1)
data_unset *array_get_element_klen(const array *a, const char *key, size_t klen);
data_unset *array_extract_element_klen(array *a, const char *key, size_t klen); /* removes found entry from array */
void array_set_key_value(array *hdrs, const char *key, size_t key_len, const char *value, size_t val_len);
void array_insert_key_value(array *hdrs, const char *key, size_t key_len, const char *value, size_t val_len);
void array_insert_value(array *hdrs, const char *value, size_t val_len);
int * array_get_int_ptr(array *a, const char *k, size_t klen);
void array_replace(array *a, data_unset *entry);
void array_print_indent(int depth);
size_t array_get_max_key_length(array *a);
__attribute_hot__
void array_reset_data_strings(array *a);
__attribute_cold__
void array_insert_unique(array *a, data_unset *entry);
__attribute_cold__
data_unset *array_pop(array *a); /* only works on "simple" lists with autogenerated keys */
__attribute_cold__
__attribute_pure__
int array_is_vlist(const array *a);
__attribute_cold__
__attribute_pure__
int array_is_kvany(const array *a);
__attribute_cold__
__attribute_pure__
int array_is_kvarray(const array *a);
__attribute_cold__
__attribute_pure__
int array_is_kvstring(const array *a);
#define array_get_element(a, key) array_get_element_klen((a), (key), sizeof(key)-1)
__attribute_pure__
data_unset *array_get_element_klen(const array *a, const char *key, size_t klen);
__attribute_cold__
data_unset *array_extract_element_klen(array *a, const char *key, size_t klen); /* removes found entry from array */
int * array_get_int_ptr(array *a, const char *k, size_t klen);
__attribute_hot__
buffer * array_get_buf_ptr(array *a, const char *k, size_t klen);
void array_insert_value(array *a, const char *v, size_t vlen);
static inline void array_set_key_value(array * const a, const char * const k, const size_t klen, const char * const v, const size_t vlen);
static inline void array_set_key_value(array * const a, const char * const k, const size_t klen, const char * const v, const size_t vlen) {
buffer_copy_string_len(array_get_buf_ptr(a, k, klen), v, vlen);
}
__attribute_cold__
void array_replace(array *a, data_unset *entry);
__attribute_cold__
int array_print(const array *a, int depth);
__attribute_cold__
void array_print_indent(int depth);
__attribute_cold__
__attribute_pure__
size_t array_get_max_key_length(const array *a);
__attribute_pure__
data_unset * array_match_key_prefix_klen (const array * const a, const char * const s, const size_t slen);
__attribute_pure__
data_unset * array_match_key_prefix_nc_klen (const array * const a, const char * const s, const size_t slen);
__attribute_pure__
data_unset * array_match_key_prefix (const array * const a, const buffer * const b);
__attribute_pure__
data_unset * array_match_key_prefix_nc (const array * const a, const buffer * const b);
__attribute_pure__
const buffer * array_match_value_prefix (const array * const a, const buffer * const b);
__attribute_pure__
const buffer * array_match_value_prefix_nc (const array * const a, const buffer * const b);
__attribute_pure__
data_unset * array_match_key_suffix (const array * const a, const buffer * const b);
__attribute_pure__
data_unset * array_match_key_suffix_nc (const array * const a, const buffer * const b);
__attribute_pure__
const buffer * array_match_value_suffix (const array * const a, const buffer * const b);
__attribute_pure__
const buffer * array_match_value_suffix_nc (const array * const a, const buffer * const b);
__attribute_pure__
data_unset * array_match_path_or_ext (const array * const a, const buffer * const b);
#endif

View File

@ -99,7 +99,7 @@ static data_unset *configparser_merge_data(data_unset *op1, const data_unset *op
for (i = 0; i < src->used; i ++) {
du = (data_unset *)src->data[i];
if (du) {
if (du->is_index_key || buffer_is_empty(du->key) || !array_get_element_klen(dst, CONST_BUF_LEN(du->key))) {
if (buffer_is_empty(du->key) || !array_get_element_klen(dst, CONST_BUF_LEN(du->key))) {
array_insert_unique(dst, du->fn->copy(du));
} else {
fprintf(stderr, "Duplicate array-key '%s'\n", du->key->ptr);

View File

@ -9,10 +9,9 @@ static data_unset *data_array_copy(const data_unset *s) {
data_array *src = (data_array *)s;
data_array *ds = data_array_init();
buffer_copy_buffer(ds->key, src->key);
if (!buffer_is_empty(src->key)) buffer_copy_buffer(ds->key, src->key);
array_free(ds->value);
ds->value = array_init_array(src->value);
ds->is_index_key = src->is_index_key;
return (data_unset *)ds;
}

View File

@ -17,7 +17,7 @@ static data_unset *data_config_copy(const data_unset *s) {
data_config *ds = data_config_init();
ds->comp = src->comp;
buffer_copy_buffer(ds->key, src->key);
if (!buffer_is_empty(src->key)) buffer_copy_buffer(ds->key, src->key);
buffer_copy_buffer(ds->comp_tag, src->comp_tag);
buffer_copy_buffer(ds->comp_key, src->comp_key);
array_free(ds->value);

View File

@ -10,8 +10,7 @@ static data_unset *data_integer_copy(const data_unset *s) {
data_integer *src = (data_integer *)s;
data_integer *ds = data_integer_init();
buffer_copy_buffer(ds->key, src->key);
ds->is_index_key = src->is_index_key;
if (!buffer_is_empty(src->key)) buffer_copy_buffer(ds->key, src->key);
ds->value = src->value;
return (data_unset *)ds;
}

View File

@ -10,9 +10,8 @@ static data_unset *data_string_copy(const data_unset *s) {
data_string *src = (data_string *)s;
data_string *ds = data_string_init();
buffer_copy_buffer(ds->key, src->key);
if (!buffer_is_empty(src->key)) buffer_copy_buffer(ds->key, src->key);
buffer_copy_buffer(ds->value, src->value);
ds->is_index_key = src->is_index_key;
return (data_unset *)ds;
}

View File

@ -102,42 +102,24 @@ void http_header_response_set(connection *con, enum http_header_e id, const char
}
void http_header_response_append(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) {
if (vlen) {
data_string *ds= (id <= HTTP_HEADER_OTHER || (con->response.htags & id))
? (data_string *)array_get_element_klen(con->response.headers,k,klen)
: NULL;
if (id > HTTP_HEADER_OTHER) con->response.htags |= id;
if (NULL == ds) {
array_insert_key_value(con->response.headers, k, klen, v, vlen);
}
else { /* append value */
buffer *vb = ds->value;
if (!buffer_string_is_empty(vb))
buffer_append_string_len(vb, CONST_STR_LEN(", "));
buffer_append_string_len(vb, v, vlen);
}
}
if (0 == vlen) return;
if (id > HTTP_HEADER_OTHER) con->response.htags |= id;
buffer * const vb = array_get_buf_ptr(con->response.headers, k, klen);
if (!buffer_string_is_empty(vb)) /* append value */
buffer_append_string_len(vb, CONST_STR_LEN(", "));
buffer_append_string_len(vb, v, vlen);
}
void http_header_response_insert(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) {
if (vlen) {
data_string *ds= (id <= HTTP_HEADER_OTHER || (con->response.htags & id))
? (data_string *)array_get_element_klen(con->response.headers,k,klen)
: NULL;
if (id > HTTP_HEADER_OTHER) con->response.htags |= id;
if (NULL == ds) {
array_insert_key_value(con->response.headers, k, klen, v, vlen);
}
else { /* append value */
buffer *vb = ds->value;
if (!buffer_string_is_empty(vb)) {
buffer_append_string_len(vb, CONST_STR_LEN("\r\n"));
buffer_append_string_len(vb, k, klen);
buffer_append_string_len(vb, CONST_STR_LEN(": "));
}
buffer_append_string_len(vb, v, vlen);
}
if (0 == vlen) return;
if (id > HTTP_HEADER_OTHER) con->response.htags |= id;
buffer * const vb = array_get_buf_ptr(con->response.headers, k, klen);
if (!buffer_string_is_empty(vb)) { /* append value */
buffer_append_string_len(vb, CONST_STR_LEN("\r\n"));
buffer_append_string_len(vb, k, klen);
buffer_append_string_len(vb, CONST_STR_LEN(": "));
}
buffer_append_string_len(vb, v, vlen);
}
@ -167,21 +149,12 @@ void http_header_request_set(connection *con, enum http_header_e id, const char
}
void http_header_request_append(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) {
if (vlen) {
data_string *ds = (id <= HTTP_HEADER_OTHER || (con->request.htags & id))
? (data_string *)array_get_element_klen(con->request.headers, k, klen)
: NULL;
if (id > HTTP_HEADER_OTHER) con->request.htags |= id;
if (NULL == ds) {
array_insert_key_value(con->request.headers, k, klen, v, vlen);
}
else { /* append value */
buffer *vb = ds->value;
if (!buffer_string_is_empty(vb))
buffer_append_string_len(vb, CONST_STR_LEN(", "));
buffer_append_string_len(vb, v, vlen);
}
}
if (0 == vlen) return;
if (id > HTTP_HEADER_OTHER) con->request.htags |= id;
buffer * const vb = array_get_buf_ptr(con->request.headers, k, klen);
if (!buffer_string_is_empty(vb)) /* append value */
buffer_append_string_len(vb, CONST_STR_LEN(", "));
buffer_append_string_len(vb, v, vlen);
}
@ -196,15 +169,10 @@ void http_header_env_set(connection *con, const char *k, size_t klen, const char
}
void http_header_env_append(connection *con, const char *k, size_t klen, const char *v, size_t vlen) {
/*if (vlen)*/ /* skip check; permit env var w/ blank value to be appended */
{
buffer * const vb = http_header_env_get(con, k, klen);
if (NULL == vb) {
array_insert_key_value(con->environment, k, klen, v, vlen);
}
else if (vlen) { /* append value */
buffer_append_string_len(vb, CONST_STR_LEN(", "));
buffer_append_string_len(vb, v, vlen);
}
}
/*if (0 == vlen) return;*//* skip check; permit env var w/ blank value */
buffer * const vb = array_get_buf_ptr(con->environment, k, klen);
if (0 == vlen) return;
if (!buffer_string_is_empty(vb)) /* append value */
buffer_append_string_len(vb, CONST_STR_LEN(", "));
buffer_append_string_len(vb, v, vlen);
}

View File

@ -209,7 +209,7 @@ static void mod_evhost_parse_host(buffer *key, array *host, buffer *authority) {
colon = ptr;
}
ptr = authority->ptr;
array_insert_key_value(host,CONST_STR_LEN("%0"),ptr,colon-ptr);
array_set_key_value(host,CONST_STR_LEN("%0"),ptr,colon-ptr);
return;
}
@ -226,7 +226,7 @@ static void mod_evhost_parse_host(buffer *key, array *host, buffer *authority) {
/* if we stopped at a dot, skip the dot */
if (*ptr == '.') ptr++;
array_insert_key_value(host, CONST_STR_LEN("%0"), ptr, colon-ptr);
array_set_key_value(host, CONST_STR_LEN("%0"), ptr, colon-ptr);
/* if the : is not the start of the authority, go on parsing the hostname */
@ -237,7 +237,7 @@ static void mod_evhost_parse_host(buffer *key, array *host, buffer *authority) {
/* is something between the dots */
buffer_copy_string_len(key, CONST_STR_LEN("%"));
buffer_append_int(key, i++);
array_insert_key_value(host, CONST_BUF_LEN(key), ptr+1, colon-ptr-1);
array_set_key_value(host, CONST_BUF_LEN(key), ptr+1, colon-ptr-1);
}
colon = ptr;
}
@ -247,7 +247,7 @@ static void mod_evhost_parse_host(buffer *key, array *host, buffer *authority) {
if (colon != ptr) {
buffer_copy_string_len(key, CONST_STR_LEN("%"));
buffer_append_int(key, i /* ++ */);
array_insert_key_value(host, CONST_BUF_LEN(key), ptr, colon-ptr);
array_set_key_value(host, CONST_BUF_LEN(key), ptr, colon-ptr);
}
}
}

View File

@ -156,7 +156,7 @@ SETDEFAULTS_FUNC(mod_ssi_set_defaults) {
static int ssi_env_add(void *venv, const char *key, size_t klen, const char *val, size_t vlen) {
array_insert_key_value((array *)venv, key, klen, val, vlen);
array_set_key_value((array *)venv, key, klen, val, vlen);
return 0;
}
@ -701,7 +701,7 @@ static int process_ssi_stmt(server *srv, connection *con, handler_ctx *p, const
if (p->if_is_false) break;
if (key && val) {
array_insert_key_value(p->ssi_vars, key, strlen(key), val, strlen(val));
array_set_key_value(p->ssi_vars, key, strlen(key), val, strlen(val));
} else if (key || val) {
log_error_write(srv, __FILE__, __LINE__, "sSSss",
"ssi: var and value have to be set in <!--#set", l[1], "=", l[2], "-->");

View File

@ -40,37 +40,6 @@ static void test_array_insert_value (void) {
array_free(a);
}
static void test_array_insert_key_value (void) {
data_string *ds;
array *a = array_init();
array_insert_key_value(a, CONST_STR_LEN("abc"), CONST_STR_LEN("alfrag"));
ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("does-not-exist"));
assert(NULL == ds);
ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("abc"));
assert(NULL != ds);
assert(buffer_is_equal_string(ds->key, CONST_STR_LEN("abc")));
assert(buffer_is_equal_string(ds->value, CONST_STR_LEN("alfrag")));
array_insert_key_value(a, CONST_STR_LEN("abc"), CONST_STR_LEN("hameplman"));
ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("does-not-exist"));
assert(NULL == ds);
ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("abc"));
assert(NULL != ds);
assert(buffer_is_equal_string(ds->key, CONST_STR_LEN("abc")));
assert(buffer_is_equal_string(ds->value, CONST_STR_LEN("alfrag, hameplman")));
array_insert_key_value(a, CONST_STR_LEN("123"), CONST_STR_LEN("alfrag"));
ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("does-not-exist"));
assert(NULL == ds);
ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("123"));
assert(NULL != ds);
assert(buffer_is_equal_string(ds->key, CONST_STR_LEN("123")));
assert(buffer_is_equal_string(ds->value, CONST_STR_LEN("alfrag")));
array_free(a);
}
static void test_array_set_key_value (void) {
data_string *ds;
array *a = array_init();
@ -97,7 +66,6 @@ static void test_array_set_key_value (void) {
int main() {
test_array_get_int_ptr();
test_array_insert_value();
test_array_insert_key_value();
test_array_set_key_value();
return 0;