]> git.kaiwu.me - njs.git/commitdiff
Introduced generic string builder API.
authorDmitry Volyntsev <xeioex@nginx.com>
Wed, 11 Dec 2019 12:30:26 +0000 (15:30 +0300)
committerDmitry Volyntsev <xeioex@nginx.com>
Wed, 11 Dec 2019 12:30:26 +0000 (15:30 +0300)
auto/sources
src/njs_chb.c [new file with mode: 0644]
src/njs_chb.h [new file with mode: 0644]
src/njs_json.c
src/njs_main.h
src/njs_vm.c
src/test/njs_unit_test.c

index bd182eba1b5926e8ca075e2d927b5b9bed0ce2b1..c289fe394ead2bd0d385841b67d4dd0c2d742ea6 100644 (file)
@@ -20,6 +20,7 @@ NJS_LIB_SRCS=" \
    src/njs_malloc.c \
    src/njs_mp.c \
    src/njs_sprintf.c \
+   src/njs_chb.c \
    src/njs_value.c \
    src/njs_vm.c \
    src/njs_vmcode.c \
diff --git a/src/njs_chb.c b/src/njs_chb.c
new file mode 100644 (file)
index 0000000..f3680d4
--- /dev/null
@@ -0,0 +1,242 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <njs_main.h>
+
+
+#define NJS_CHB_MIN_SIZE       256
+
+
+void
+njs_chb_append0(njs_chb_t *chain, const char *msg, size_t len)
+{
+    u_char  *p;
+
+    if (len != 0 && !chain->error) {
+        p = njs_chb_reserve(chain, len);
+        if (njs_slow_path(p == NULL)) {
+            return;
+        }
+
+        memcpy(p, msg, len);
+
+        njs_chb_written(chain, len);
+    }
+}
+
+
+u_char *
+njs_chb_reserve(njs_chb_t *chain, size_t size)
+{
+    njs_chb_node_t  *n;
+
+    if (njs_slow_path(size == 0)) {
+        return NULL;
+    }
+
+    n = chain->last;
+
+    if (njs_fast_path(n != NULL && njs_chb_node_room(n) >= size)) {
+        return n->pos;
+    }
+
+    if (size < NJS_CHB_MIN_SIZE) {
+        size = NJS_CHB_MIN_SIZE;
+    }
+
+    n = njs_mp_alloc(chain->pool, sizeof(njs_chb_node_t) + size);
+    if (njs_slow_path(n == NULL)) {
+        chain->error = 1;
+        return NULL;
+    }
+
+    n->next = NULL;
+    n->start = (u_char *) n + sizeof(njs_chb_node_t);
+    n->pos = n->start;
+    n->end = n->pos + size;
+
+    if (chain->last != NULL) {
+        chain->last->next = n;
+
+    } else {
+        chain->nodes = n;
+    }
+
+    chain->last = n;
+
+    return n->start;
+}
+
+
+void
+njs_chb_vsprintf(njs_chb_t *chain, size_t size, const char *fmt, va_list args)
+{
+    u_char  *start, *end;
+
+    start = njs_chb_reserve(chain, size);
+    if (njs_slow_path(start == NULL)) {
+        return;
+    }
+
+    end = njs_vsprintf(start, start + size, fmt, args);
+
+    njs_chb_written(chain, end - start);
+}
+
+
+void
+njs_chb_sprintf(njs_chb_t *chain, size_t size, const char* fmt, ...)
+{
+    va_list  args;
+
+    va_start(args, fmt);
+
+    njs_chb_vsprintf(chain, size, fmt, args);
+
+    va_end(args);
+}
+
+
+/*
+ * Drains size bytes from the beginning of the chain.
+ */
+void
+njs_chb_drain(njs_chb_t *chain, size_t drain)
+{
+    njs_chb_node_t  *n;
+
+    n = chain->nodes;
+
+    while (n != NULL) {
+        if (njs_chb_node_size(n) > drain) {
+            n->start += drain;
+            return;
+        }
+
+        drain -= njs_chb_node_size(n);
+        chain->nodes = n->next;
+
+        njs_mp_free(chain->pool, n);
+        n = chain->nodes;
+    }
+
+    chain->last = NULL;
+}
+
+
+/*
+ * Drops size bytes from the end of the chain.
+ */
+void
+njs_chb_drop(njs_chb_t *chain, size_t drop)
+{
+    size_t          size;
+    njs_chb_node_t  *n, *next;
+
+    n = chain->last;
+
+    if (njs_fast_path(n != NULL && (njs_chb_node_size(n) > drop))) {
+        n->pos -= drop;
+        return;
+    }
+
+    n = chain->nodes;
+    size = njs_chb_size(chain);
+
+    if (drop >= size) {
+        njs_chb_destroy(chain);
+        njs_chb_init(chain, chain->pool);
+        return;
+    }
+
+    while (n != NULL) {
+        size -= njs_chb_node_size(n);
+
+        if (size <= drop) {
+            chain->last = n;
+            chain->last->pos -= drop - size;
+
+            n = chain->last->next;
+            chain->last->next = NULL;
+
+            break;
+        }
+
+        n = n->next;
+    }
+
+    while (n != NULL) {
+        next = n->next;
+        njs_mp_free(chain->pool, n);
+        n = next;
+    }
+}
+
+
+njs_int_t
+njs_chb_join(njs_chb_t *chain, njs_str_t *str)
+{
+    u_char          *start;
+    size_t          size;
+    njs_chb_node_t  *n;
+
+    if (chain->error) {
+        return NJS_DECLINED;
+    }
+
+    n = chain->nodes;
+
+    if (n == NULL) {
+        str->length = 0;
+        str->start = NULL;
+        return NJS_OK;
+    }
+
+    size = njs_chb_size(chain);
+
+    start = njs_mp_alloc(chain->pool, size);
+    if (njs_slow_path(start == NULL)) {
+        return NJS_ERROR;
+    }
+
+    n = chain->nodes;
+    str->length = size;
+    str->start = start;
+
+    njs_chb_join_to(chain, start);
+
+    return NJS_OK;
+}
+
+
+void
+njs_chb_join_to(njs_chb_t *chain, u_char *dst)
+{
+    njs_chb_node_t  *n;
+
+    n = chain->nodes;
+
+    while (n != NULL) {
+        dst = njs_cpymem(dst, n->start, njs_chb_node_size(n));
+        n = n->next;
+    }
+}
+
+
+void
+njs_chb_destroy(njs_chb_t *chain)
+{
+    njs_chb_node_t  *n, *next;
+
+    n = chain->nodes;
+
+    while (n != NULL) {
+        next = n->next;
+        njs_mp_free(chain->pool, n);
+        n = next;
+    }
+}
diff --git a/src/njs_chb.h b/src/njs_chb.h
new file mode 100644 (file)
index 0000000..065f628
--- /dev/null
@@ -0,0 +1,114 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_CHB_H_INCLUDED_
+#define _NJS_CHB_H_INCLUDED_
+
+
+typedef struct njs_chb_node_s njs_chb_node_t;
+
+struct njs_chb_node_s {
+    njs_chb_node_t          *next;
+    u_char                  *start;
+    u_char                  *pos;
+    u_char                  *end;
+};
+
+typedef struct {
+    njs_bool_t              error;
+    njs_mp_t                *pool;
+    njs_chb_node_t          *nodes;
+    njs_chb_node_t          *last;
+} njs_chb_t;
+
+
+void njs_chb_append0(njs_chb_t *chain, const char *msg, size_t len);
+void njs_chb_vsprintf(njs_chb_t *chain, size_t size, const char *fmt,
+    va_list args);
+void njs_chb_sprintf(njs_chb_t *chain, size_t size, const char* fmt, ...);
+u_char *njs_chb_reserve(njs_chb_t *chain, size_t size);
+void njs_chb_drain(njs_chb_t *chain, size_t drop);
+void njs_chb_drop(njs_chb_t *chain, size_t drop);
+njs_int_t njs_chb_join(njs_chb_t *chain, njs_str_t *str);
+void njs_chb_join_to(njs_chb_t *chain, u_char *dst);
+void njs_chb_destroy(njs_chb_t *chain);
+
+
+#define njs_chb_append(chain, msg, len)                                      \
+    njs_chb_append0(chain, (const char *) msg, len)
+
+#define njs_chb_append_literal(chain, literal)                               \
+    njs_chb_append0(chain, literal, njs_length(literal))
+
+#define njs_chb_append_str(chain, str)                                       \
+    njs_chb_append0(chain, (const char *) (str)->start, (str)->length)
+
+
+#define njs_chb_node_size(n) (size_t) ((n)->pos - (n)->start)
+#define njs_chb_node_room(n) (size_t) ((n)->end - (n)->pos)
+
+
+njs_inline void
+njs_chb_init(njs_chb_t *chain, njs_mp_t *pool)
+{
+    chain->error = 0;
+    chain->pool = pool;
+    chain->nodes = NULL;
+    chain->last = NULL;
+}
+
+
+njs_inline size_t
+njs_chb_size(njs_chb_t *chain)
+{
+    size_t          size;
+    njs_chb_node_t  *n;
+
+    n = chain->nodes;
+
+    size = 0;
+
+    while (n != NULL) {
+        size += njs_chb_node_size(n);
+        n = n->next;
+    }
+
+    return size;
+}
+
+
+njs_inline ssize_t
+njs_chb_utf8_length(njs_chb_t *chain)
+{
+    ssize_t         len, length;
+    njs_chb_node_t  *n;
+
+    n = chain->nodes;
+
+    length = 0;
+
+    while (n != NULL) {
+        len = njs_utf8_length(n->start, njs_chb_node_size(n));
+        if (njs_slow_path(len < 0)) {
+            return len;
+        }
+
+        length += len;
+        n = n->next;
+    }
+
+    return length;
+}
+
+
+njs_inline void
+njs_chb_written(njs_chb_t *chain, size_t bytes)
+{
+    chain->last->pos += bytes;
+}
+
+
+#endif /* _NJS_JSON_H_INCLUDED_ */
index 82d5df5fb7505aa9b97c273c63f25ff8bb1e3fba..e740070fb555c374d4567aec6cd08f38ab353f70 100644 (file)
@@ -44,23 +44,10 @@ typedef struct {
 } njs_json_parse_t;
 
 
-typedef struct njs_chb_node_s njs_chb_node_t;
-
-struct njs_chb_node_s {
-    njs_chb_node_t             *next;
-    u_char                     *start;
-    u_char                     *pos;
-    u_char                     *end;
-};
-
-
 typedef struct {
     njs_value_t                retval;
 
     njs_vm_t                   *vm;
-    njs_mp_t                   *pool;
-    njs_chb_node_t             *nodes;
-    njs_chb_node_t             *last;
 
     njs_uint_t                 depth;
     njs_json_state_t           states[NJS_JSON_MAX_DEPTH];
@@ -104,33 +91,15 @@ static njs_int_t njs_json_stringify_replacer(njs_json_stringify_t* stringify,
 static njs_int_t njs_json_stringify_array(njs_vm_t *vm,
     njs_json_stringify_t *stringify);
 
-static njs_int_t njs_json_append_value(njs_json_stringify_t *stringify,
-    const njs_value_t *value);
-static njs_int_t njs_json_append_string(njs_json_stringify_t *stringify,
-    const njs_value_t *value, char quote);
-static njs_int_t njs_json_append_number(njs_json_stringify_t *stringify,
-    const njs_value_t *value);
+static void njs_json_append_value(njs_chb_t *chain, const njs_value_t *value);
+static void njs_json_append_string(njs_chb_t *chain, const njs_value_t *value,
+    char quote);
+static void njs_json_append_number(njs_chb_t *chain, const njs_value_t *value);
 
 static njs_object_t *njs_json_wrap_value(njs_vm_t *vm, njs_value_t *wrapper,
     const njs_value_t *value);
 
 
-#define NJS_JSON_BUF_MIN_SIZE       128
-
-#define njs_json_buf_written(stringify, bytes)                              \
-    (stringify)->last->pos += (bytes);
-
-#define njs_json_buf_node_size(n) (size_t) ((n)->pos - (n)->start)
-#define njs_json_buf_node_room(n) (size_t) ((n)->end - (n)->pos)
-
-static njs_int_t njs_json_buf_append(njs_json_stringify_t *stringify,
-    const char *msg, size_t len);
-static u_char *njs_json_buf_reserve(njs_json_stringify_t *stringify,
-    size_t size);
-static njs_int_t njs_json_buf_pullup(njs_json_stringify_t *stringify,
-    njs_str_t *str);
-
-
 static const njs_object_prop_t  njs_json_object_properties[];
 
 
@@ -224,10 +193,7 @@ njs_json_stringify(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     stringify = &json_stringify;
 
     stringify->vm = vm;
-    stringify->pool = vm->mem_pool;
     stringify->depth = 0;
-    stringify->nodes = NULL;
-    stringify->last = NULL;
     stringify->keys_type = NJS_ENUM_STRING;
 
     replacer = njs_arg(args, nargs, 2);
@@ -1129,43 +1095,24 @@ njs_json_pop_stringify_state(njs_json_stringify_t *stringify)
      || ((value)->type >= NJS_REGEXP))
 
 
-#define njs_json_stringify_append(str, len)                                   \
-    ret = njs_json_buf_append(stringify, (char *) str, len);                  \
-    if (ret != NJS_OK) {                                                      \
-        goto memory_error;                                                    \
-    }
-
-
 #define njs_json_stringify_indent(times)                                      \
     if (stringify->space.length != 0) {                                       \
-        njs_json_stringify_append("\n", 1);                                   \
+        njs_chb_append(&chain,"\n", 1);                                       \
         for (i = 0; i < (njs_int_t) (times) - 1; i++) {                       \
-            njs_json_stringify_append(stringify->space.start,                 \
-                                      stringify->space.length);               \
+            njs_chb_append_str(&chain, &stringify->space);                    \
         }                                                                     \
     }
 
-#define njs_json_stringify_append_value(value)                                \
-    ret = njs_json_append_value(stringify, value);                            \
-    if (njs_slow_path(ret != NJS_OK)) {                                       \
-        if (ret == NJS_DECLINED) {                                            \
-            return NJS_ERROR;                                                 \
-        }                                                                     \
-                                                                              \
-        goto memory_error;                                                    \
-    }
-
-
 static njs_int_t
 njs_json_stringify_iterator(njs_vm_t *vm, njs_json_stringify_t *stringify,
     njs_value_t *object)
 {
-    u_char            *start;
+    u_char            *p;
     size_t            size;
+    njs_chb_t         chain;
     ssize_t           length;
     njs_int_t         i;
     njs_int_t         ret;
-    njs_str_t         str;
     njs_value_t       *key, *value, wrapper;
     njs_object_t      *obj;
     njs_json_state_t  *state;
@@ -1180,17 +1127,19 @@ njs_json_stringify_iterator(njs_vm_t *vm, njs_json_stringify_t *stringify,
         goto memory_error;
     }
 
+    njs_chb_init(&chain, vm->mem_pool);
+
     for ( ;; ) {
         switch (state->type) {
         case NJS_JSON_OBJECT:
             if (state->index == 0) {
-                njs_json_stringify_append("{", 1);
+                njs_chb_append_literal(&chain,"{");
                 njs_json_stringify_indent(stringify->depth);
             }
 
             if (state->index >= state->keys->length) {
                 njs_json_stringify_indent(stringify->depth - 1);
-                njs_json_stringify_append("}", 1);
+                njs_chb_append_literal(&chain,"}");
 
                 state = njs_json_pop_stringify_state(stringify);
                 if (state == NULL) {
@@ -1226,15 +1175,15 @@ njs_json_stringify_iterator(njs_vm_t *vm, njs_json_stringify_t *stringify,
             }
 
             if (state->written) {
-                njs_json_stringify_append(",", 1);
+                njs_chb_append_literal(&chain,",");
                 njs_json_stringify_indent(stringify->depth);
             }
 
             state->written = 1;
-            njs_json_append_string(stringify, key, '\"');
-            njs_json_stringify_append(":", 1);
+            njs_json_append_string(&chain, key, '\"');
+            njs_chb_append_literal(&chain,":");
             if (stringify->space.length != 0) {
-                njs_json_stringify_append(" ", 1);
+                njs_chb_append_literal(&chain," ");
             }
 
             if (njs_json_is_object(value)) {
@@ -1246,19 +1195,19 @@ njs_json_stringify_iterator(njs_vm_t *vm, njs_json_stringify_t *stringify,
                 break;
             }
 
-            njs_json_stringify_append_value(value);
+            njs_json_append_value(&chain, value);
 
             break;
 
         case NJS_JSON_ARRAY:
             if (state->index == 0) {
-                njs_json_stringify_append("[", 1);
+                njs_chb_append_literal(&chain,"[");
                 njs_json_stringify_indent(stringify->depth);
             }
 
             if (state->index >= njs_array_len(&state->value)) {
                 njs_json_stringify_indent(stringify->depth - 1);
-                njs_json_stringify_append("]", 1);
+                njs_chb_append_literal(&chain,"]");
 
                 state = njs_json_pop_stringify_state(stringify);
                 if (state == NULL) {
@@ -1269,7 +1218,7 @@ njs_json_stringify_iterator(njs_vm_t *vm, njs_json_stringify_t *stringify,
             }
 
             if (state->written) {
-                njs_json_stringify_append(",", 1);
+                njs_chb_append_literal(&chain,",");
                 njs_json_stringify_indent(stringify->depth);
             }
 
@@ -1296,7 +1245,7 @@ njs_json_stringify_iterator(njs_vm_t *vm, njs_json_stringify_t *stringify,
             }
 
             state->written = 1;
-            njs_json_stringify_append_value(value);
+            njs_json_append_value(&chain, value);
 
             break;
         }
@@ -1304,43 +1253,40 @@ njs_json_stringify_iterator(njs_vm_t *vm, njs_json_stringify_t *stringify,
 
 done:
 
-    ret = njs_json_buf_pullup(stringify, &str);
-    if (njs_slow_path(ret != NJS_OK)) {
-        goto memory_error;
-    }
-
     /*
      * The value to stringify is wrapped as '{"": value}'.
-     * An empty object means empty result.
+     * Stripping the wrapper's data.
      */
-    if (str.length <= njs_length("{\n\n}")) {
-        njs_set_undefined(&vm->retval);
-        goto release;
-    }
-
-    /* Stripping the wrapper's data. */
 
-    start = str.start + njs_length("{\"\":");
-    size = str.length - njs_length("{\"\":}");
+    njs_chb_drain(&chain, njs_length("{\"\":"));
+    njs_chb_drop(&chain, njs_length("}"));
 
     if (stringify->space.length != 0) {
-        start += njs_length("\n ");
-        size -= njs_length("\n \n");
+        njs_chb_drain(&chain, njs_length("\n "));
+        njs_chb_drop(&chain, njs_length("\n"));
     }
 
-    length = njs_utf8_length(start, size);
+    size = njs_chb_size(&chain);
+    if (njs_slow_path(size == 0)) {
+        njs_set_undefined(&vm->retval);
+        goto release;
+    }
+
+    length = njs_chb_utf8_length(&chain);
     if (njs_slow_path(length < 0)) {
         length = 0;
     }
 
-    ret = njs_string_new(vm, &vm->retval, start, size, length);
-    if (njs_slow_path(ret != NJS_OK)) {
+    p = njs_string_alloc(vm, &vm->retval, size, length);
+    if (njs_slow_path(p == NULL)) {
         goto memory_error;
     }
 
+    njs_chb_join_to(&chain, p);
+
 release:
 
-    njs_mp_free(vm->mem_pool, str.start);
+    njs_chb_destroy(&chain);
 
     return NJS_OK;
 
@@ -1521,8 +1467,8 @@ njs_json_stringify_array(njs_vm_t *vm, njs_json_stringify_t  *stringify)
 }
 
 
-static njs_int_t
-njs_json_append_value(njs_json_stringify_t *stringify, const njs_value_t *value)
+static void
+njs_json_append_value(njs_chb_t *chain, const njs_value_t *value)
 {
     switch (value->type) {
     case NJS_OBJECT_STRING:
@@ -1530,14 +1476,16 @@ njs_json_append_value(njs_json_stringify_t *stringify, const njs_value_t *value)
         /* Fall through. */
 
     case NJS_STRING:
-        return njs_json_append_string(stringify, value, '\"');
+        njs_json_append_string(chain, value, '\"');
+        break;
 
     case NJS_OBJECT_NUMBER:
         value = njs_object_value(value);
         /* Fall through. */
 
     case NJS_NUMBER:
-        return njs_json_append_number(stringify, value);
+        njs_json_append_number(chain, value);
+        break;
 
     case NJS_OBJECT_BOOLEAN:
         value = njs_object_value(value);
@@ -1545,26 +1493,27 @@ njs_json_append_value(njs_json_stringify_t *stringify, const njs_value_t *value)
 
     case NJS_BOOLEAN:
         if (njs_is_true(value)) {
-            return njs_json_buf_append(stringify, "true", 4);
+            njs_chb_append_literal(chain, "true");
 
         } else {
-            return njs_json_buf_append(stringify, "false", 5);
+            njs_chb_append_literal(chain, "false");
         }
 
+        break;
+
     case NJS_UNDEFINED:
     case NJS_NULL:
     case NJS_SYMBOL:
     case NJS_INVALID:
     case NJS_FUNCTION:
     default:
-        return njs_json_buf_append(stringify, "null", 4);
+        njs_chb_append_literal(chain, "null");
     }
 }
 
 
-static njs_int_t
-njs_json_append_string(njs_json_stringify_t *stringify,
-    const njs_value_t *value, char quote)
+static void
+njs_json_append_string(njs_chb_t *chain, const njs_value_t *value, char quote)
 {
     u_char             c, *dst, *dst_end;
     size_t             length;
@@ -1580,12 +1529,12 @@ njs_json_append_string(njs_json_stringify_t *stringify,
     end = p + str.size;
     length = str.length;
 
-    dst = njs_json_buf_reserve(stringify, 64);
+    dst = njs_chb_reserve(chain, length + 2);
     if (njs_slow_path(dst == NULL)) {
-        return NJS_ERROR;
+        return;
     }
 
-    dst_end = dst + 64;
+    dst_end = dst + length + 2;
 
     *dst++ = quote;
 
@@ -1651,26 +1600,25 @@ njs_json_append_string(njs_json_stringify_t *stringify,
         }
 
         if (dst_end - dst <= 6) {
-            njs_json_buf_written(stringify, dst - stringify->last->pos);
+            njs_chb_written(chain, dst - chain->last->pos);
 
-            dst = njs_json_buf_reserve(stringify, 64);
+            dst = njs_chb_reserve(chain, 64);
             if (njs_slow_path(dst == NULL)) {
-                return NJS_ERROR;
+                return;
             }
 
             dst_end = dst + 64;
         }
     }
 
-    njs_json_buf_written(stringify, dst - stringify->last->pos);
+    njs_chb_written(chain, dst - chain->last->pos);
 
-    return njs_json_buf_append(stringify, &quote, 1);
+    njs_chb_append(chain, &quote, 1);
 }
 
 
-static njs_int_t
-njs_json_append_number(njs_json_stringify_t *stringify,
-    const njs_value_t *value)
+static void
+njs_json_append_number(njs_chb_t *chain, const njs_value_t *value)
 {
     u_char  *p;
     size_t  size;
@@ -1679,20 +1627,18 @@ njs_json_append_number(njs_json_stringify_t *stringify,
     num = njs_number(value);
 
     if (isnan(num) || isinf(num)) {
-        return njs_json_buf_append(stringify, "null", 4);
+        njs_chb_append_literal(chain, "null");
 
     } else {
-        p = njs_json_buf_reserve(stringify, 64);
+        p = njs_chb_reserve(chain, 64);
         if (njs_slow_path(p == NULL)) {
-            return NJS_ERROR;
+            return;
         }
 
         size = njs_dtoa(num, (char *) p);
 
-        njs_json_buf_written(stringify, size);
+        njs_chb_written(chain, size);
     }
-
-    return NJS_OK;
 }
 
 
@@ -1737,117 +1683,6 @@ njs_json_wrap_value(njs_vm_t *vm, njs_value_t *wrapper,
 }
 
 
-static njs_int_t
-njs_json_buf_append(njs_json_stringify_t *stringify, const char *msg,
-    size_t len)
-{
-    u_char  *p;
-
-    if (len != 0) {
-        p = njs_json_buf_reserve(stringify, len);
-        if (njs_slow_path(p == NULL)) {
-            return NJS_ERROR;
-        }
-
-        memcpy(p, msg, len);
-
-        njs_json_buf_written(stringify, len);
-    }
-
-    return NJS_OK;
-}
-
-
-static u_char *
-njs_json_buf_reserve(njs_json_stringify_t *stringify, size_t size)
-{
-    njs_chb_node_t  *n;
-
-    if (njs_slow_path(size == 0)) {
-        return NULL;
-    }
-
-    n = stringify->last;
-
-    if (njs_fast_path(n != NULL && njs_json_buf_node_room(n) >= size)) {
-        return n->pos;
-    }
-
-    if (size < NJS_JSON_BUF_MIN_SIZE) {
-        size = NJS_JSON_BUF_MIN_SIZE;
-    }
-
-    n = njs_mp_alloc(stringify->pool, sizeof(njs_chb_node_t) + size);
-    if (njs_slow_path(n == NULL)) {
-        return NULL;
-    }
-
-    n->next = NULL;
-    n->start = (u_char *) n + sizeof(njs_chb_node_t);
-    n->pos = n->start;
-    n->end = n->pos + size;
-
-    if (stringify->last != NULL) {
-        stringify->last->next = n;
-
-    } else {
-        stringify->nodes = n;
-    }
-
-    stringify->last = n;
-
-    return n->start;
-}
-
-
-static njs_int_t
-njs_json_buf_pullup(njs_json_stringify_t *stringify, njs_str_t *str)
-{
-    u_char          *start;
-    size_t          size;
-    njs_chb_node_t  *n;
-
-    n = stringify->nodes;
-
-    if (n == NULL) {
-        str->length = 0;
-        str->start = NULL;
-        return NJS_OK;
-    }
-
-    if (n->next == NULL) {
-        str->length = njs_json_buf_node_size(n);
-        str->start = n->start;
-        return NJS_OK;
-    }
-
-    size = 0;
-
-    while (n != NULL) {
-        size += njs_json_buf_node_size(n);
-        n = n->next;
-    }
-
-    start = njs_mp_alloc(stringify->pool, size);
-    if (njs_slow_path(start == NULL)) {
-        return NJS_ERROR;
-    }
-
-    n = stringify->nodes;
-    str->length = size;
-    str->start = start;
-
-    while (n != NULL) {
-        size = njs_json_buf_node_size(n);
-        memcpy(start, n->start, size);
-        start += size;
-        n = n->next;
-    }
-
-    return NJS_OK;
-}
-
-
 static const njs_object_prop_t  njs_json_object_properties[] =
 {
     {
@@ -1881,22 +1716,13 @@ const njs_object_init_t  njs_json_object_init = {
 };
 
 
-#define njs_dump(str)                                                         \
-    ret = njs_json_buf_append(stringify, str, njs_length(str));               \
-    if (njs_slow_path(ret != NJS_OK)) {                                       \
-        goto memory_error;                                                    \
-    }
-
-
 static njs_int_t
-njs_dump_value(njs_json_stringify_t *stringify, const njs_value_t *value,
-    njs_uint_t console)
+njs_dump_value(njs_json_stringify_t *stringify, njs_chb_t *chain,
+    const njs_value_t *value, njs_uint_t console)
 {
-    u_char       *p;
-    njs_int_t    ret;
     njs_str_t    str;
+    njs_int_t    ret;
     njs_value_t  str_val;
-    u_char       buf[32];
 
     njs_int_t   (*to_string)(njs_vm_t *, njs_value_t *, const njs_value_t *);
 
@@ -1904,26 +1730,20 @@ njs_dump_value(njs_json_stringify_t *stringify, const njs_value_t *value,
     case NJS_OBJECT_STRING:
         value = njs_object_value(value);
 
-        njs_string_get(value, &str);
-
-        njs_dump("[String: ");
-
-        ret = njs_json_append_string(stringify, value, '\'');
-        if (njs_slow_path(ret != NJS_OK)) {
-            goto memory_error;
-        }
-
-        njs_dump("]");
+        njs_chb_append_literal(chain, "[String: ");
+        njs_json_append_string(chain, value, '\'');
+        njs_chb_append_literal(chain, "]");
         break;
 
     case NJS_STRING:
         njs_string_get(value, &str);
 
         if (!console || stringify->depth != 0) {
-            return njs_json_append_string(stringify, value, '\'');
+            njs_json_append_string(chain, value, '\'');
+            return NJS_OK;
         }
 
-        return njs_json_buf_append(stringify, (char *) str.start, str.length);
+        njs_chb_append_str(chain, &str);
 
         break;
 
@@ -1936,15 +1756,7 @@ njs_dump_value(njs_json_stringify_t *stringify, const njs_value_t *value,
         }
 
         njs_string_get(&str_val, &str);
-
-        njs_dump("[Symbol: ");
-
-        ret = njs_json_buf_append(stringify, (char *) str.start, str.length);
-        if (njs_slow_path(ret != NJS_OK)) {
-            goto memory_error;
-        }
-
-        njs_dump("]");
+        njs_chb_sprintf(chain, 16 + str.length, "[Symbol: %V]", &str);
 
         break;
 
@@ -1955,11 +1767,7 @@ njs_dump_value(njs_json_stringify_t *stringify, const njs_value_t *value,
         }
 
         njs_string_get(&str_val, &str);
-
-        ret = njs_json_buf_append(stringify, (char *) str.start, str.length);
-        if (njs_slow_path(ret != NJS_OK)) {
-            goto memory_error;
-        }
+        njs_chb_append_str(chain, &str);
 
         break;
 
@@ -1970,7 +1778,7 @@ njs_dump_value(njs_json_stringify_t *stringify, const njs_value_t *value,
                           && signbit(njs_number(value))))
         {
 
-            njs_dump("[Number: -0]");
+            njs_chb_append_literal(chain, "[Number: -0]");
             break;
         }
 
@@ -1980,15 +1788,7 @@ njs_dump_value(njs_json_stringify_t *stringify, const njs_value_t *value,
         }
 
         njs_string_get(&str_val, &str);
-
-        njs_dump("[Number: ");
-
-        ret = njs_json_buf_append(stringify, (char *) str.start, str.length);
-        if (njs_slow_path(ret != NJS_OK)) {
-            goto memory_error;
-        }
-
-        njs_dump("]");
+        njs_chb_sprintf(chain, 16 + str.length, "[Number: %V]", &str);
 
         break;
 
@@ -1996,42 +1796,42 @@ njs_dump_value(njs_json_stringify_t *stringify, const njs_value_t *value,
         value = njs_object_value(value);
 
         if (njs_is_true(value)) {
-            njs_dump("[Boolean: true]");
+            njs_chb_append_literal(chain, "[Boolean: true]");
 
         } else {
-            njs_dump("[Boolean: false]");
+            njs_chb_append_literal(chain, "[Boolean: false]");
         }
 
         break;
 
     case NJS_BOOLEAN:
         if (njs_is_true(value)) {
-            njs_dump("true");
+            njs_chb_append_literal(chain, "true");
 
         } else {
-            njs_dump("false");
+            njs_chb_append_literal(chain, "false");
         }
 
         break;
 
     case NJS_UNDEFINED:
-        njs_dump("undefined");
+        njs_chb_append_literal(chain, "undefined");
         break;
 
     case NJS_NULL:
-        njs_dump("null");
+        njs_chb_append_literal(chain, "null");
         break;
 
     case NJS_INVALID:
-        njs_dump("<empty>");
+        njs_chb_append_literal(chain, "<empty>");
         break;
 
     case NJS_FUNCTION:
         if (njs_function(value)->native) {
-            njs_dump("[Function: native]");
+            njs_chb_append_literal(chain, "[Function: native]");
 
         } else {
-            njs_dump("[Function]");
+            njs_chb_append_literal(chain, "[Function]");
         }
 
         break;
@@ -2041,7 +1841,7 @@ njs_dump_value(njs_json_stringify_t *stringify, const njs_value_t *value,
                           && signbit(njs_number(value))))
         {
 
-            njs_dump("-0");
+            njs_chb_append_literal(chain, "-0");
             break;
         }
 
@@ -2074,22 +1874,16 @@ njs_dump_value(njs_json_stringify_t *stringify, const njs_value_t *value,
         }
 
         njs_string_get(&str_val, &str);
+        njs_chb_append_str(chain, &str);
 
-        return njs_json_buf_append(stringify, (char *) str.start, str.length);
+        break;
 
     default:
-        p = njs_sprintf(buf, buf + njs_length(buf), "[Unknown value type:%uD]",
-                        value->type);
-        return njs_json_buf_append(stringify, (char *) buf, p - buf);
-    }
-
-    return ret;
 
-memory_error:
-
-    njs_memory_error(stringify->vm);
+        njs_chb_sprintf(chain, 64, "[Unknown value type:%uD]", value->type);
+    }
 
-    return NJS_ERROR;
+    return NJS_OK;
 }
 
 
@@ -2115,17 +1909,6 @@ njs_dump_is_object(const njs_value_t *value)
 }
 
 
-#define njs_dump_append_value(value)                                          \
-    ret = njs_dump_value(stringify, value, console);                          \
-    if (njs_slow_path(ret != NJS_OK)) {                                       \
-        if (ret == NJS_DECLINED) {                                            \
-            goto exception;                                                   \
-        }                                                                     \
-                                                                              \
-        goto memory_error;                                                    \
-    }
-
-
 static const njs_value_t  string_get = njs_string("[Getter]");
 static const njs_value_t  string_set = njs_string("[Setter]");
 static const njs_value_t  string_get_set = njs_long_string("[Getter/Setter]");
@@ -2137,6 +1920,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
 {
     njs_int_t             i;
     njs_int_t             ret;
+    njs_chb_t             chain;
     njs_str_t             str;
     njs_value_t           *key, *val, tag;
     njs_json_state_t      *state;
@@ -2148,15 +1932,14 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
     stringify = &dump_stringify;
 
     stringify->vm = vm;
-    stringify->pool = vm->mem_pool;
     stringify->depth = 0;
-    stringify->nodes = NULL;
-    stringify->last = NULL;
     njs_set_undefined(&stringify->replacer);
     stringify->keys_type = NJS_ENUM_STRING | NJS_ENUM_SYMBOL;
 
+    njs_chb_init(&chain, vm->mem_pool);
+
     if (!njs_dump_is_object(value)) {
-        ret = njs_dump_value(stringify, value, console);
+        ret = njs_dump_value(stringify, &chain, value, console);
         if (njs_slow_path(ret != NJS_OK)) {
             goto memory_error;
         }
@@ -2186,17 +1969,17 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
 
                 if (ret == NJS_OK) {
                     (void) njs_string_prop(&string, &tag);
-                    njs_json_stringify_append(string.start, string.size)
-                    njs_json_stringify_append(" ", 1);
+                    njs_chb_append(&chain, string.start, string.size);
+                    njs_chb_append_literal(&chain, " ");
                 }
 
-                njs_json_stringify_append("{", 1);
+                njs_chb_append_literal(&chain, "{");
                 njs_json_stringify_indent(stringify->depth + 1);
             }
 
             if (state->index >= state->keys->length) {
                 njs_json_stringify_indent(stringify->depth);
-                njs_json_stringify_append("}", 1);
+                njs_chb_append_literal(&chain, "}");
 
                 state = njs_json_pop_stringify_state(stringify);
                 if (state == NULL) {
@@ -2254,16 +2037,16 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
             }
 
             if (state->written) {
-                njs_json_stringify_append(",", 1);
+                njs_chb_append_literal(&chain, ",");
                 njs_json_stringify_indent(stringify->depth + 1);
             }
 
             state->written = 1;
             njs_key_string_get(vm, key, &pq.lhq.key);
-            njs_json_stringify_append(pq.lhq.key.start, pq.lhq.key.length);
-            njs_json_stringify_append(":", 1);
+            njs_chb_append(&chain, pq.lhq.key.start, pq.lhq.key.length);
+            njs_chb_append_literal(&chain, ":");
             if (stringify->space.length != 0) {
-                njs_json_stringify_append(" ", 1);
+                njs_chb_append_literal(&chain, " ");
             }
 
             if (njs_dump_is_object(val)) {
@@ -2275,19 +2058,26 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
                 break;
             }
 
-            njs_dump_append_value(val);
+            ret = njs_dump_value(stringify, &chain, val, console);
+            if (njs_slow_path(ret != NJS_OK)) {
+                if (ret == NJS_DECLINED) {
+                    goto exception;
+                }
+
+                goto memory_error;
+            }
 
             break;
 
         case NJS_JSON_ARRAY:
             if (state->index == 0) {
-                njs_json_stringify_append("[", 1);
+                njs_chb_append_literal(&chain, "[");
                 njs_json_stringify_indent(stringify->depth + 1);
             }
 
             if (state->index >= njs_array_len(&state->value)) {
                 njs_json_stringify_indent(stringify->depth);
-                njs_json_stringify_append("]", 1);
+                njs_chb_append_literal(&chain, "]");
 
                 state = njs_json_pop_stringify_state(stringify);
                 if (state == NULL) {
@@ -2298,7 +2088,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
             }
 
             if (state->written) {
-                njs_json_stringify_append(",", 1);
+                njs_chb_append_literal(&chain, ",");
                 njs_json_stringify_indent(stringify->depth + 1);
             }
 
@@ -2314,7 +2104,15 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
             }
 
             state->written = 1;
-            njs_dump_append_value(val);
+
+            ret = njs_dump_value(stringify, &chain, val, console);
+            if (njs_slow_path(ret != NJS_OK)) {
+                if (ret == NJS_DECLINED) {
+                    goto exception;
+                }
+
+                goto memory_error;
+            }
 
             break;
         }
@@ -2322,11 +2120,13 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
 
 done:
 
-    ret = njs_json_buf_pullup(stringify, &str);
+    ret = njs_chb_join(&chain, &str);
     if (njs_slow_path(ret != NJS_OK)) {
         goto memory_error;
     }
 
+    njs_chb_destroy(&chain);
+
     *retval = str;
 
     return NJS_OK;
index ce12807bd4257372c7f6f353d2a780481aaaefde..5fc40c74436497717fea87aea8917fc26bd03180 100644 (file)
@@ -31,6 +31,7 @@
 #include <njs_rbtree.h>
 #include <njs_mp.h>
 #include <njs_arr.h>
+#include <njs_chb.h>
 #include <njs_sprintf.h>
 
 #include <njs_pcre.h>
index 42f7a222db8c02089b50b1d955895b382f25469e..cd2953d609eda4ae8bb2e4dd2042a99d455eb49d 100644 (file)
@@ -1014,8 +1014,8 @@ njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
 njs_int_t
 njs_vm_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace, njs_str_t *dst)
 {
-    u_char                 *p, *start, *end;
-    size_t                 len, count;
+    size_t                 count;
+    njs_chb_t              chain;
     njs_uint_t             i;
     njs_backtrace_entry_t  *be, *prev;
 
@@ -1023,52 +1023,10 @@ njs_vm_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace, njs_str_t *dst)
         return NJS_OK;
     }
 
-    len = dst->length + 1;
+    njs_chb_init(&chain, vm->mem_pool);
 
-    count = 0;
-    prev = NULL;
-
-    be = backtrace->start;
-
-    for (i = 0; i < backtrace->items; i++) {
-        if (i != 0 && prev->name.start == be->name.start
-            && prev->line == be->line)
-        {
-            count++;
-
-        } else {
-
-            if (count != 0) {
-                len += njs_length("      repeats  times\n")
-                       + NJS_INT_T_LEN;
-                count = 0;
-            }
-
-            len += be->name.length + njs_length("    at  ()\n");
-
-            if (be->line != 0) {
-                len += be->file.length + NJS_INT_T_LEN + 1;
-
-            } else {
-                len += njs_length("native");
-            }
-        }
-
-        prev = be;
-        be++;
-    }
-
-    p = njs_mp_alloc(vm->mem_pool, len);
-    if (p == NULL) {
-        njs_memory_error(vm);
-        return NJS_ERROR;
-    }
-
-    start = p;
-    end = start + len;
-
-    p = njs_cpymem(p, dst->start, dst->length);
-    *p++ = '\n';
+    njs_chb_append_str(&chain, dst);
+    njs_chb_append(&chain, "\n", 1);
 
     count = 0;
     prev = NULL;
@@ -1083,19 +1041,19 @@ njs_vm_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace, njs_str_t *dst)
 
         } else {
             if (count != 0) {
-                p = njs_sprintf(p, end, "      repeats %uz times\n",
-                                count);
+                njs_chb_sprintf(&chain, 64, "      repeats %uz times\n", count);
                 count = 0;
             }
 
-            p = njs_sprintf(p, end, "    at %V ", &be->name);
+            njs_chb_sprintf(&chain, 10 + be->name.length, "    at %V ",
+                            &be->name);
 
             if (be->line != 0) {
-                p = njs_sprintf(p, end, "(%V:%uD)\n", &be->file,
-                                be->line);
+                njs_chb_sprintf(&chain, 12 + be->file.length,
+                                "(%V:%uD)\n", &be->file, be->line);
 
             } else {
-                p = njs_sprintf(p, end, "(native)\n");
+                njs_chb_append(&chain, "(native)\n", 9);
             }
         }
 
@@ -1103,8 +1061,8 @@ njs_vm_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace, njs_str_t *dst)
         be++;
     }
 
-    dst->start = start;
-    dst->length = p - dst->start;
+    njs_chb_join(&chain, dst);
+    njs_chb_destroy(&chain);
 
     return NJS_OK;
 }
index c9ef5e42569176d07ff24bb8d837743637a60d03..488e17cc39d432aa9042235199f596a5170946c7 100644 (file)
@@ -16884,6 +16884,150 @@ njs_file_dirname_test(njs_vm_t *vm, njs_opts_t *opts, njs_stat_t *stat)
 }
 
 
+static njs_int_t
+njs_chb_test(njs_vm_t *vm, njs_opts_t *opts, njs_stat_t *stat)
+{
+    u_char     *p;
+    njs_int_t  ret, i;
+    njs_chb_t  chain;
+    njs_str_t  string, arg;
+
+    static const njs_str_t  expected = njs_str("arg: \"XYZ\" -5");
+
+    njs_chb_init(&chain, vm->mem_pool);
+
+    p = njs_chb_reserve(&chain, 513);
+    if (p == NULL) {
+        ret = NJS_ERROR;
+        njs_printf("njs_chb_reserve() failed\n");
+        goto done;
+    }
+
+    njs_memset(p, 'Z', 256);
+
+    njs_chb_written(&chain, 256);
+
+    for (i = 0; i < 768; i++) {
+        njs_chb_append(&chain, "X", 1);
+    }
+
+    ret = njs_chb_join(&chain, &string);
+    if (ret != NJS_OK) {
+        njs_printf("njs_chb_join() failed\n");
+        goto done;
+    }
+
+    if (string.length != 1024 || njs_chb_size(&chain) != 1024) {
+        ret = NJS_ERROR;
+        njs_printf("njs_chb_join() corrupts "
+                   "string.length:%z njs_chb_size(&chain):%z != 1024\n",
+                   string.length, njs_chb_size(&chain));
+        goto done;
+    }
+
+    for (i = 0; i < 1024; i++) {
+        if (string.start[i] != ((i < 256) ? 'Z' : 'X')) {
+            ret = NJS_ERROR;
+            njs_printf("njs_chb_join() corrupts string[%i]:%c != '%c'\n",
+                       i, string.start[i], (i < 256) ? 'Z' : 'X');
+            goto done;
+        }
+    }
+
+    njs_mp_free(vm->mem_pool, string.start);
+
+    for (i = 0; i < 222; i++) {;
+        njs_chb_drain(&chain, 3);
+    }
+
+    ret = njs_chb_join(&chain, &string);
+    if (ret != NJS_OK) {
+        njs_printf("njs_chb_join() failed\n");
+        goto done;
+    }
+
+    if (string.length != 358 || njs_chb_size(&chain) != 358) {
+        ret = NJS_ERROR;
+        njs_printf("njs_chb_join() corrupts "
+                   "string.length:%z njs_chb_size(&chain):%z != 358\n",
+                   string.length, njs_chb_size(&chain));
+        goto done;
+    }
+
+    for (i = 0; i < 358; i++) {
+        if (string.start[i] != 'X') {
+            ret = NJS_ERROR;
+            njs_printf("njs_chb_join() corrupts string[%i]:%c != 'X'\n",
+                       i, string.start[i]);
+            goto done;
+        }
+    }
+
+    for (i = 0; i < 512; i++) {
+        njs_chb_append(&chain, "ABC", 3);
+    }
+
+    for (i = 0; i < 447; i++) {
+        njs_chb_drop(&chain, 2);
+    }
+
+    ret = njs_chb_join(&chain, &string);
+    if (ret != NJS_OK) {
+        njs_printf("njs_chb_join() failed\n");
+        goto done;
+    }
+
+    if (string.length != 1000 || njs_chb_size(&chain) != 1000) {
+        ret = NJS_ERROR;
+        njs_printf("njs_chb_join() corrupts "
+                   "string.length:%z njs_chb_size(&chain):%z != 1000\n",
+                   string.length, njs_chb_size(&chain));
+        goto done;
+    }
+
+    njs_chb_drop(&chain, 500);
+    njs_chb_drain(&chain, 501);
+
+    if (njs_chb_size(&chain) != 0) {
+        ret = NJS_ERROR;
+        njs_printf("njs_chb_drop() corrupts "
+                   "njs_chb_size(&chain):%z != 0\n", njs_chb_size(&chain));
+        goto done;
+    }
+
+    arg = njs_str_value("XYZ");
+
+    njs_chb_sprintf(&chain, 32, "arg: \"%V\" %d", &arg, -5);
+
+    ret = njs_chb_join(&chain, &string);
+    if (ret != NJS_OK) {
+        njs_printf("njs_chb_join() failed\n");
+        goto done;
+    }
+
+    if (!njs_strstr_eq(&string, &expected)) {
+        ret = NJS_ERROR;
+        njs_printf("njs_chb_sprintf() corrupts \"%V\" != \"%V\"\n", &string,
+                   &expected);
+        goto done;
+    }
+
+    njs_chb_destroy(&chain);
+    njs_mp_free(vm->mem_pool, string.start);
+
+done:
+
+    if (ret != NJS_OK) {
+        stat->failed++;
+        return NJS_OK;
+    }
+
+    stat->passed++;
+
+    return NJS_OK;
+}
+
+
 static njs_int_t
 njs_api_test(njs_opts_t *opts, njs_stat_t *stat)
 {
@@ -16903,6 +17047,8 @@ njs_api_test(njs_opts_t *opts, njs_stat_t *stat)
           njs_str("njs_file_basename_test") },
         { njs_file_dirname_test,
           njs_str("njs_file_dirname_test") },
+        { njs_chb_test,
+          njs_str("njs_chb_test") },
     };
 
     vm = NULL;