details:   
https://github.com/nginx/njs/commit/853bf75366488af34e6d2db338bc0c8bfb850ea2
branches:  master
commit:    853bf75366488af34e6d2db338bc0c8bfb850ea2
user:      Vadim Zhestikov <v.zhesti...@f5.com>
date:      Mon, 23 Jun 2025 17:44:57 -0700
description:
Improved memory consumption of String.prototype.concat() with numbers.


---
 src/njs_dtoa.h           |   2 +
 src/njs_string.c         | 110 ++++++++++++++++++++++++++++++++++++-----------
 src/test/njs_unit_test.c |   9 ++++
 3 files changed, 95 insertions(+), 26 deletions(-)

diff --git a/src/njs_dtoa.h b/src/njs_dtoa.h
index 35ff09a5..4dcfc098 100644
--- a/src/njs_dtoa.h
+++ b/src/njs_dtoa.h
@@ -7,6 +7,8 @@
 #ifndef _NJS_DTOA_H_INCLUDED_
 #define _NJS_DTOA_H_INCLUDED_
 
+#define NJS_DTOA_MAX_LEN  njs_length("-1.7976931348623157e+308")
+
 NJS_EXPORT size_t njs_dtoa(double value, char *start);
 NJS_EXPORT size_t njs_dtoa_precision(double value, char *start, size_t prec);
 NJS_EXPORT size_t njs_dtoa_exponential(double value, char *start,
diff --git a/src/njs_string.c b/src/njs_string.c
index 6d7c464d..d3991451 100644
--- a/src/njs_string.c
+++ b/src/njs_string.c
@@ -601,56 +601,114 @@ njs_int_t
 njs_string_prototype_concat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused, njs_value_t *retval)
 {
-    u_char             *p, *start;
-    uint64_t           size, length, mask;
+    char               *np, *np_end;
+    double             num;
+    u_char             *p;
+    uint64_t           sz, size, length;
     njs_int_t          ret;
     njs_uint_t         i;
     njs_string_prop_t  string;
+    char               buf[512], tmp[NJS_DTOA_MAX_LEN];
 
     if (njs_is_null_or_undefined(&args[0])) {
         njs_type_error(vm, "\"this\" argument is null or undefined");
         return NJS_ERROR;
     }
 
+    size = 0;
+    length = 0;
+
+    np = buf;
+    np_end = buf + sizeof(buf);
+
     for (i = 0; i < nargs; i++) {
-        if (!njs_is_string(&args[i])) {
-            ret = njs_value_to_string(vm, &args[i], &args[i]);
-            if (ret != NJS_OK) {
-                return ret;
+        if (njs_is_number(&args[i])) {
+            num = njs_number(&args[i]);
+
+            if (isnan(num)) {
+                size += njs_length("NaN");
+                length += njs_length("NaN");
+
+            } else if (isinf(num)) {
+                if (num < 0) {
+                    size += njs_length("-Infinity");
+                    length += njs_length("-Infinity");
+
+                } else {
+                    size += njs_length("Infinity");
+                    length += njs_length("Infinity");
+                }
+
+            } else {
+                if (njs_fast_path(np < np_end - NJS_DTOA_MAX_LEN)) {
+                    sz = njs_dtoa(num, np + sizeof(uint8_t));
+
+                    *np = (uint8_t) sz;
+                    np += sizeof(uint8_t) + sz;
+
+                } else {
+                    sz = njs_dtoa(num, tmp);
+                }
+
+                size += sz;
+                length += sz;
+            }
+
+        } else {
+            if (!njs_is_string(&args[i])) {
+                ret = njs_value_to_string(vm, &args[i], &args[i]);
+                if (ret != NJS_OK) {
+                    return ret;
+                }
+
             }
+
+            njs_string_prop(vm, &string, &args[i]);
+
+            size += string.size;
+            length += string.length;
         }
     }
 
-    if (nargs == 1) {
-        njs_string_copy(retval, &args[0]);
-        return NJS_OK;
+    p = njs_string_alloc(vm, retval, size, length);
+    if (njs_slow_path(p == NULL)) {
+        return NJS_ERROR;
     }
 
-    size = 0;
-    length = 0;
-    mask = -1;
+    np = buf;
 
     for (i = 0; i < nargs; i++) {
-        (void) njs_string_prop(vm, &string, &args[i]);
+        if (njs_is_number(&args[i])) {
+            num = njs_number(&args[i]);
 
-        size += string.size;
-        length += string.length;
-    }
+            if (isnan(num)) {
+                p = njs_cpymem(p, "NaN", njs_length("NaN"));
 
-    length &= mask;
+            } else if (isinf(num)) {
+                if (num < 0) {
+                    p = njs_cpymem(p, "-Infinity", njs_length("-Infinity"));
 
-    start = njs_string_alloc(vm, retval, size, length);
-    if (njs_slow_path(start == NULL)) {
-        return NJS_ERROR;
-    }
+                } else {
+                    p = njs_cpymem(p, "Infinity", njs_length("Infinity"));
+                }
 
-    p = start;
+            } else {
+                if (njs_fast_path(np < np_end - NJS_DTOA_MAX_LEN)) {
+                    length = *np++;
+                    p = njs_cpymem(p, np, length);
+                    np += length;
 
-    for (i = 0; i < nargs; i++) {
-        (void) njs_string_prop(vm, &string, &args[i]);
+                } else {
+                    sz = njs_dtoa(num, (char *) p);
+                    p += sz;
+                }
+            }
 
-        p = memcpy(p, string.start, string.size);
-        p += string.size;
+        } else {
+            njs_string_prop(vm, &string, &args[i]);
+
+            p = njs_cpymem(p, string.start, string.size);
+        }
     }
 
     return NJS_OK;
diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c
index 01ff08d4..33472f24 100644
--- a/src/test/njs_unit_test.c
+++ b/src/test/njs_unit_test.c
@@ -11036,6 +11036,15 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("''.concat.apply('a', [ 'b', 'c' ], 'd')"),
       njs_str("abc") },
 
+    { njs_str("''.concat.apply('', Array(128).fill(1.23456789123e14)) == 
'123456789123000'.repeat(128)"),
+      njs_str("true") },
+
+    { njs_str("''.concat.apply('', 
Array(128).fill(0).map((v,i)=>Math.log2(i))).startsWith('-Infinity')"),
+      njs_str("true") },
+
+    { njs_str("''.concat.apply('', Array(256).fill(0).map((v,i)=> !(i % 2) ? 
Math.exp(i) : 'α'.repeat(Math.log2(i)))).endsWith('110ααααααα')"),
+      njs_str("true") },
+
     { njs_str("[].join.call([1,2,3])"),
       njs_str("1,2,3") },
 
_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
https://mailman.nginx.org/mailman/listinfo/nginx-devel

Reply via email to