diff options
author | Valentin Bartenev <vbart@nginx.com> | 2020-08-13 02:46:54 +0300 |
---|---|---|
committer | Valentin Bartenev <vbart@nginx.com> | 2020-08-13 02:46:54 +0300 |
commit | 93146616cf56a94fc2979cb978c7b451c5592594 (patch) | |
tree | db840e75ef2c08731e699b4eb44641ad6639c2ca /src/nxt_var.c | |
parent | 21ac95f17e70f2f20fe8e2a99bbe9cc7328a6e62 (diff) | |
download | unit-93146616cf56a94fc2979cb978c7b451c5592594.tar.gz unit-93146616cf56a94fc2979cb978c7b451c5592594.tar.bz2 |
Basic variables support.
Diffstat (limited to '')
-rw-r--r-- | src/nxt_var.c | 616 |
1 files changed, 616 insertions, 0 deletions
diff --git a/src/nxt_var.c b/src/nxt_var.c new file mode 100644 index 00000000..2731fd09 --- /dev/null +++ b/src/nxt_var.c @@ -0,0 +1,616 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_main.h> + + +struct nxt_var_s { + size_t plain; + nxt_uint_t vars; + u_char data[]; + +/* + uint32_t indexes[vars]; + size_t positions[vars]; + u_char chars[plain]; +*/ +}; + + +typedef struct { + nxt_var_t *var; + nxt_str_t *value; +} nxt_var_value_t; + + +struct nxt_var_query_s { + nxt_array_t values; /* of nxt_var_value_t */ + nxt_array_t parts; /* of nxt_str_t * */ + + nxt_lvlhsh_t cache; + + nxt_str_t *spare; + nxt_uint_t waiting; + nxt_uint_t failed; /* 1 bit */ + + void *ctx; + void *data; + + nxt_work_handler_t ready; + nxt_work_handler_t error; +}; + + +#define nxt_var_indexes(var) ((uint32_t *) (var)->data) + +#define nxt_var_positions(var) \ + ((size_t *) ((var)->data + (var)->vars * sizeof(uint32_t))) + +#define nxt_var_plain_start(var) \ + ((var)->data + (var)->vars * (sizeof(uint32_t) + sizeof(size_t))) + + +static nxt_int_t nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data); +static nxt_var_decl_t *nxt_var_hash_find(nxt_str_t *name); + +static nxt_int_t nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data); +static nxt_str_t *nxt_var_cache_find(nxt_lvlhsh_t *lh, uint32_t index); +static nxt_int_t nxt_var_cache_add(nxt_lvlhsh_t *lh, uint32_t index, + nxt_str_t *value, nxt_mp_t *mp); + +static u_char *nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part, + nxt_bool_t *is_var); + +static void nxt_var_query_finish(nxt_task_t *task, nxt_var_query_t *query); + + +static const nxt_lvlhsh_proto_t nxt_var_hash_proto nxt_aligned(64) = { + NXT_LVLHSH_DEFAULT, + nxt_var_hash_test, + nxt_lvlhsh_alloc, + nxt_lvlhsh_free, +}; + +static const nxt_lvlhsh_proto_t nxt_var_cache_proto nxt_aligned(64) = { + NXT_LVLHSH_DEFAULT, + nxt_var_cache_test, + nxt_mp_lvlhsh_alloc, + nxt_mp_lvlhsh_free, +}; + + +static nxt_lvlhsh_t nxt_var_hash; +static uint32_t nxt_var_count; + +static nxt_var_handler_t *nxt_var_index; + + +static nxt_int_t +nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data) +{ + nxt_var_decl_t *decl; + + decl = data; + + return nxt_strstr_eq(&lhq->key, &decl->name) ? NXT_OK : NXT_DECLINED; +} + + +static nxt_var_decl_t * +nxt_var_hash_find(nxt_str_t *name) +{ + nxt_lvlhsh_query_t lhq; + + lhq.key_hash = nxt_djb_hash(name->start, name->length); + lhq.key = *name; + lhq.proto = &nxt_var_hash_proto; + + if (nxt_lvlhsh_find(&nxt_var_hash, &lhq) != NXT_OK) { + return NULL; + } + + return lhq.value; +} + + +static nxt_int_t +nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data) +{ + return NXT_OK; +} + + +static nxt_str_t * +nxt_var_cache_find(nxt_lvlhsh_t *lh, uint32_t index) +{ + nxt_lvlhsh_query_t lhq; + + lhq.key_hash = nxt_murmur_hash2_uint32(&index); + lhq.key.length = sizeof(uint32_t); + lhq.key.start = (u_char *) &index; + lhq.proto = &nxt_var_cache_proto; + + if (nxt_lvlhsh_find(lh, &lhq) != NXT_OK) { + return NULL; + } + + return lhq.value; +} + + +static nxt_int_t +nxt_var_cache_add(nxt_lvlhsh_t *lh, uint32_t index, nxt_str_t *value, + nxt_mp_t *mp) +{ + nxt_lvlhsh_query_t lhq; + + lhq.key_hash = nxt_murmur_hash2_uint32(&index); + lhq.replace = 0; + lhq.key.length = sizeof(uint32_t); + lhq.key.start = (u_char *) &index; + lhq.value = value; + lhq.proto = &nxt_var_cache_proto; + lhq.pool = mp; + + return nxt_lvlhsh_insert(lh, &lhq); +} + + +nxt_int_t +nxt_var_register(nxt_var_decl_t *decl, size_t n) +{ + nxt_uint_t i; + nxt_lvlhsh_query_t lhq; + + lhq.replace = 0; + lhq.proto = &nxt_var_hash_proto; + + for (i = 0; i < n; i++) { + lhq.key = decl[i].name; + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); + lhq.value = &decl[i]; + + if (nxt_slow_path(nxt_lvlhsh_insert(&nxt_var_hash, &lhq) != NXT_OK)) { + return NXT_ERROR; + } + } + + nxt_var_count += n; + + return NXT_OK; +} + + +nxt_int_t +nxt_var_index_init(void) +{ + nxt_uint_t i; + nxt_var_decl_t *decl; + nxt_var_handler_t *index; + nxt_lvlhsh_each_t lhe; + + index = nxt_memalign(64, nxt_var_count * sizeof(nxt_var_handler_t)); + if (index == NULL) { + return NXT_ERROR; + } + + nxt_lvlhsh_each_init(&lhe, &nxt_var_hash_proto); + + for (i = 0; i < nxt_var_count; i++) { + decl = nxt_lvlhsh_each(&nxt_var_hash, &lhe); + decl->index = i; + index[i] = decl->handler; + } + + nxt_var_index = index; + + return NXT_OK; +} + + +nxt_var_t * +nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp) +{ + u_char *p, *end, *plain_pos; + size_t plain, size, *positions; + uint32_t *indexes; + nxt_var_t *var; + nxt_str_t part; + nxt_uint_t n; + nxt_bool_t is_var; + nxt_var_decl_t *decl; + + plain = 0; + n = 0; + + p = str->start; + end = p + str->length; + + while (p < end) { + p = nxt_var_next_part(p, end - p, &part, &is_var); + + if (nxt_slow_path(p == NULL)) { + return NULL; + } + + if (is_var) { + n++; + + } else { + plain += part.length; + } + } + + size = sizeof(nxt_var_t) + + n * (sizeof(nxt_var_handler_t) + sizeof (size_t)) + + plain; + + var = nxt_mp_get(mp, size); + if (nxt_slow_path(var == NULL)) { + return NULL; + } + + var->plain = plain; + var->vars = n; + + indexes = nxt_var_indexes(var); + positions = nxt_var_positions(var); + plain_pos = nxt_var_plain_start(var); + + plain = 0; + n = 0; + + p = str->start; + + while (p < end) { + p = nxt_var_next_part(p, end - p, &part, &is_var); + + if (is_var) { + decl = nxt_var_hash_find(&part); + + if (nxt_slow_path(decl == NULL)) { + return NULL; + } + + indexes[n] = decl->index; + positions[n] = plain; + + n++; + + } else { + plain_pos = nxt_cpymem(plain_pos, part.start, part.length); + plain += part.length; + } + } + + return var; +} + + +nxt_int_t +nxt_var_test(nxt_str_t *str, u_char *error) +{ + u_char *p, *end, *next; + nxt_str_t part; + nxt_bool_t is_var; + nxt_var_decl_t *decl; + + p = str->start; + end = p + str->length; + + while (p < end) { + next = nxt_var_next_part(p, end - p, &part, &is_var); + + if (next == NULL) { + nxt_sprintf(error, error + NXT_MAX_ERROR_STR, + "Invalid variable at position %uz%Z", p - str->start); + + return NXT_ERROR; + } + + if (is_var) { + decl = nxt_var_hash_find(&part); + + if (decl == NULL) { + nxt_sprintf(error, error + NXT_MAX_ERROR_STR, + "Unknown variable \"%V\"%Z", &part); + + return NXT_ERROR; + } + } + + p = next; + } + + return NXT_OK; +} + + +static u_char * +nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part, + nxt_bool_t *is_var) +{ + u_char *p, *end, ch, c; + nxt_bool_t bracket; + + end = start + length; + + p = nxt_memchr(start, '$', length); + + if (p == start) { + *is_var = 1; + + p++; + + if (p == end) { + return NULL; + } + + if (*p == '{') { + bracket = 1; + + if (end - p < 2) { + return NULL; + } + + p++; + + } else { + bracket = 0; + } + + start = p; + + for ( ;; ) { + ch = *p; + + c = (u_char) (ch | 0x20); + if ((c < 'a' || c > 'z') && ch != '_') { + + if (bracket && ch != '}') { + return NULL; + } + + break; + } + + p++; + + if (p == end) { + if (bracket) { + return NULL; + } + + break; + } + } + + length = p - start; + end = p + bracket; + + } else { + *is_var = 0; + + if (p != NULL) { + length = p - start; + end = p; + } + } + + part->length = length; + part->start = start; + + return end; +} + + +nxt_int_t +nxt_var_query_init(nxt_var_query_t **query_p, void *ctx, nxt_mp_t *mp) +{ + nxt_var_query_t *query; + + query = *query_p; + + if (*query_p == NULL) { + query = nxt_mp_zget(mp, sizeof(nxt_var_query_t)); + if (nxt_slow_path(query == NULL)) { + return NXT_ERROR; + } + + nxt_array_init(&query->values, mp, sizeof(nxt_var_value_t)); + nxt_array_init(&query->parts, mp, sizeof(nxt_str_t *)); + + } else { + nxt_array_reset(&query->values); + } + + query->ctx = ctx; + + *query_p = query; + + return NXT_OK; +} + + +void +nxt_var_query(nxt_task_t *task, nxt_var_query_t *query, nxt_var_t *var, + nxt_str_t *str) +{ + uint32_t *indexes; + nxt_mp_t *mp; + nxt_str_t *value; + nxt_int_t ret; + nxt_uint_t i; + nxt_var_value_t *val; + + if (var->vars == 0) { + str->length = var->plain; + str->start = nxt_var_plain_start(var); + return; + } + + if (nxt_slow_path(query->failed)) { + return; + } + + mp = query->values.mem_pool; + indexes = nxt_var_indexes(var); + value = query->spare; + + for (i = 0; i < var->vars; i++) { + + if (value == NULL) { + value = nxt_mp_zget(mp, sizeof(nxt_str_t)); + if (nxt_slow_path(value == NULL)) { + goto fail; + } + } + + ret = nxt_var_cache_add(&query->cache, indexes[i], value, mp); + + if (ret != NXT_OK) { + if (nxt_slow_path(ret == NXT_ERROR)) { + goto fail; + } + + continue; /* NXT_DECLINED */ + } + + ret = nxt_var_index[indexes[i]](task, query, value, query->ctx); + + value = NULL; + + if (ret != NXT_OK) { + if (nxt_slow_path(ret != NXT_AGAIN)) { + goto fail; + } + + query->waiting++; + } + } + + query->spare = value; + + val = nxt_array_add(&query->values); + if (nxt_slow_path(val == NULL)) { + goto fail; + } + + val->var = var; + val->value = str; + + return; + +fail: + + query->failed = 1; +} + + +void +nxt_var_query_resolve(nxt_task_t *task, nxt_var_query_t *query, void *data, + nxt_work_handler_t ready, nxt_work_handler_t error) +{ + query->data = data; + query->ready = ready; + query->error = error; + + if (query->waiting == 0) { + nxt_var_query_finish(task, query); + } +} + + +void +nxt_var_query_handle(nxt_task_t *task, nxt_var_query_t *query, + nxt_bool_t failed) +{ + query->failed |= failed; + + if (--query->waiting == 0) { + nxt_var_query_finish(task, query); + } +} + + +static void +nxt_var_query_finish(nxt_task_t *task, nxt_var_query_t *query) +{ + u_char *p, *src; + size_t length, plain, next, *positions; + uint32_t *indexes; + nxt_str_t *str, **part; + nxt_var_t *var; + nxt_uint_t i, j; + nxt_var_value_t *val; + + if (query->failed) { + goto done; + } + + val = query->values.elts; + + for (i = 0; i < query->values.nelts; i++) { + var = val[i].var; + + length = var->plain; + indexes = nxt_var_indexes(var); + + for (j = 0; j < var->vars; j++) { + str = nxt_var_cache_find(&query->cache, indexes[j]); + + nxt_assert(str != NULL); + + part = nxt_array_add(&query->parts); + + if (nxt_slow_path(part == NULL)) { + query->failed = 1; + goto done; + } + + *part = str; + + length += str->length; + } + + p = nxt_mp_nget(query->values.mem_pool, length); + if (nxt_slow_path(p == NULL)) { + query->failed = 1; + goto done; + } + + val[i].value->length = length; + val[i].value->start = p; + + part = query->parts.elts; + positions = nxt_var_positions(var); + src = nxt_var_plain_start(var); + + plain = 0; + + for (j = 0; j < var->vars; j++) { + next = positions[j]; + + if (next != plain) { + p = nxt_cpymem(p, &src[plain], next - plain); + plain = next; + } + + p = nxt_cpymem(p, part[j]->start, part[j]->length); + } + + if (plain != var->plain) { + nxt_memcpy(p, &src[plain], var->plain - plain); + } + + nxt_array_reset(&query->parts); + } + +done: + + nxt_work_queue_add(&task->thread->engine->fast_work_queue, + query->failed ? query->error : query->ready, + task, query->ctx, query->data); +} |