[njs] Improved memory consumption of String.prototype.concat() with numbers.
noreply at nginx.com
noreply at nginx.com
Thu Jul 3 16:35:03 UTC 2025
details: https://github.com/nginx/njs/commit/853bf75366488af34e6d2db338bc0c8bfb850ea2
branches: master
commit: 853bf75366488af34e6d2db338bc0c8bfb850ea2
user: Vadim Zhestikov <v.zhestikov at 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") },
More information about the nginx-devel
mailing list