Optimized internal representation of JSON objects and arrays.

This commit is contained in:
Valentin Bartenev
2017-05-23 14:02:37 +03:00
parent c7be5bd6ae
commit 48155f3a49
2 changed files with 267 additions and 179 deletions

View File

@@ -20,6 +20,8 @@ typedef struct {
nxt_conf_json_value_t *nxt_conf_json_value_get(nxt_conf_json_value_t *value, nxt_conf_json_value_t *nxt_conf_json_value_get(nxt_conf_json_value_t *value,
nxt_str_t *path); nxt_str_t *path);
nxt_conf_json_value_t *nxt_conf_json_object_get_member(
nxt_conf_json_value_t *value, u_char *name, size_t length);
nxt_conf_json_value_t *nxt_conf_json_parse(u_char *pos, size_t length, nxt_conf_json_value_t *nxt_conf_json_parse(u_char *pos, size_t length,
nxt_mem_pool_t *pool); nxt_mem_pool_t *pool);
uintptr_t nxt_conf_json_print_value(u_char *pos, nxt_conf_json_value_t *value, uintptr_t nxt_conf_json_print_value(u_char *pos, nxt_conf_json_value_t *value,

View File

@@ -25,6 +25,10 @@ typedef enum {
} nxt_conf_json_type_t; } nxt_conf_json_type_t;
typedef struct nxt_conf_json_array_s nxt_conf_json_array_t;
typedef struct nxt_conf_json_object_s nxt_conf_json_object_t;
struct nxt_conf_json_value_s { struct nxt_conf_json_value_s {
union { union {
uint32_t boolean; /* 1 bit. */ uint32_t boolean; /* 1 bit. */
@@ -32,26 +36,30 @@ struct nxt_conf_json_value_s {
/* double number; */ /* double number; */
u_char str[15]; u_char str[15];
nxt_str_t *string; nxt_str_t *string;
nxt_lvlhsh_t *object; nxt_conf_json_array_t *array;
nxt_array_t *array; nxt_conf_json_object_t *object;
} u; } u;
nxt_conf_json_type_t type:8; /* 3 bits. */ nxt_conf_json_type_t type:8; /* 3 bits. */
}; };
struct nxt_conf_json_array_s {
nxt_uint_t count;
nxt_conf_json_value_t elements[];
};
typedef struct { typedef struct {
nxt_conf_json_value_t name; nxt_conf_json_value_t name;
nxt_conf_json_value_t value; nxt_conf_json_value_t value;
} nxt_conf_json_obj_member_t; } nxt_conf_json_obj_member_t;
static nxt_int_t nxt_conf_json_object_hash_test(nxt_lvlhsh_query_t *lhq, struct nxt_conf_json_object_s {
void *data); nxt_uint_t count;
static nxt_int_t nxt_conf_json_object_member_add(nxt_lvlhsh_t *lvlhsh, nxt_conf_json_obj_member_t members[];
nxt_conf_json_obj_member_t *member, nxt_mem_pool_t *pool); };
static nxt_conf_json_value_t *nxt_conf_json_object_member_get(
nxt_lvlhsh_t *lvlhsh, u_char *name, size_t length);
static u_char *nxt_conf_json_skip_space(u_char *pos, u_char *end); static u_char *nxt_conf_json_skip_space(u_char *pos, u_char *end);
@@ -59,6 +67,10 @@ static u_char *nxt_conf_json_parse_value(u_char *pos, u_char *end,
nxt_conf_json_value_t *value, nxt_mem_pool_t *pool); nxt_conf_json_value_t *value, nxt_mem_pool_t *pool);
static u_char *nxt_conf_json_parse_object(u_char *pos, u_char *end, static u_char *nxt_conf_json_parse_object(u_char *pos, u_char *end,
nxt_conf_json_value_t *value, nxt_mem_pool_t *pool); nxt_conf_json_value_t *value, nxt_mem_pool_t *pool);
static nxt_int_t nxt_conf_json_object_hash_add(nxt_lvlhsh_t *lvlhsh,
nxt_conf_json_obj_member_t *member, nxt_mem_pool_t *pool);
static nxt_int_t nxt_conf_json_object_hash_test(nxt_lvlhsh_query_t *lhq,
void *data);
static u_char *nxt_conf_json_parse_array(u_char *pos, u_char *end, static u_char *nxt_conf_json_parse_array(u_char *pos, u_char *end,
nxt_conf_json_value_t *value, nxt_mem_pool_t *pool); nxt_conf_json_value_t *value, nxt_mem_pool_t *pool);
static u_char *nxt_conf_json_parse_string(u_char *pos, u_char *end, static u_char *nxt_conf_json_parse_string(u_char *pos, u_char *end,
@@ -95,89 +107,6 @@ nxt_conf_json_indentation(u_char *pos, nxt_conf_json_pretty_t *pretty)
} }
static const nxt_lvlhsh_proto_t nxt_conf_json_object_hash_proto
nxt_aligned(64) =
{
NXT_LVLHSH_DEFAULT,
0,
nxt_conf_json_object_hash_test,
nxt_lvlhsh_pool_alloc,
nxt_lvlhsh_pool_free,
};
static nxt_int_t
nxt_conf_json_object_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
{
nxt_conf_json_value_t *name;
name = &((nxt_conf_json_obj_member_t *) data)->name;
if (name->type == NXT_CONF_JSON_SHORT_STRING) {
if (nxt_str_eq(&lhq->key, &name->u.str[1], name->u.str[0])) {
return NXT_OK;
}
} else {
if (nxt_strstr_eq(&lhq->key, name->u.string)) {
return NXT_OK;
}
}
return NXT_DECLINED;
}
static nxt_int_t
nxt_conf_json_object_member_add(nxt_lvlhsh_t *lvlhsh,
nxt_conf_json_obj_member_t *member, nxt_mem_pool_t *pool)
{
nxt_lvlhsh_query_t lhq;
nxt_conf_json_value_t *name;
name = &member->name;
if (name->type == NXT_CONF_JSON_SHORT_STRING) {
lhq.key.length = name->u.str[0];
lhq.key.start = &name->u.str[1];
} else {
lhq.key = *name->u.string;
}
lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
lhq.replace = 0;
lhq.value = member;
lhq.proto = &nxt_conf_json_object_hash_proto;
lhq.pool = pool;
return nxt_lvlhsh_insert(lvlhsh, &lhq);
}
static nxt_conf_json_value_t *
nxt_conf_json_object_member_get(nxt_lvlhsh_t *lvlhsh, u_char *name,
size_t length)
{
nxt_lvlhsh_query_t lhq;
nxt_conf_json_obj_member_t *member;
lhq.key_hash = nxt_djb_hash(name, length);
lhq.key.length = length;
lhq.key.start = name;
lhq.proto = &nxt_conf_json_object_hash_proto;
if (nxt_fast_path(nxt_lvlhsh_find(lvlhsh, &lhq) == NXT_OK)) {
member = lhq.value;
return &member->value;
}
return NULL;
}
nxt_conf_json_value_t * nxt_conf_json_value_t *
nxt_conf_json_value_get(nxt_conf_json_value_t *value, nxt_str_t *path) nxt_conf_json_value_get(nxt_conf_json_value_t *value, nxt_str_t *path)
{ {
@@ -198,12 +127,7 @@ nxt_conf_json_value_get(nxt_conf_json_value_t *value, nxt_str_t *path)
p++; p++;
} }
if (value->type != NXT_CONF_JSON_OBJECT) { value = nxt_conf_json_object_get_member(value, start, p - start);
return NULL;
}
value = nxt_conf_json_object_member_get(value->u.object, start,
p - start);
if (value == NULL) { if (value == NULL) {
return NULL; return NULL;
@@ -214,6 +138,41 @@ nxt_conf_json_value_get(nxt_conf_json_value_t *value, nxt_str_t *path)
} }
nxt_conf_json_value_t *
nxt_conf_json_object_get_member(nxt_conf_json_value_t *value, u_char *name,
size_t length)
{
nxt_str_t str;
nxt_uint_t n;
nxt_conf_json_object_t *object;
nxt_conf_json_obj_member_t *member;
if (value->type != NXT_CONF_JSON_OBJECT) {
return NULL;
}
object = value->u.object;
for (n = 0; n < object->count; n++) {
member = &object->members[n];
if (member->name.type == NXT_CONF_JSON_SHORT_STRING) {
str.length = member->name.u.str[0];
str.start = &member->name.u.str[1];
} else {
str = *member->name.u.string;
}
if (nxt_str_eq(&str, name, length)) {
return &member->value;
}
}
return NULL;
}
nxt_conf_json_value_t * nxt_conf_json_value_t *
nxt_conf_json_parse(u_char *pos, size_t length, nxt_mem_pool_t *pool) nxt_conf_json_parse(u_char *pos, size_t length, nxt_mem_pool_t *pool)
{ {
@@ -330,23 +289,28 @@ nxt_conf_json_parse_value(u_char *pos, u_char *end,
} }
static const nxt_lvlhsh_proto_t nxt_conf_json_object_hash_proto
nxt_aligned(64) =
{
NXT_LVLHSH_DEFAULT,
0,
nxt_conf_json_object_hash_test,
nxt_lvlhsh_pool_alloc,
nxt_lvlhsh_pool_free,
};
static u_char * static u_char *
nxt_conf_json_parse_object(u_char *pos, u_char *end, nxt_conf_json_parse_object(u_char *pos, u_char *end,
nxt_conf_json_value_t *value, nxt_mem_pool_t *pool) nxt_conf_json_value_t *value, nxt_mem_pool_t *pool)
{ {
nxt_int_t rc; nxt_int_t rc;
nxt_lvlhsh_t *object; nxt_uint_t count;
nxt_conf_json_obj_member_t *member; nxt_lvlhsh_t hash;
nxt_mem_pool_t *temp_pool;
object = nxt_mem_alloc(pool, sizeof(nxt_lvlhsh_t)); nxt_lvlhsh_each_t lhe;
if (nxt_slow_path(object == NULL)) { nxt_conf_json_object_t *object;
return NULL; nxt_conf_json_obj_member_t *member, *element;
}
nxt_lvlhsh_init(object);
value->type = NXT_CONF_JSON_OBJECT;
value->u.object = object;
pos = nxt_conf_json_skip_space(pos + 1, end); pos = nxt_conf_json_skip_space(pos + 1, end);
@@ -354,52 +318,64 @@ nxt_conf_json_parse_object(u_char *pos, u_char *end,
return NULL; return NULL;
} }
if (*pos != '}') { temp_pool = nxt_mem_pool_create(256);
if (nxt_slow_path(temp_pool == NULL)) {
for ( ;; ) {
if (*pos != '"') {
return NULL; return NULL;
} }
member = nxt_mem_alloc(pool, sizeof(nxt_conf_json_obj_member_t)); nxt_lvlhsh_init(&hash);
count = 0;
if (*pos != '}') {
for ( ;; ) {
count++;
if (*pos != '"') {
goto error;
}
member = nxt_mem_alloc(temp_pool,
sizeof(nxt_conf_json_obj_member_t));
if (nxt_slow_path(member == NULL)) { if (nxt_slow_path(member == NULL)) {
return NULL; goto error;
} }
pos = nxt_conf_json_parse_string(pos, end, &member->name, pool); pos = nxt_conf_json_parse_string(pos, end, &member->name, pool);
if (nxt_slow_path(pos == NULL)) { if (nxt_slow_path(pos == NULL)) {
return NULL; goto error;
}
rc = nxt_conf_json_object_hash_add(&hash, member, temp_pool);
if (nxt_slow_path(rc != NXT_OK)) {
goto error;
} }
pos = nxt_conf_json_skip_space(pos, end); pos = nxt_conf_json_skip_space(pos, end);
if (nxt_slow_path(pos == end || *pos != ':')) { if (nxt_slow_path(pos == end || *pos != ':')) {
return NULL; goto error;
} }
pos = nxt_conf_json_skip_space(pos + 1, end); pos = nxt_conf_json_skip_space(pos + 1, end);
if (nxt_slow_path(pos == end)) { if (nxt_slow_path(pos == end)) {
return NULL; goto error;
} }
pos = nxt_conf_json_parse_value(pos, end, &member->value, pool); pos = nxt_conf_json_parse_value(pos, end, &member->value, pool);
if (nxt_slow_path(pos == NULL)) { if (nxt_slow_path(pos == NULL)) {
return NULL; goto error;
}
rc = nxt_conf_json_object_member_add(object, member, pool);
if (nxt_slow_path(rc != NXT_OK)) {
return NULL;
} }
pos = nxt_conf_json_skip_space(pos, end); pos = nxt_conf_json_skip_space(pos, end);
if (nxt_slow_path(pos == end)) { if (nxt_slow_path(pos == end)) {
return NULL; goto error;
} }
if (*pos == '}') { if (*pos == '}') {
@@ -407,18 +383,101 @@ nxt_conf_json_parse_object(u_char *pos, u_char *end,
} }
if (nxt_slow_path(*pos != ',')) { if (nxt_slow_path(*pos != ',')) {
return NULL; goto error;
} }
pos = nxt_conf_json_skip_space(pos + 1, end); pos = nxt_conf_json_skip_space(pos + 1, end);
if (nxt_slow_path(pos == end)) { if (nxt_slow_path(pos == end)) {
return NULL; goto error;
} }
} }
} }
object = nxt_mem_alloc(pool, sizeof(nxt_conf_json_object_t)
+ count * sizeof(nxt_conf_json_obj_member_t));
if (nxt_slow_path(object == NULL)) {
return NULL;
}
value->u.object = object;
value->type = NXT_CONF_JSON_OBJECT;
object->count = count;
member = object->members;
nxt_memzero(&lhe, sizeof(nxt_lvlhsh_each_t));
lhe.proto = &nxt_conf_json_object_hash_proto;
for ( ;; ) {
element = nxt_lvlhsh_each(&hash, &lhe);
if (element == NULL) {
break;
}
*member++ = *element;
}
nxt_mem_pool_destroy(temp_pool);
return pos + 1; return pos + 1;
error:
nxt_mem_pool_destroy(temp_pool);
return NULL;
}
static nxt_int_t
nxt_conf_json_object_hash_add(nxt_lvlhsh_t *lvlhsh,
nxt_conf_json_obj_member_t *member, nxt_mem_pool_t *pool)
{
nxt_lvlhsh_query_t lhq;
nxt_conf_json_value_t *name;
name = &member->name;
if (name->type == NXT_CONF_JSON_SHORT_STRING) {
lhq.key.length = name->u.str[0];
lhq.key.start = &name->u.str[1];
} else {
lhq.key = *name->u.string;
}
lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
lhq.replace = 0;
lhq.value = member;
lhq.proto = &nxt_conf_json_object_hash_proto;
lhq.pool = pool;
return nxt_lvlhsh_insert(lvlhsh, &lhq);
}
static nxt_int_t
nxt_conf_json_object_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
{
nxt_str_t str;
nxt_conf_json_value_t *name;
name = &((nxt_conf_json_obj_member_t *) data)->name;
if (name->type == NXT_CONF_JSON_SHORT_STRING) {
str.length = name->u.str[0];
str.start = &name->u.str[1];
} else {
str = *name->u.string;
}
if (nxt_strstr_eq(&lhq->key, &str)) {
return NXT_OK;
}
return NXT_DECLINED;
} }
@@ -426,15 +485,11 @@ static u_char *
nxt_conf_json_parse_array(u_char *pos, u_char *end, nxt_conf_json_parse_array(u_char *pos, u_char *end,
nxt_conf_json_value_t *value, nxt_mem_pool_t *pool) nxt_conf_json_value_t *value, nxt_mem_pool_t *pool)
{ {
nxt_array_t *array; nxt_uint_t count;
nxt_list_t *list;
array = nxt_array_create(pool, 8, sizeof(nxt_conf_json_value_t)); nxt_mem_pool_t *temp_pool;
if (nxt_slow_path(array == NULL)) { nxt_conf_json_array_t *array;
return NULL; nxt_conf_json_value_t *element;
}
value->type = NXT_CONF_JSON_ARRAY;
value->u.array = array;
pos = nxt_conf_json_skip_space(pos + 1, end); pos = nxt_conf_json_skip_space(pos + 1, end);
@@ -442,24 +497,38 @@ nxt_conf_json_parse_array(u_char *pos, u_char *end,
return NULL; return NULL;
} }
if (*pos != ']') { temp_pool = nxt_mem_pool_create(256);
if (nxt_slow_path(temp_pool == NULL)) {
for ( ;; ) {
value = nxt_array_add(array);
if (nxt_slow_path(value == NULL)) {
return NULL; return NULL;
} }
pos = nxt_conf_json_parse_value(pos, end, value, pool); list = nxt_list_create(temp_pool, 8, sizeof(nxt_conf_json_value_t));
if (nxt_slow_path(list == NULL)) {
goto error;
}
count = 0;
if (*pos != ']') {
for ( ;; ) {
count++;
element = nxt_list_add(list);
if (nxt_slow_path(element == NULL)) {
goto error;
}
pos = nxt_conf_json_parse_value(pos, end, element, pool);
if (nxt_slow_path(pos == NULL)) { if (nxt_slow_path(pos == NULL)) {
return NULL; goto error;
} }
pos = nxt_conf_json_skip_space(pos, end); pos = nxt_conf_json_skip_space(pos, end);
if (nxt_slow_path(pos == end)) { if (nxt_slow_path(pos == end)) {
return NULL; goto error;
} }
if (*pos == ']') { if (*pos == ']') {
@@ -467,18 +536,41 @@ nxt_conf_json_parse_array(u_char *pos, u_char *end,
} }
if (nxt_slow_path(*pos != ',')) { if (nxt_slow_path(*pos != ',')) {
return NULL; goto error;
} }
pos = nxt_conf_json_skip_space(pos + 1, end); pos = nxt_conf_json_skip_space(pos + 1, end);
if (nxt_slow_path(pos == end)) { if (nxt_slow_path(pos == end)) {
return NULL; goto error;
} }
} }
} }
array = nxt_mem_alloc(pool, sizeof(nxt_conf_json_array_t)
+ count * sizeof(nxt_conf_json_value_t));
if (nxt_slow_path(array == NULL)) {
goto error;
}
value->u.array = array;
value->type = NXT_CONF_JSON_ARRAY;
array->count = count;
element = array->elements;
nxt_list_each(value, list) {
*element++ = *value;
} nxt_list_loop;
nxt_mem_pool_destroy(temp_pool);
return pos + 1; return pos + 1;
error:
nxt_mem_pool_destroy(temp_pool);
return NULL;
} }
@@ -944,8 +1036,8 @@ nxt_conf_json_print_array(u_char *pos, nxt_conf_json_value_t *value,
nxt_conf_json_pretty_t *pretty) nxt_conf_json_pretty_t *pretty)
{ {
size_t len; size_t len;
uint32_t n; nxt_uint_t n;
nxt_array_t *array; nxt_conf_json_array_t *array;
array = value->u.array; array = value->u.array;
@@ -957,9 +1049,9 @@ nxt_conf_json_print_array(u_char *pos, nxt_conf_json_value_t *value,
pretty->level++; pretty->level++;
} }
value = array->elts; value = array->elements;
for (n = 0; n < array->nelts; n++) { for (n = 0; n < array->count; n++) {
len += nxt_conf_json_print_value(NULL, &value[n], pretty); len += nxt_conf_json_print_value(NULL, &value[n], pretty);
if (pretty != NULL) { if (pretty != NULL) {
@@ -983,8 +1075,8 @@ nxt_conf_json_print_array(u_char *pos, nxt_conf_json_value_t *value,
*pos++ = '['; *pos++ = '[';
if (array->nelts != 0) { if (array->count != 0) {
value = array->elts; value = array->elements;
if (pretty != NULL) { if (pretty != NULL) {
pos = nxt_conf_json_newline(pos); pos = nxt_conf_json_newline(pos);
@@ -995,7 +1087,7 @@ nxt_conf_json_print_array(u_char *pos, nxt_conf_json_value_t *value,
pos = (u_char *) nxt_conf_json_print_value(pos, &value[0], pretty); pos = (u_char *) nxt_conf_json_print_value(pos, &value[0], pretty);
for (n = 1; n < array->nelts; n++) { for (n = 1; n < array->count; n++) {
*pos++ = ','; *pos++ = ',';
if (pretty != NULL) { if (pretty != NULL) {
@@ -1029,14 +1121,10 @@ nxt_conf_json_print_object(u_char *pos, nxt_conf_json_value_t *value,
nxt_conf_json_pretty_t *pretty) nxt_conf_json_pretty_t *pretty)
{ {
size_t len; size_t len;
nxt_lvlhsh_t *object; nxt_uint_t n;
nxt_lvlhsh_each_t lhe; nxt_conf_json_object_t *object;
nxt_conf_json_obj_member_t *member; nxt_conf_json_obj_member_t *member;
nxt_memzero(&lhe, sizeof(nxt_lvlhsh_each_t));
lhe.proto = &nxt_conf_json_object_hash_proto;
object = value->u.object; object = value->u.object;
if (pos == NULL) { if (pos == NULL) {
@@ -1047,15 +1135,11 @@ nxt_conf_json_print_object(u_char *pos, nxt_conf_json_value_t *value,
pretty->level++; pretty->level++;
} }
for ( ;; ) { member = object->members;
member = nxt_lvlhsh_each(object, &lhe);
if (member == NULL) { for (n = 0; n < object->count; n++) {
break; len += nxt_conf_json_print_string(NULL, &member[n].name) + 1
} + nxt_conf_json_print_value(NULL, &member[n].value, pretty)
len += nxt_conf_json_print_string(NULL, &member->name) + 1
+ nxt_conf_json_print_value(NULL, &member->value, pretty)
+ 1; + 1;
if (pretty != NULL) { if (pretty != NULL) {
@@ -1079,21 +1163,23 @@ nxt_conf_json_print_object(u_char *pos, nxt_conf_json_value_t *value,
*pos++ = '{'; *pos++ = '{';
member = nxt_lvlhsh_each(object, &lhe); if (object->count != 0) {
if (member != NULL) {
if (pretty != NULL) { if (pretty != NULL) {
pos = nxt_conf_json_newline(pos); pos = nxt_conf_json_newline(pos);
pretty->level++; pretty->level++;
} }
member = object->members;
n = 0;
for ( ;; ) { for ( ;; ) {
if (pretty != NULL) { if (pretty != NULL) {
pos = nxt_conf_json_indentation(pos, pretty); pos = nxt_conf_json_indentation(pos, pretty);
} }
pos = (u_char *) nxt_conf_json_print_string(pos, &member->name); pos = (u_char *) nxt_conf_json_print_string(pos, &member[n].name);
*pos++ = ':'; *pos++ = ':';
@@ -1101,12 +1187,12 @@ nxt_conf_json_print_object(u_char *pos, nxt_conf_json_value_t *value,
*pos++ = ' '; *pos++ = ' ';
} }
pos = (u_char *) nxt_conf_json_print_value(pos, &member->value, pos = (u_char *) nxt_conf_json_print_value(pos, &member[n].value,
pretty); pretty);
member = nxt_lvlhsh_each(object, &lhe); n++;
if (member == NULL) { if (n == object->count) {
break; break;
} }