]> git.kaiwu.me - nginx.git/commitdiff
Core: added data model and JSON serialization libraries.
authorHenry Yuan <h.yuan@f5.com>
Thu, 16 Jul 2026 22:00:32 +0000 (22:00 +0000)
committerhyuan-netizen <h.yuan@f5.com>
Mon, 10 Aug 2026 18:23:36 +0000 (11:23 -0700)
Co-authored-by: Valentin Bartenev <vbart@nginx.com>
Co-authored-by: Ruslan Ermilov <ru@nginx.com>
Co-authored-by: Vladimir Homutov <vl@nginx.com>
Co-authored-by: Evgeny Shirykalov <e.shirykalov@f5.com>
Co-authored-by: Dmitry Plotnikov <d.plotnikov@f5.com>
auto/sources
src/core/ngx_core.h
src/core/ngx_data.c [new file with mode: 0644]
src/core/ngx_data.h [new file with mode: 0644]
src/core/ngx_json.c [new file with mode: 0644]
src/core/ngx_json.h [new file with mode: 0644]

index 2bce9cf8d0a6317beaa6adcf072632edd7c1ab15..52a59d0896b661ec3c28be526d82207eec83f47b 100644 (file)
@@ -41,6 +41,8 @@ CORE_DEPS="src/core/nginx.h \
            src/core/ngx_resolver.h \
            src/core/ngx_open_file_cache.h \
            src/core/ngx_crypt.h \
+           src/core/ngx_data.h \
+           src/core/ngx_json.h \
            src/core/ngx_proxy_protocol.h \
            src/core/ngx_syslog.h"
 
@@ -79,6 +81,8 @@ CORE_SRCS="src/core/nginx.c \
            src/core/ngx_resolver.c \
            src/core/ngx_open_file_cache.c \
            src/core/ngx_crypt.c \
+           src/core/ngx_data.c \
+           src/core/ngx_json.c \
            src/core/ngx_proxy_protocol.c \
            src/core/ngx_syslog.c"
 
index b919d5b31d356fd2aad92cd8967f6504cfdffb6a..d0226ac89d500410563dd02c464f404893e1b3a8 100644 (file)
@@ -79,6 +79,8 @@ typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);
 #include <ngx_times.h>
 #include <ngx_rwlock.h>
 #include <ngx_shmtx.h>
+#include <ngx_data.h>
+#include <ngx_json.h>
 #include <ngx_slab.h>
 #include <ngx_inet.h>
 #include <ngx_cycle.h>
diff --git a/src/core/ngx_data.c b/src/core/ngx_data.c
new file mode 100644 (file)
index 0000000..d3937c1
--- /dev/null
@@ -0,0 +1,312 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+static size_t  ngx_data_sizes[] = {
+    sizeof(ngx_data_obj_t),            /* NGX_DATA_OBJECT_TYPE */
+    sizeof(ngx_data_obj_t),            /* NGX_DATA_LIST_TYPE */
+    sizeof(ngx_str_t),                 /* NGX_DATA_STRING_TYPE */
+    sizeof(int64_t),                   /* NGX_DATA_INTEGER_TYPE */
+    sizeof(ngx_uint_t),                /* NGX_DATA_BOOLEAN_TYPE */
+    0,                                 /* NGX_DATA_NULL_TYPE */
+};
+
+
+ngx_data_item_t *
+ngx_data_new_item(ngx_pool_t *pool, ngx_uint_t type)
+{
+    ngx_data_item_t  *item;
+
+    item = ngx_pcalloc(pool, offsetof(ngx_data_item_t, data)
+                             + ngx_data_sizes[type]);
+    if (item == NULL) {
+        return NULL;
+    }
+
+    item->type = type;
+
+    return item;
+}
+
+
+void
+ngx_data_add_item(ngx_data_item_t *obj, ngx_str_t *name, ngx_data_item_t *item)
+{
+    ngx_data_obj_t  *object;
+
+    if (obj->type != NGX_DATA_OBJECT_TYPE && obj->type != NGX_DATA_LIST_TYPE) {
+        return;
+    }
+
+    object = &obj->data.object;
+
+    if (name) {
+        item->name = *name;
+    }
+
+    if (object->next == NULL) {
+        object->next = &object->item;
+    }
+
+    *object->next = item;
+    object->next = &item->next;
+}
+
+
+ngx_data_item_t *
+ngx_data_obj_handler(uintptr_t data, ngx_pool_t *pool, void *ctx)
+{
+    ngx_data_item_t  *obj, *item;
+    ngx_data_decl_t  *decl;
+
+    obj = ngx_data_new_object(pool);
+    if (obj == NULL) {
+        return NULL;
+    }
+
+    decl = (ngx_data_decl_t *) data;
+
+    while (decl->name.len) {
+
+        item = decl->handler(decl->data, pool, ctx);
+        if (item == NULL) {
+            return NULL;
+        }
+
+        if (item != NGX_DATA_DECLINE) {
+            ngx_data_add_item(obj, &decl->name, item);
+        }
+
+        decl++;
+    }
+
+    return obj;
+}
+
+
+ngx_data_item_t *
+ngx_data_obj_fields_handler(uintptr_t data, ngx_pool_t *pool, void *ctx,
+    ngx_array_t *fields)
+{
+    ngx_str_t        *value;
+    ngx_uint_t        i;
+    ngx_data_item_t  *obj, *item;
+    ngx_data_decl_t  *decl;
+
+    obj = ngx_data_new_object(pool);
+    if (obj == NULL) {
+        return NULL;
+    }
+
+    decl = (ngx_data_decl_t *) data;
+
+    while (decl->name.len) {
+
+        if (fields) {
+            value = fields->elts;
+
+            for (i = 0; i < fields->nelts; i++) {
+
+                if (decl->name.len == value[i].len
+                    && ngx_strncmp(decl->name.data, value[i].data,
+                                   value[i].len)
+                    == 0)
+                {
+                    goto found;
+                }
+            }
+
+            decl++;
+            continue;
+        }
+
+found:
+
+        item = decl->handler(decl->data, pool, ctx);
+        if (item == NULL) {
+            return NULL;
+        }
+
+        if (item != NGX_DATA_DECLINE) {
+            ngx_data_add_item(obj, &decl->name, item);
+        }
+
+        decl++;
+    }
+
+    return obj;
+}
+
+
+ngx_data_item_t *
+ngx_data_number_handler(uintptr_t data, ngx_pool_t *pool, void *ctx)
+{
+    ngx_data_item_t  *item;
+
+    item = ngx_data_new_integer(pool);
+    if (item == NULL) {
+        return NULL;
+    }
+
+    item->data.integer = data;
+
+    return item;
+}
+
+
+ngx_data_item_t *
+ngx_data_string_handler(uintptr_t data, ngx_pool_t *pool, void *ctx)
+{
+    ngx_str_t        *value, *src;
+    ngx_data_item_t  *item;
+
+    item = ngx_data_new_string(pool);
+    if (item == NULL) {
+        return NULL;
+    }
+
+    value = &item->data.string;
+
+    src = (ngx_str_t *) data;
+
+    value->len = src->len;
+
+    value->data = ngx_pstrdup(pool, src);
+    if (value->data == NULL) {
+        return NULL;
+    }
+
+    return item;
+}
+
+
+ngx_data_item_t *
+ngx_data_boolean_handler(uintptr_t data, ngx_pool_t *pool, void *ctx)
+{
+    ngx_data_item_t  *item;
+
+    item = ngx_data_new_boolean(pool);
+    if (item == NULL) {
+        return NULL;
+    }
+
+    item->data.boolean = data;
+
+    return item;
+}
+
+
+ngx_data_item_t *
+ngx_data_time_handler(uintptr_t data, ngx_pool_t *pool, void *ctx)
+{
+    u_char      *p;
+    ngx_tm_t     tm;
+    ngx_str_t    src;
+    ngx_time_t  *tp;
+    u_char       iso8601[sizeof("1974-01-19T13:00:00.000Z") - 1];
+
+    tp = (ngx_time_t *) data;
+
+    ngx_gmtime(tp->sec, &tm);
+
+    p = ngx_sprintf(iso8601, "%4d-%02d-%02dT%02d:%02d:%02d",
+                    tm.ngx_tm_year, tm.ngx_tm_mon, tm.ngx_tm_mday,
+                    tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec);
+
+    if (tp->msec) {
+        p = ngx_sprintf(p, ".%03ui", tp->msec);
+    }
+
+    *p++ = 'Z';
+
+    src.data = iso8601;
+    src.len = p - iso8601;
+
+    return ngx_data_string_handler((uintptr_t) &src, pool, NULL);
+}
+
+
+ngx_data_item_t *
+ngx_data_struct_int64_handler(uintptr_t data, ngx_pool_t *pool, void *ctx)
+{
+    ngx_data_item_t  *item;
+
+    item = ngx_data_new_integer(pool);
+    if (item == NULL) {
+        return NULL;
+    }
+
+    item->data.integer = *(int64_t *) ((u_char *) ctx + data);
+
+    return item;
+}
+
+
+ngx_data_item_t *
+ngx_data_struct_int_handler(uintptr_t data, ngx_pool_t *pool, void *ctx)
+{
+    ngx_int_t  value;
+
+    value = *(ngx_int_t *) ((u_char *) ctx + data);
+
+    return ngx_data_number_handler(value, pool, NULL);
+}
+
+
+ngx_data_item_t *
+ngx_data_struct_atomic_handler(uintptr_t data, ngx_pool_t *pool, void *ctx)
+{
+    ngx_data_item_t  *item;
+
+    item = ngx_data_new_integer(pool);
+    if (item == NULL) {
+        return NULL;
+    }
+
+    item->data.integer = *(ngx_atomic_uint_t *) ((u_char *) ctx + data);
+
+    return item;
+}
+
+
+ngx_data_item_t *
+ngx_data_struct_str_handler(uintptr_t data, ngx_pool_t *pool, void *ctx)
+{
+    ngx_str_t        *value, *src;
+    ngx_data_item_t  *item;
+
+    item = ngx_data_new_string(pool);
+    if (item == NULL) {
+        return NULL;
+    }
+
+    value = &item->data.string;
+
+    src = (ngx_str_t *) ((u_char *) ctx + data);
+
+    value->len = src->len;
+
+    value->data = ngx_pstrdup(pool, src);
+    if (value->data == NULL) {
+        return NULL;
+    }
+
+    return item;
+}
+
+
+ngx_data_item_t *
+ngx_data_struct_boolean_handler(uintptr_t data, ngx_pool_t *pool, void *ctx)
+{
+    ngx_uint_t  value;
+
+    value = *(ngx_uint_t *) ((u_char *) ctx + data);
+
+    return ngx_data_boolean_handler(value, pool, NULL);
+}
diff --git a/src/core/ngx_data.h b/src/core/ngx_data.h
new file mode 100644 (file)
index 0000000..5a74c1d
--- /dev/null
@@ -0,0 +1,101 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_DATA_H_INCLUDED_
+#define _NGX_DATA_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef struct ngx_data_item_s ngx_data_item_t;
+
+
+typedef struct {
+    ngx_data_item_t          *item;
+    ngx_data_item_t         **next;
+} ngx_data_obj_t;
+
+
+struct ngx_data_item_s {
+    ngx_data_item_t          *next;
+    ngx_str_t                 name;
+    ngx_uint_t                type;
+
+    union {
+        ngx_data_obj_t        object;
+        ngx_str_t             string;
+        int64_t               integer;
+        ngx_uint_t            boolean;
+    } data;
+};
+
+
+ngx_data_item_t *ngx_data_new_item(ngx_pool_t *pool, ngx_uint_t type);
+
+void ngx_data_add_item(ngx_data_item_t *obj, ngx_str_t *name,
+    ngx_data_item_t *item);
+
+#define NGX_DATA_OBJECT_TYPE  0
+#define NGX_DATA_LIST_TYPE    1
+#define NGX_DATA_STRING_TYPE  2
+#define NGX_DATA_INTEGER_TYPE 3
+#define NGX_DATA_BOOLEAN_TYPE 4
+#define NGX_DATA_NULL_TYPE    5
+
+
+#define ngx_data_new_object(pool)                                             \
+    ngx_data_new_item(pool, NGX_DATA_OBJECT_TYPE)
+#define ngx_data_new_list(pool)                                               \
+    ngx_data_new_item(pool, NGX_DATA_LIST_TYPE)
+#define ngx_data_new_string(pool)                                             \
+    ngx_data_new_item(pool, NGX_DATA_STRING_TYPE)
+#define ngx_data_new_integer(pool)                                            \
+    ngx_data_new_item(pool, NGX_DATA_INTEGER_TYPE)
+#define ngx_data_new_boolean(pool)                                            \
+    ngx_data_new_item(pool, NGX_DATA_BOOLEAN_TYPE)
+#define ngx_data_new_null(pool)                                               \
+    ngx_data_new_item(pool, NGX_DATA_NULL_TYPE)
+
+
+typedef struct {
+    ngx_str_t            name;
+    ngx_data_item_t   *(*handler)(uintptr_t data, ngx_pool_t *pool, void *ctx);
+    uintptr_t            data;
+} ngx_data_decl_t;
+
+#define ngx_data_null_decl  { ngx_null_string, NULL, 0 }
+
+#define NGX_DATA_DECLINE  (ngx_data_item_t *) -1
+
+
+ngx_data_item_t *ngx_data_obj_handler(uintptr_t data, ngx_pool_t *pool,
+    void *ctx);
+ngx_data_item_t *ngx_data_obj_fields_handler(uintptr_t data, ngx_pool_t *pool,
+    void *ctx, ngx_array_t *fields);
+
+ngx_data_item_t *ngx_data_number_handler(uintptr_t data, ngx_pool_t *pool,
+    void *ctx);
+ngx_data_item_t *ngx_data_string_handler(uintptr_t data, ngx_pool_t *pool,
+    void *ctx);
+ngx_data_item_t *ngx_data_boolean_handler(uintptr_t data, ngx_pool_t *pool,
+    void *ctx);
+ngx_data_item_t *ngx_data_time_handler(uintptr_t data, ngx_pool_t *pool,
+    void *ctx);
+
+ngx_data_item_t *ngx_data_struct_int64_handler(uintptr_t data, ngx_pool_t *pool,
+    void *ctx);
+ngx_data_item_t *ngx_data_struct_int_handler(uintptr_t data, ngx_pool_t *pool,
+    void *ctx);
+ngx_data_item_t *ngx_data_struct_atomic_handler(uintptr_t data,
+    ngx_pool_t *pool, void *ctx);
+ngx_data_item_t *ngx_data_struct_str_handler(uintptr_t data, ngx_pool_t *pool,
+    void *ctx);
+ngx_data_item_t *ngx_data_struct_boolean_handler(uintptr_t data,
+    ngx_pool_t *pool, void *ctx);
+
+#endif /* _NGX_DATA_H_INCLUDED_ */
diff --git a/src/core/ngx_json.c b/src/core/ngx_json.c
new file mode 100644 (file)
index 0000000..f826d43
--- /dev/null
@@ -0,0 +1,317 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef ssize_t   (*ngx_json_length_pt)(ngx_data_item_t *item);
+typedef u_char   *(*ngx_json_encode_pt)(u_char *p, ngx_data_item_t *item);
+
+
+typedef struct {
+    ngx_json_length_pt        length;
+    ngx_json_encode_pt        encode;
+} ngx_json_encode_t;
+
+
+static ssize_t ngx_json_obj_length(ngx_data_item_t *item);
+static ssize_t ngx_json_list_length(ngx_data_item_t *item);
+static ssize_t ngx_json_string_length(ngx_data_item_t *item);
+static ssize_t ngx_json_integer_length(ngx_data_item_t *item);
+static ssize_t ngx_json_boolean_length(ngx_data_item_t *item);
+static ssize_t ngx_json_null_length(ngx_data_item_t *item);
+
+static u_char *ngx_json_obj_encode(u_char *p, ngx_data_item_t *item);
+static u_char *ngx_json_list_encode(u_char *p, ngx_data_item_t *item);
+static u_char *ngx_json_string_encode(u_char *p, ngx_data_item_t *item);
+static u_char *ngx_json_integer_encode(u_char *p, ngx_data_item_t *item);
+static u_char *ngx_json_boolean_encode(u_char *p, ngx_data_item_t *item);
+static u_char *ngx_json_null_encode(u_char *p, ngx_data_item_t *item);
+
+
+static ngx_json_encode_t  ngx_json_encode[] = {
+
+    /* NGX_DATA_OBJECT_TYPE */
+    { ngx_json_obj_length,
+      ngx_json_obj_encode },
+
+    /* NGX_DATA_LIST_TYPE */
+    { ngx_json_list_length,
+      ngx_json_list_encode },
+
+    /* NGX_DATA_STRING_TYPE */
+    { ngx_json_string_length,
+      ngx_json_string_encode },
+
+    /* NGX_DATA_INTEGER_TYPE */
+    { ngx_json_integer_length,
+      ngx_json_integer_encode },
+
+    /* NGX_DATA_BOOLEAN_TYPE */
+    { ngx_json_boolean_length,
+      ngx_json_boolean_encode },
+
+    /* NGX_DATA_NULL_TYPE */
+    { ngx_json_null_length,
+      ngx_json_null_encode },
+};
+
+
+ngx_buf_t *
+ngx_json_render(ngx_pool_t *pool, ngx_data_item_t *item)
+{
+    size_t      length;
+    ngx_buf_t  *buf;
+
+    length = ngx_json_encode[item->type].length(item);
+    if (length == (size_t) NGX_ERROR) {
+        return NULL;
+    }
+
+    buf = ngx_create_temp_buf(pool, length);
+    if (buf == NULL) {
+        return NULL;
+    }
+
+    buf->last = ngx_json_encode[item->type].encode(buf->last, item);
+
+    return buf;
+}
+
+
+static ssize_t
+ngx_json_obj_length(ngx_data_item_t *item)
+{
+    size_t            len, total;
+    ngx_data_obj_t   *obj;
+    ngx_data_item_t  *i;
+
+    obj = &item->data.object;
+    total = sizeof("{}") - 1;
+    i = obj->item;
+
+    if (i) {
+        for ( ;; ) {
+            len = sizeof("\"\":") - 1 + i->name.len
+                  + ngx_escape_json(NULL, i->name.data, i->name.len);
+
+            if (total > NGX_MAX_SIZE_T_VALUE - len) {
+                return NGX_ERROR;
+            }
+
+            total += len;
+
+            len = ngx_json_encode[i->type].length(i);
+            if (len == (size_t) NGX_ERROR) {
+                return NGX_ERROR;
+            }
+
+            if (total > NGX_MAX_SIZE_T_VALUE - len) {
+                return NGX_ERROR;
+            }
+
+            total += len;
+
+            i = i->next;
+
+            if (i == NULL) {
+                break;
+            }
+
+            if (total > NGX_MAX_SIZE_T_VALUE - (sizeof(",") - 1)) {
+                return NGX_ERROR;
+            }
+
+            total += sizeof(",") - 1;
+        }
+    }
+
+    return total;
+}
+
+
+static u_char *
+ngx_json_obj_encode(u_char *p, ngx_data_item_t *item)
+{
+    ngx_data_obj_t   *obj;
+    ngx_data_item_t  *i;
+
+    obj = &item->data.object;
+    i = obj->item;
+
+    *p++ = '{';
+
+    if (i) {
+        for ( ;; ) {
+            *p++ = '"';
+
+            p = (u_char *) ngx_escape_json(p, i->name.data, i->name.len);
+
+            *p++ = '"';
+            *p++ = ':';
+
+            p = ngx_json_encode[i->type].encode(p, i);
+
+            i = i->next;
+
+            if (i == NULL) {
+                break;
+            }
+
+            *p++ = ',';
+        }
+    }
+
+    *p++ = '}';
+
+    return p;
+}
+
+
+static ssize_t
+ngx_json_list_length(ngx_data_item_t *item)
+{
+    size_t            len, total;
+    ngx_data_obj_t   *obj;
+    ngx_data_item_t  *i;
+
+    obj = &item->data.object;
+    total = sizeof("[]") - 1;
+    i = obj->item;
+
+    if (i) {
+        for ( ;; ) {
+            len = ngx_json_encode[i->type].length(i);
+            if (len == (size_t) NGX_ERROR) {
+                return NGX_ERROR;
+            }
+
+            if (total > NGX_MAX_SIZE_T_VALUE - len) {
+                return NGX_ERROR;
+            }
+
+            total += len;
+
+            i = i->next;
+
+            if (i == NULL) {
+                break;
+            }
+
+            if (total > NGX_MAX_SIZE_T_VALUE - (sizeof(",") - 1)) {
+                return NGX_ERROR;
+            }
+
+            total += sizeof(",") - 1;
+        }
+    }
+
+    return total;
+}
+
+
+static u_char *
+ngx_json_list_encode(u_char *p, ngx_data_item_t *item)
+{
+    ngx_data_obj_t   *obj;
+    ngx_data_item_t  *i;
+
+    obj = &item->data.object;
+    i = obj->item;
+
+    *p++ = '[';
+
+    if (i) {
+        for ( ;; ) {
+            p = ngx_json_encode[i->type].encode(p, i);
+
+            i = i->next;
+
+            if (i == NULL) {
+                break;
+            }
+
+            *p++ = ',';
+        }
+    }
+
+    *p++ = ']';
+
+    return p;
+}
+
+
+static ssize_t
+ngx_json_string_length(ngx_data_item_t *item)
+{
+    ngx_str_t  *str;
+
+    str = &item->data.string;
+
+    return sizeof("\"\"") - 1 + str->len
+           + ngx_escape_json(NULL, str->data, str->len);
+}
+
+
+static u_char *
+ngx_json_string_encode(u_char *p, ngx_data_item_t *item)
+{
+    ngx_str_t  *str;
+
+    str = &item->data.string;
+
+    *p++ = '"';
+
+    p = (u_char *) ngx_escape_json(p, str->data, str->len);
+
+    *p++ = '"';
+
+    return p;
+}
+
+
+static ssize_t
+ngx_json_integer_length(ngx_data_item_t *item)
+{
+    return NGX_INT64_LEN;
+}
+
+
+static u_char *
+ngx_json_integer_encode(u_char *p, ngx_data_item_t *item)
+{
+    return ngx_sprintf(p, "%L", item->data.integer);
+}
+
+
+static ssize_t
+ngx_json_boolean_length(ngx_data_item_t *item)
+{
+    return sizeof("false") - 1;
+}
+
+
+static u_char *
+ngx_json_boolean_encode(u_char *p, ngx_data_item_t *item)
+{
+    return item->data.boolean ? ngx_cpymem(p, "true", 4)
+                              : ngx_cpymem(p, "false", 5);
+}
+
+
+static ssize_t
+ngx_json_null_length(ngx_data_item_t *item)
+{
+    return sizeof("null") - 1;
+}
+
+
+static u_char *
+ngx_json_null_encode(u_char *p, ngx_data_item_t *item)
+{
+    return ngx_cpymem(p, "null", 4);
+}
diff --git a/src/core/ngx_json.h b/src/core/ngx_json.h
new file mode 100644 (file)
index 0000000..b94a9df
--- /dev/null
@@ -0,0 +1,18 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_JSON_H_INCLUDED_
+#define _NGX_JSON_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+ngx_buf_t *ngx_json_render(ngx_pool_t *pool, ngx_data_item_t *item);
+
+
+#endif /* _NGX_JSON_H_INCLUDED_ */