summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_var.c
diff options
context:
space:
mode:
authorValentin Bartenev <vbart@nginx.com>2020-08-13 02:46:54 +0300
committerValentin Bartenev <vbart@nginx.com>2020-08-13 02:46:54 +0300
commit93146616cf56a94fc2979cb978c7b451c5592594 (patch)
treedb840e75ef2c08731e699b4eb44641ad6639c2ca /src/nxt_var.c
parent21ac95f17e70f2f20fe8e2a99bbe9cc7328a6e62 (diff)
downloadunit-93146616cf56a94fc2979cb978c7b451c5592594.tar.gz
unit-93146616cf56a94fc2979cb978c7b451c5592594.tar.bz2
Basic variables support.
Diffstat (limited to 'src/nxt_var.c')
-rw-r--r--src/nxt_var.c616
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);
+}