From dc849e9bb74a7a164a3ea52f661cc712b1ffbd43 Mon Sep 17 00:00:00 2001 From: itchyny Date: Tue, 4 Mar 2025 22:13:55 +0900 Subject: [PATCH] Improve performance of repeating strings (#3272) This commit improves the performance of repeating strings, by copying the result string instead of the string being repeated. Also it adds an error message when the result string is too long. --- src/builtin.c | 15 ++------------- src/jv.c | 26 ++++++++++++++++++++++++++ src/jv.h | 1 + tests/jq.test | 12 ++++++++++++ 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/builtin.c b/src/builtin.c index 06cd213d..0757a86b 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -343,21 +343,10 @@ jv binop_multiply(jv a, jv b) { str = b; num = a; } - jv res; double d = jv_number_value(num); - if (d < 0 || isnan(d)) { - res = jv_null(); - } else { - int n = d; - size_t alen = jv_string_length_bytes(jv_copy(str)); - res = jv_string_empty(alen * n); - for (; n > 0; n--) { - res = jv_string_append_buf(res, jv_string_value(str), alen); - } - } - jv_free(str); jv_free(num); - return res; + return jv_string_repeat(str, + d < 0 || isnan(d) ? -1 : d > INT_MAX ? INT_MAX : (int)d); } else if (ak == JV_KIND_OBJECT && bk == JV_KIND_OBJECT) { return jv_object_merge_recursive(a, b); } else { diff --git a/src/jv.c b/src/jv.c index 3e9920ed..fd6cfcab 100644 --- a/src/jv.c +++ b/src/jv.c @@ -1324,6 +1324,32 @@ jv jv_string_indexes(jv j, jv k) { return a; } +jv jv_string_repeat(jv j, int n) { + assert(JVP_HAS_KIND(j, JV_KIND_STRING)); + if (n < 0) { + jv_free(j); + return jv_null(); + } + int len = jv_string_length_bytes(jv_copy(j)); + int64_t res_len = (int64_t)len * n; + if (res_len >= INT_MAX) { + jv_free(j); + return jv_invalid_with_msg(jv_string("Repeat string result too long")); + } + if (res_len == 0) { + jv_free(j); + return jv_string(""); + } + jv res = jv_string_empty(res_len); + res = jvp_string_append(res, jv_string_value(j), len); + for (int curr = len, grow; curr < res_len; curr += grow) { + grow = MIN(res_len - curr, curr); + res = jvp_string_append(res, jv_string_value(res), grow); + } + jv_free(j); + return res; +} + jv jv_string_split(jv j, jv sep) { assert(JVP_HAS_KIND(j, JV_KIND_STRING)); assert(JVP_HAS_KIND(sep, JV_KIND_STRING)); diff --git a/src/jv.h b/src/jv.h index 5d4197cf..b9710610 100644 --- a/src/jv.h +++ b/src/jv.h @@ -136,6 +136,7 @@ jv jv_string_fmt(const char*, ...) JV_PRINTF_LIKE(1, 2); jv jv_string_append_codepoint(jv a, uint32_t c); jv jv_string_append_buf(jv a, const char* buf, int len); jv jv_string_append_str(jv a, const char* str); +jv jv_string_repeat(jv j, int n); jv jv_string_split(jv j, jv sep); jv jv_string_explode(jv j); jv jv_string_implode(jv j); diff --git a/tests/jq.test b/tests/jq.test index 959895c4..2e3c1e8b 100644 --- a/tests/jq.test +++ b/tests/jq.test @@ -1460,6 +1460,18 @@ indices("o") "abc" [null,null] +. * 100000 | [.[:10],.[-10:]] +"abc" +["abcabcabca","cabcabcabc"] + +. * 1000000000 +"" +"" + +try (. * 1000000000) catch . +"abc" +"Repeat string result too long" + [.[] / ","] ["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] [["a"," bc"," def"," ghij"," jklmn"," a","b"," c","d"," e","f"],["a","b","c","d"," e","f","g","h"]]