ソースを参照

Decouple `parseNumber()` from `VariantData`

Benoit Blanchon 1 年間 前
コミット
4ada3f849c

+ 16 - 18
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);
 }

+ 16 - 3
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() {

+ 11 - 0
src/ArduinoJson/Numbers/convertNumber.hpp

@@ -121,10 +121,21 @@ canConvertNumber(TIn value) {
          value <= FloatTraits<TIn>::template highest_for<TOut>();
 }
 
+// float32 -> float32
+// float64 -> float64
+// float64 -> float32
+template <typename TOut, typename TIn>
+enable_if_t<is_floating_point<TIn>::value && is_floating_point<TOut>::value,
+            bool>
+canConvertNumber(TIn) {
+  return true;
+}
+
 template <typename TOut, typename TIn>
 TOut convertNumber(TIn value) {
   return canConvertNumber<TOut>(value) ? TOut(value) : 0;
 }
+
 ARDUINOJSON_END_PRIVATE_NAMESPACE
 
 #if defined(__clang__)

+ 75 - 21
src/ArduinoJson/Numbers/parseNumber.hpp

@@ -5,20 +5,81 @@
 #pragma once
 
 #include <ArduinoJson/Numbers/FloatTraits.hpp>
+#include <ArduinoJson/Numbers/JsonFloat.hpp>
 #include <ArduinoJson/Numbers/convertNumber.hpp>
 #include <ArduinoJson/Polyfills/assert.hpp>
 #include <ArduinoJson/Polyfills/ctype.hpp>
 #include <ArduinoJson/Polyfills/math.hpp>
 #include <ArduinoJson/Polyfills/type_traits.hpp>
-#include <ArduinoJson/Variant/Converter.hpp>
-#include <ArduinoJson/Variant/VariantData.hpp>
 
 ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename A, typename B>
 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 <typename T>
+  T convertTo() const {
+    switch (type_) {
+      case NumberType::Float:
+        return convertNumber<T>(value_.asFloat);
+      case NumberType::SignedInteger:
+        return convertNumber<T>(value_.asSignedInteger);
+      case NumberType::UnsignedInteger:
+        return convertNumber<T>(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<JsonFloat> traits;
   typedef largest_type<traits::mantissa_type, JsonUInt> 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<JsonFloat>(mantissa), exponent);
 
-  result.setFloat(is_negative ? -final_result : final_result);
-  return true;
+  return Number(is_negative ? -final_result : final_result);
 }
 
 template <typename T>
 inline T parseNumber(const char* s) {
-  VariantData value;
-  parseNumber(s, value);
-  return Converter<T>::fromJson(JsonVariantConst(&value, nullptr));
+  return parseNumber(s).convertTo<T>();
 }
+
 ARDUINOJSON_END_PRIVATE_NAMESPACE