summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoritchyny <itchyny@cybozu.co.jp>2024-03-02 00:33:17 +0900
committerGitHub <noreply@github.com>2024-03-01 16:33:17 +0100
commitce0e788ce28c675808a6bc9a34f8db1199cd7cef (patch)
treeaf497f3fe3d560a92d5dd6e25874bfd07e0fc8b3
parent913b26469f47351a9f75a1bc4145f45421115e37 (diff)
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.
-rw-r--r--src/builtin.c21
-rw-r--r--tests/jq.test4
2 files changed, 19 insertions, 6 deletions
diff --git a/src/builtin.c b/src/builtin.c
index 9aebd1f2..393fac0d 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 b94f29d2..60715f69 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)