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