From 0c5e97152cf0f991d8238aa84fe0dde11cc16137 Mon Sep 17 00:00:00 2001 From: msharipov Date: Sun, 18 Feb 2024 13:10:13 -0500 Subject: [PATCH] refactor(util): use parse_integer from simdjson simdjson provides functions for parsing decimal integers that should run faster than manually coded equivalent. Changed parse_integer_exact to use the parse_integer function from simdjson. Due to inconsistencies between what simdjson and the original implementation consider to be an error, some pre-emptive checks are run on the input string to avoid false positives/negatives. --- src/quick-lint-js/util/integer.cpp | 82 +++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/src/quick-lint-js/util/integer.cpp b/src/quick-lint-js/util/integer.cpp index a136d9dfc2..812778621e 100644 --- a/src/quick-lint-js/util/integer.cpp +++ b/src/quick-lint-js/util/integer.cpp @@ -21,6 +21,15 @@ #include #include +#ifdef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_SWAR_NUMBER_PARSING +#undef SIMDJSON_SWAR_NUMBER_PARSING +#include +#include +#define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif + QLJS_WARNING_IGNORE_CLANG("-Wmissing-prototypes") // TODO(strager): Fix. QLJS_WARNING_IGNORE_GCC("-Wmissing-declarations") // TODO(strager): Fix. QLJS_WARNING_IGNORE_GCC("-Wuseless-cast") @@ -194,7 +203,78 @@ Parse_Integer_Exact_Error parse_integer_exact_generic( template Parse_Integer_Exact_Error parse_integer_exact(std::string_view s, T &value) { - return parse_integer_exact_generic, T>(s, value); + if (s.empty()) { + return Parse_Integer_Exact_Error::invalid; + } + + if constexpr (std::numeric_limits::max() > + std::numeric_limits::max()) { + return parse_integer_exact_generic, T>(s, value); + } + + const char *s_first_digit = &(s.front()); + const char *s_end = &(s.back()) + 1; + bool negative = false; + + while (s_first_digit < s_end) { + char c = *s_first_digit; + if (c >= '1' && c <= '9') { + break; + } else if (c == '-') { + if (negative) { + return Parse_Integer_Exact_Error::invalid; + } else { + negative = true; + } + } else if (c == '0') { + if (negative) { + return Parse_Integer_Exact_Error::invalid; + } + } else { + return Parse_Integer_Exact_Error::invalid; + } + s_first_digit++; + } + + if (s_first_digit != s_end) { + for (const char *i = s_first_digit; i < s_end; i++) { + if (*i < '0' || *i > '9') { + return Parse_Integer_Exact_Error::invalid; + } + } + } else { + value = static_cast(0); + return Parse_Integer_Exact_Error::ok; + } + + const simdjson::simdjson_result parse_result = + simdjson::fallback::numberparsing::parse_integer( + reinterpret_cast( + static_cast(s_first_digit - negative)), + reinterpret_cast(static_cast(s_end))); + + switch (parse_result.error()) { + case (simdjson::SUCCESS): + break; + case (simdjson::INCORRECT_TYPE): + return Parse_Integer_Exact_Error::out_of_range; + case (simdjson::NUMBER_ERROR): + return Parse_Integer_Exact_Error::invalid; + default: + QLJS_UNREACHABLE(); + } + + int64_t parse_value = parse_result.value_unsafe(); + + static constexpr T result_min = std::numeric_limits::min(); + static constexpr T result_max = std::numeric_limits::max(); + + if (parse_value < result_min || parse_value > result_max) { + return Parse_Integer_Exact_Error::out_of_range; + } + + value = static_cast(parse_value); + return Parse_Integer_Exact_Error::ok; } template Parse_Integer_Exact_Error parse_integer_exact(std::string_view,