From 93b2544ab446ebc63bedd5d05c373eb1c28ddafa Mon Sep 17 00:00:00 2001 From: Jimmy Lu Date: Thu, 29 Feb 2024 10:23:28 -0800 Subject: [PATCH] Safer JSON cast from floating point to integer across platforms Summary: https://github.com/facebookincubator/velox/pull/8899 When casting from floating point to integer, first cast losslessly into `double`, then check if it can be cast into `int64_t` using boundaries from `folly::constexpr_clamp_cast`, if it fits then we do another integer to integer cast which is trivial. Reviewed By: bikramSingh91 Differential Revision: D54313505 fbshipit-source-id: 7ad5cf182fe3ac8b304679b7e95eff21cee51d00 --- velox/functions/prestosql/types/JsonType.cpp | 24 +++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/velox/functions/prestosql/types/JsonType.cpp b/velox/functions/prestosql/types/JsonType.cpp index 9defbf9c7c98..7b11b8f9eaa1 100644 --- a/velox/functions/prestosql/types/JsonType.cpp +++ b/velox/functions/prestosql/types/JsonType.cpp @@ -569,16 +569,28 @@ simdjson::simdjson_result fromString(const std::string_view& s) { template simdjson::error_code convertIfInRange(From x, exec::GenericWriter& writer) { static_assert(std::is_signed_v && std::is_signed_v); - static_assert(std::is_integral_v || !std::is_integral_v); - if constexpr (!std::is_same_v) { - constexpr From kMin = std::numeric_limits::lowest(); - constexpr From kMax = std::numeric_limits::max(); + if constexpr (std::is_integral_v == std::is_integral_v) { + if constexpr (sizeof(To) < sizeof(From)) { + constexpr From kMin = std::numeric_limits::lowest(); + constexpr From kMax = std::numeric_limits::max(); + if (!(kMin <= x && x <= kMax)) { + return simdjson::NUMBER_OUT_OF_RANGE; + } + } + writer.castTo() = x; + return simdjson::SUCCESS; + } else { + static_assert(std::is_integral_v && !std::is_integral_v); + // Upper/lower bound values that could be accurately represented in both + // int64_t and double types. Same values are used by + // folly::constexpr_clamp_cast. + constexpr double kMin = -9223372036854774784.0; + constexpr double kMax = 9223372036854774784.0; if (!(kMin <= x && x <= kMax)) { return simdjson::NUMBER_OUT_OF_RANGE; } + return convertIfInRange(x, writer); } - writer.castTo() = x; - return simdjson::SUCCESS; } template