From ce0e788ce28c675808a6bc9a34f8db1199cd7cef Mon Sep 17 00:00:00 2001 From: itchyny Date: Sat, 2 Mar 2024 00:33:17 +0900 Subject: [PATCH] improve tonumber/0 performance by parsing input as number literal Previously, the tonumber/0 filter parses the input as JSON values, but this is less-performant on large non-number strings. Parsing the input string as number literal fixes the performance issue. Also, this fix changes the filter to reject numbers with white spaces. --- src/builtin.c | 21 +++++++++++++++++---- tests/jq.test | 4 ++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/builtin.c b/src/builtin.c index 9aebd1f2d2..393fac0ded 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -43,6 +43,8 @@ void *alloca (size_t); #include "locfile.h" #include "jv_unicode.h" #include "jv_alloc.h" +#include "jv_dtoa.h" +#include "jv_dtoa_tsd.h" #include "jv_private.h" #include "util.h" @@ -464,11 +466,22 @@ static jv f_tonumber(jq_state *jq, jv input) { return input; } if (jv_get_kind(input) == JV_KIND_STRING) { - jv parsed = jv_parse(jv_string_value(input)); - if (!jv_is_valid(parsed) || jv_get_kind(parsed) == JV_KIND_NUMBER) { - jv_free(input); - return parsed; + const char* s = jv_string_value(input); +#ifdef USE_DECNUM + jv number = jv_number_with_literal(s); + if (jv_get_kind(number) == JV_KIND_INVALID) { + return type_error(input, "cannot be parsed as a number"); + } +#else + char *end = 0; + double d = jvp_strtod(tsd_dtoa_context_get(), s, &end); + if (end == 0 || *end != 0) { + return type_error(input, "cannot be parsed as a number"); } + jv number = jv_number(d); +#endif + jv_free(input); + return number; } return type_error(input, "cannot be parsed as a number"); } diff --git a/tests/jq.test b/tests/jq.test index b94f29d245..60715f693a 100644 --- a/tests/jq.test +++ b/tests/jq.test @@ -2000,8 +2000,8 @@ null 2 .[] |= try tonumber -["1", "2a", "3", 4] -[1, 3, 4] +["1", "2a", "3", " 4 ", "5.67", ".89", "-876", "+5.43", 21] +[1, 3, 5.67, 0.89, -876, 5.43, 21] # Also 1859, but from 2073 any(keys[]|tostring?;true)