diff --git a/extras/tests/Numbers/parseNumber.cpp b/extras/tests/Numbers/parseNumber.cpp index ba9412fc..85d5b535 100644 --- a/extras/tests/Numbers/parseNumber.cpp +++ b/extras/tests/Numbers/parseNumber.cpp @@ -9,45 +9,43 @@ using namespace ArduinoJson; using namespace ArduinoJson::detail; TEST_CASE("Test unsigned integer overflow") { - VariantData first, second; + Number first, second; // Avoids MSVC warning C4127 (conditional expression is constant) size_t integerSize = sizeof(JsonInteger); if (integerSize == 8) { - parseNumber("18446744073709551615", first); - parseNumber("18446744073709551616", second); + first = parseNumber("18446744073709551615"); + second = parseNumber("18446744073709551616"); } else { - parseNumber("4294967295", first); - parseNumber("4294967296", second); + first = parseNumber("4294967295"); + second = parseNumber("4294967296"); } - REQUIRE(first.type() == uint8_t(VALUE_IS_UNSIGNED_INTEGER)); - REQUIRE(second.type() == uint8_t(VALUE_IS_FLOAT)); + REQUIRE(first.type() == NumberType::UnsignedInteger); + REQUIRE(second.type() == NumberType::Float); } TEST_CASE("Test signed integer overflow") { - VariantData first, second; + Number first, second; // Avoids MSVC warning C4127 (conditional expression is constant) size_t integerSize = sizeof(JsonInteger); if (integerSize == 8) { - parseNumber("-9223372036854775808", first); - parseNumber("-9223372036854775809", second); + first = parseNumber("-9223372036854775808"); + second = parseNumber("-9223372036854775809"); } else { - parseNumber("-2147483648", first); - parseNumber("-2147483649", second); + first = parseNumber("-2147483648"); + second = parseNumber("-2147483649"); } - REQUIRE(first.type() == uint8_t(VALUE_IS_SIGNED_INTEGER)); - REQUIRE(second.type() == uint8_t(VALUE_IS_FLOAT)); + REQUIRE(first.type() == NumberType::SignedInteger); + REQUIRE(second.type() == NumberType::Float); } TEST_CASE("Invalid value") { - VariantData result; + auto result = parseNumber("6a3"); - parseNumber("6a3", result); - - REQUIRE(result.type() == uint8_t(VALUE_IS_NULL)); + REQUIRE(result.type() == NumberType::Invalid); } diff --git a/src/ArduinoJson/Json/JsonDeserializer.hpp b/src/ArduinoJson/Json/JsonDeserializer.hpp index 33de891a..29c5c4d9 100644 --- a/src/ArduinoJson/Json/JsonDeserializer.hpp +++ b/src/ArduinoJson/Json/JsonDeserializer.hpp @@ -517,10 +517,23 @@ class JsonDeserializer { } buffer_[n] = 0; - if (!parseNumber(buffer_, result)) - return DeserializationError::InvalidInput; + auto number = parseNumber(buffer_); + switch (number.type()) { + case NumberType::UnsignedInteger: + result.setInteger(number.asUnsignedInteger()); + return DeserializationError::Ok; - return DeserializationError::Ok; + case NumberType::SignedInteger: + result.setInteger(number.asSignedInteger()); + return DeserializationError::Ok; + + case NumberType::Float: + result.setFloat(number.asFloat()); + return DeserializationError::Ok; + + default: + return DeserializationError::InvalidInput; + } } DeserializationError::Code skipNumericValue() { diff --git a/src/ArduinoJson/Numbers/convertNumber.hpp b/src/ArduinoJson/Numbers/convertNumber.hpp index 0136278e..0b6ffdc3 100644 --- a/src/ArduinoJson/Numbers/convertNumber.hpp +++ b/src/ArduinoJson/Numbers/convertNumber.hpp @@ -121,10 +121,21 @@ canConvertNumber(TIn value) { value <= FloatTraits::template highest_for(); } +// float32 -> float32 +// float64 -> float64 +// float64 -> float32 +template +enable_if_t::value && is_floating_point::value, + bool> +canConvertNumber(TIn) { + return true; +} + template TOut convertNumber(TIn value) { return canConvertNumber(value) ? TOut(value) : 0; } + ARDUINOJSON_END_PRIVATE_NAMESPACE #if defined(__clang__) diff --git a/src/ArduinoJson/Numbers/parseNumber.hpp b/src/ArduinoJson/Numbers/parseNumber.hpp index 7fb3cba3..4e279203 100644 --- a/src/ArduinoJson/Numbers/parseNumber.hpp +++ b/src/ArduinoJson/Numbers/parseNumber.hpp @@ -5,20 +5,81 @@ #pragma once #include +#include #include #include #include #include #include -#include -#include ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template using largest_type = conditional_t<(sizeof(A) > sizeof(B)), A, B>; -inline bool parseNumber(const char* s, VariantData& result) { +enum class NumberType : uint8_t { + Invalid, + Float, + SignedInteger, + UnsignedInteger +}; + +union NumberValue { + NumberValue() {} + NumberValue(JsonFloat x) : asFloat(x) {} + NumberValue(JsonInteger x) : asSignedInteger(x) {} + NumberValue(JsonUInt x) : asUnsignedInteger(x) {} + + JsonInteger asSignedInteger; + JsonUInt asUnsignedInteger; + JsonFloat asFloat; +}; + +class Number { + NumberType type_; + NumberValue value_; + + public: + Number() : type_(NumberType::Invalid) {} + Number(JsonFloat value) : type_(NumberType::Float), value_(value) {} + Number(JsonInteger value) : type_(NumberType::SignedInteger), value_(value) {} + Number(JsonUInt value) : type_(NumberType::UnsignedInteger), value_(value) {} + + template + T convertTo() const { + switch (type_) { + case NumberType::Float: + return convertNumber(value_.asFloat); + case NumberType::SignedInteger: + return convertNumber(value_.asSignedInteger); + case NumberType::UnsignedInteger: + return convertNumber(value_.asUnsignedInteger); + default: + return T(); + } + } + + NumberType type() const { + return type_; + } + + JsonInteger asSignedInteger() const { + ARDUINOJSON_ASSERT(type_ == NumberType::SignedInteger); + return value_.asSignedInteger; + } + + JsonUInt asUnsignedInteger() const { + ARDUINOJSON_ASSERT(type_ == NumberType::UnsignedInteger); + return value_.asUnsignedInteger; + } + + JsonFloat asFloat() const { + ARDUINOJSON_ASSERT(type_ == NumberType::Float); + return value_.asFloat; + } +}; + +inline Number parseNumber(const char* s) { typedef FloatTraits traits; typedef largest_type mantissa_t; typedef traits::exponent_type exponent_t; @@ -38,20 +99,18 @@ inline bool parseNumber(const char* s, VariantData& result) { #if ARDUINOJSON_ENABLE_NAN if (*s == 'n' || *s == 'N') { - result.setFloat(traits::nan()); - return true; + return Number(traits::nan()); } #endif #if ARDUINOJSON_ENABLE_INFINITY if (*s == 'i' || *s == 'I') { - result.setFloat(is_negative ? -traits::inf() : traits::inf()); - return true; + return Number(is_negative ? -traits::inf() : traits::inf()); } #endif if (!isdigit(*s) && *s != '.') - return false; + return Number(); mantissa_t mantissa = 0; exponent_t exponent_offset = 0; @@ -73,12 +132,10 @@ inline bool parseNumber(const char* s, VariantData& result) { const mantissa_t sintMantissaMax = mantissa_t(1) << (sizeof(JsonInteger) * 8 - 1); if (mantissa <= sintMantissaMax) { - result.setInteger(JsonInteger(~mantissa + 1)); - return true; + return Number(JsonInteger(~mantissa + 1)); } } else { - result.setInteger(JsonUInt(mantissa)); - return true; + return Number(JsonUInt(mantissa)); } } @@ -120,10 +177,9 @@ inline bool parseNumber(const char* s, VariantData& result) { exponent = exponent * 10 + (*s - '0'); if (exponent + exponent_offset > traits::exponent_max) { if (negative_exponent) - result.setFloat(is_negative ? -0.0f : 0.0f); + return Number(is_negative ? -0.0f : 0.0f); else - result.setFloat(is_negative ? -traits::inf() : traits::inf()); - return true; + return Number(is_negative ? -traits::inf() : traits::inf()); } s++; } @@ -134,19 +190,17 @@ inline bool parseNumber(const char* s, VariantData& result) { // we should be at the end of the string, otherwise it's an error if (*s != '\0') - return false; + return Number(); JsonFloat final_result = make_float(static_cast(mantissa), exponent); - result.setFloat(is_negative ? -final_result : final_result); - return true; + return Number(is_negative ? -final_result : final_result); } template inline T parseNumber(const char* s) { - VariantData value; - parseNumber(s, value); - return Converter::fromJson(JsonVariantConst(&value, nullptr)); + return parseNumber(s).convertTo(); } + ARDUINOJSON_END_PRIVATE_NAMESPACE