mirror of
				https://github.com/eledio-devices/thirdparty-ArduinoJson.git
				synced 2025-10-31 16:14:11 +01:00 
			
		
		
		
	Added overflow handling in JsonVariant::as<T>() and JsonVariant::is<T>()
This commit is contained in:
		| @@ -1,6 +1,14 @@ | |||||||
| ArduinoJson: change log | ArduinoJson: change log | ||||||
| ======================= | ======================= | ||||||
|  |  | ||||||
|  | HEAD | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | * Fixed an integer overflow in the JSON deserializer | ||||||
|  | * Added overflow handling in `JsonVariant::as<T>()` and `JsonVariant::is<T>()`. | ||||||
|  |    - `as<T>()` returns `0` if the integer `T` overflows | ||||||
|  |    - `is<T>()` returns `false` if the integer `T` overflows | ||||||
|  |  | ||||||
| v6.9.1 (2019-03-01) | v6.9.1 (2019-03-01) | ||||||
| ------ | ------ | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								fuzzing/json_seed_corpus/IntegerOverflow.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								fuzzing/json_seed_corpus/IntegerOverflow.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | 9720730739393920739 | ||||||
| @@ -3,7 +3,7 @@ | |||||||
| ROOT_DIR=$(dirname $0)/../../ | ROOT_DIR=$(dirname $0)/../../ | ||||||
| INCLUDE_DIR=${ROOT_DIR}/src/ | INCLUDE_DIR=${ROOT_DIR}/src/ | ||||||
| FUZZING_DIR=${ROOT_DIR}/fuzzing/ | FUZZING_DIR=${ROOT_DIR}/fuzzing/ | ||||||
| CXXFLAGS="-g -fprofile-instr-generate -fcoverage-mapping -fsanitize=address,fuzzer" | CXXFLAGS="-g -fprofile-instr-generate -fcoverage-mapping -fsanitize=address,undefined,fuzzer -fno-sanitize-recover=all" | ||||||
|  |  | ||||||
| fuzz() { | fuzz() { | ||||||
| 	NAME="$1" | 	NAME="$1" | ||||||
|   | |||||||
| @@ -6,8 +6,7 @@ | |||||||
|  |  | ||||||
| #include "../Deserialization/deserialize.hpp" | #include "../Deserialization/deserialize.hpp" | ||||||
| #include "../Memory/MemoryPool.hpp" | #include "../Memory/MemoryPool.hpp" | ||||||
| #include "../Numbers/isFloat.hpp" | #include "../Numbers/parseNumber.hpp" | ||||||
| #include "../Numbers/isInteger.hpp" |  | ||||||
| #include "../Polyfills/type_traits.hpp" | #include "../Polyfills/type_traits.hpp" | ||||||
| #include "../Variant/VariantData.hpp" | #include "../Variant/VariantData.hpp" | ||||||
| #include "EscapeSequence.hpp" | #include "EscapeSequence.hpp" | ||||||
| @@ -251,14 +250,6 @@ class JsonDeserializer { | |||||||
|     } |     } | ||||||
|     buffer[n] = 0; |     buffer[n] = 0; | ||||||
|  |  | ||||||
|     if (isInteger(buffer)) { |  | ||||||
|       result.setInteger(parseInteger<Integer>(buffer)); |  | ||||||
|       return DeserializationError::Ok; |  | ||||||
|     } |  | ||||||
|     if (isFloat(buffer)) { |  | ||||||
|       result.setFloat(parseFloat<Float>(buffer)); |  | ||||||
|       return DeserializationError::Ok; |  | ||||||
|     } |  | ||||||
|     c = buffer[0]; |     c = buffer[0]; | ||||||
|     if (c == 't') {  // true |     if (c == 't') {  // true | ||||||
|       result.setBoolean(true); |       result.setBoolean(true); | ||||||
| @@ -275,6 +266,23 @@ class JsonDeserializer { | |||||||
|       return n == 4 ? DeserializationError::Ok |       return n == 4 ? DeserializationError::Ok | ||||||
|                     : DeserializationError::IncompleteInput; |                     : DeserializationError::IncompleteInput; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     ParsedNumber<Float, UInt> num = parseNumber<Float, UInt>(buffer); | ||||||
|  |  | ||||||
|  |     switch (num.type()) { | ||||||
|  |       case VALUE_IS_NEGATIVE_INTEGER: | ||||||
|  |         result.setNegativeInteger(num.uintValue); | ||||||
|  |         return DeserializationError::Ok; | ||||||
|  |  | ||||||
|  |       case VALUE_IS_POSITIVE_INTEGER: | ||||||
|  |         result.setPositiveInteger(num.uintValue); | ||||||
|  |         return DeserializationError::Ok; | ||||||
|  |  | ||||||
|  |       case VALUE_IS_FLOAT: | ||||||
|  |         result.setFloat(num.floatValue); | ||||||
|  |         return DeserializationError::Ok; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return DeserializationError::InvalidInput; |     return DeserializationError::InvalidInput; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -79,8 +79,7 @@ class MsgPackDeserializer { | |||||||
| #if ARDUINOJSON_USE_LONG_LONG | #if ARDUINOJSON_USE_LONG_LONG | ||||||
|         return readInteger<uint64_t>(variant); |         return readInteger<uint64_t>(variant); | ||||||
| #else | #else | ||||||
|         readInteger<uint32_t>(); |         return DeserializationError::NotSupported; | ||||||
|         return readInteger<uint32_t>(variant); |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|       case 0xd0: |       case 0xd0: | ||||||
| @@ -96,8 +95,7 @@ class MsgPackDeserializer { | |||||||
| #if ARDUINOJSON_USE_LONG_LONG | #if ARDUINOJSON_USE_LONG_LONG | ||||||
|         return readInteger<int64_t>(variant); |         return readInteger<int64_t>(variant); | ||||||
| #else | #else | ||||||
|         if (!skip(4)) return DeserializationError::IncompleteInput; |         return DeserializationError::NotSupported; | ||||||
|         return readInteger<int32_t>(variant); |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|       case 0xca: |       case 0xca: | ||||||
|   | |||||||
| @@ -24,7 +24,8 @@ class MsgPackSerializer { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   template <typename T> |   template <typename T> | ||||||
|   typename enable_if<sizeof(T) == 8>::type visitFloat(T value64) { |   typename enable_if<sizeof(T) == 8>::type visitFloat(T value64) | ||||||
|  |       ARDUINOJSON_NO_SANITIZE("float-cast-overflow") { | ||||||
|     float value32 = float(value64); |     float value32 = float(value64); | ||||||
|     if (value32 == value64) { |     if (value32 == value64) { | ||||||
|       writeByte(0xCA); |       writeByte(0xCA); | ||||||
|   | |||||||
| @@ -17,10 +17,10 @@ struct FloatTraits {}; | |||||||
|  |  | ||||||
| template <typename T> | template <typename T> | ||||||
| struct FloatTraits<T, 8 /*64bits*/> { | struct FloatTraits<T, 8 /*64bits*/> { | ||||||
|   typedef int64_t mantissa_type; |   typedef uint64_t mantissa_type; | ||||||
|   static const short mantissa_bits = 52; |   static const short mantissa_bits = 52; | ||||||
|   static const mantissa_type mantissa_max = |   static const mantissa_type mantissa_max = | ||||||
|       (static_cast<mantissa_type>(1) << mantissa_bits) - 1; |       (mantissa_type(1) << mantissa_bits) - 1; | ||||||
|  |  | ||||||
|   typedef int16_t exponent_type; |   typedef int16_t exponent_type; | ||||||
|   static const exponent_type exponent_max = 308; |   static const exponent_type exponent_max = 308; | ||||||
| @@ -95,6 +95,14 @@ struct FloatTraits<T, 8 /*64bits*/> { | |||||||
|     return forge(0x7ff00000, 0x00000000); |     return forge(0x7ff00000, 0x00000000); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   static T highest() { | ||||||
|  |     return forge(0x7FEFFFFF, 0xFFFFFFFF); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static T lowest() { | ||||||
|  |     return forge(0xFFEFFFFF, 0xFFFFFFFF); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   // constructs a double floating point values from its binary representation |   // constructs a double floating point values from its binary representation | ||||||
|   // we use this function to workaround platforms with single precision literals |   // we use this function to workaround platforms with single precision literals | ||||||
|   // (for example, when -fsingle-precision-constant is passed to GCC) |   // (for example, when -fsingle-precision-constant is passed to GCC) | ||||||
| @@ -105,10 +113,10 @@ struct FloatTraits<T, 8 /*64bits*/> { | |||||||
|  |  | ||||||
| template <typename T> | template <typename T> | ||||||
| struct FloatTraits<T, 4 /*32bits*/> { | struct FloatTraits<T, 4 /*32bits*/> { | ||||||
|   typedef int32_t mantissa_type; |   typedef uint32_t mantissa_type; | ||||||
|   static const short mantissa_bits = 23; |   static const short mantissa_bits = 23; | ||||||
|   static const mantissa_type mantissa_max = |   static const mantissa_type mantissa_max = | ||||||
|       (static_cast<mantissa_type>(1) << mantissa_bits) - 1; |       (mantissa_type(1) << mantissa_bits) - 1; | ||||||
|  |  | ||||||
|   typedef int8_t exponent_type; |   typedef int8_t exponent_type; | ||||||
|   static const exponent_type exponent_max = 38; |   static const exponent_type exponent_max = 38; | ||||||
| @@ -156,5 +164,13 @@ struct FloatTraits<T, 4 /*32bits*/> { | |||||||
|   static T inf() { |   static T inf() { | ||||||
|     return forge(0x7f800000); |     return forge(0x7f800000); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   static T highest() { | ||||||
|  |     return forge(0x7f7fffff); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static T lowest() { | ||||||
|  |     return forge(0xFf7fffff); | ||||||
|  |   } | ||||||
| }; | }; | ||||||
| }  // namespace ARDUINOJSON_NAMESPACE | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
|   | |||||||
							
								
								
									
										105
									
								
								src/ArduinoJson/Numbers/convertNumber.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/ArduinoJson/Numbers/convertNumber.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2019 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #if defined(__clang__) | ||||||
|  | #pragma clang diagnostic push | ||||||
|  | #pragma clang diagnostic ignored "-Wconversion" | ||||||
|  | #elif defined(__GNUC__) | ||||||
|  | #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) | ||||||
|  | #pragma GCC diagnostic push | ||||||
|  | #endif | ||||||
|  | #pragma GCC diagnostic ignored "-Wconversion" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include "../Polyfills/limits.hpp" | ||||||
|  | #include "Float.hpp" | ||||||
|  | #include "FloatTraits.hpp" | ||||||
|  | #include "Integer.hpp" | ||||||
|  |  | ||||||
|  | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
|  | template <typename TOut, typename TIn> | ||||||
|  | typename enable_if<is_integral<TOut>::value && sizeof(TOut) <= sizeof(TIn), | ||||||
|  |                    bool>::type | ||||||
|  | canStorePositiveInteger(TIn value) { | ||||||
|  |   return value <= TIn(numeric_limits<TOut>::highest()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename TOut, typename TIn> | ||||||
|  | typename enable_if<is_integral<TOut>::value && sizeof(TIn) < sizeof(TOut), | ||||||
|  |                    bool>::type | ||||||
|  | canStorePositiveInteger(TIn) { | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename TOut, typename TIn> | ||||||
|  | typename enable_if<is_floating_point<TOut>::value, bool>::type | ||||||
|  | canStorePositiveInteger(TIn) { | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename TOut, typename TIn> | ||||||
|  | typename enable_if<is_floating_point<TOut>::value, bool>::type | ||||||
|  | canStoreNegativeInteger(TIn) { | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename TOut, typename TIn> | ||||||
|  | typename enable_if<is_integral<TOut>::value && is_signed<TOut>::value && | ||||||
|  |                        sizeof(TOut) <= sizeof(TIn), | ||||||
|  |                    bool>::type | ||||||
|  | canStoreNegativeInteger(TIn value) { | ||||||
|  |   return value <= TIn(numeric_limits<TOut>::highest()) + 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename TOut, typename TIn> | ||||||
|  | typename enable_if<is_integral<TOut>::value && is_signed<TOut>::value && | ||||||
|  |                        sizeof(TIn) < sizeof(TOut), | ||||||
|  |                    bool>::type | ||||||
|  | canStoreNegativeInteger(TIn) { | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename TOut, typename TIn> | ||||||
|  | typename enable_if<is_integral<TOut>::value && is_unsigned<TOut>::value, | ||||||
|  |                    bool>::type | ||||||
|  | canStoreNegativeInteger(TIn) { | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename TOut, typename TIn> | ||||||
|  | TOut convertPositiveInteger(TIn value) { | ||||||
|  |   return canStorePositiveInteger<TOut>(value) ? TOut(value) : 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename TOut, typename TIn> | ||||||
|  | TOut convertNegativeInteger(TIn value) { | ||||||
|  |   return canStoreNegativeInteger<TOut>(value) ? TOut(~value + 1) : 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename TOut, typename TIn> | ||||||
|  | typename enable_if<is_floating_point<TOut>::value, TOut>::type convertFloat( | ||||||
|  |     TIn value) { | ||||||
|  |   return TOut(value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename TOut, typename TIn> | ||||||
|  | typename enable_if<!is_floating_point<TOut>::value, TOut>::type convertFloat( | ||||||
|  |     TIn value) { | ||||||
|  |   return value >= numeric_limits<TOut>::lowest() && | ||||||
|  |                  value <= numeric_limits<TOut>::highest() | ||||||
|  |              ? TOut(value) | ||||||
|  |              : 0; | ||||||
|  | } | ||||||
|  | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
|  |  | ||||||
|  | #if defined(__clang__) | ||||||
|  | #pragma clang diagnostic pop | ||||||
|  | #elif defined(__GNUC__) | ||||||
|  | #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) | ||||||
|  | #pragma GCC diagnostic pop | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
| @@ -1,36 +0,0 @@ | |||||||
| // ArduinoJson - arduinojson.org |  | ||||||
| // Copyright Benoit Blanchon 2014-2019 |  | ||||||
| // MIT License |  | ||||||
|  |  | ||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include <string.h>  // for strcmp |  | ||||||
| #include "../Polyfills/ctype.hpp" |  | ||||||
|  |  | ||||||
| namespace ARDUINOJSON_NAMESPACE { |  | ||||||
|  |  | ||||||
| inline bool isFloat(const char* s) { |  | ||||||
|   if (!s) return false; |  | ||||||
|  |  | ||||||
|   if (!strcmp(s, "NaN")) return true; |  | ||||||
|   if (issign(*s)) s++; |  | ||||||
|   if (!strcmp(s, "Infinity")) return true; |  | ||||||
|   if (*s == '\0') return false; |  | ||||||
|  |  | ||||||
|   while (isdigit(*s)) s++; |  | ||||||
|  |  | ||||||
|   if (*s == '.') { |  | ||||||
|     s++; |  | ||||||
|     while (isdigit(*s)) s++; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (*s == 'e' || *s == 'E') { |  | ||||||
|     s++; |  | ||||||
|     if (issign(*s)) s++; |  | ||||||
|     if (!isdigit(*s)) return false; |  | ||||||
|     while (isdigit(*s)) s++; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return *s == '\0'; |  | ||||||
| } |  | ||||||
| }  // namespace ARDUINOJSON_NAMESPACE |  | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| // ArduinoJson - arduinojson.org |  | ||||||
| // Copyright Benoit Blanchon 2014-2019 |  | ||||||
| // MIT License |  | ||||||
|  |  | ||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include "../Polyfills/ctype.hpp" |  | ||||||
|  |  | ||||||
| namespace ARDUINOJSON_NAMESPACE { |  | ||||||
|  |  | ||||||
| inline bool isInteger(const char* s) { |  | ||||||
|   if (!s || !*s) return false; |  | ||||||
|   if (issign(*s)) s++; |  | ||||||
|   while (isdigit(*s)) s++; |  | ||||||
|   return *s == '\0'; |  | ||||||
| } |  | ||||||
| }  // namespace ARDUINOJSON_NAMESPACE |  | ||||||
| @@ -4,85 +4,15 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "../Numbers/FloatTraits.hpp" | #include "convertNumber.hpp" | ||||||
| #include "../Polyfills/ctype.hpp" | #include "parseNumber.hpp" | ||||||
| #include "../Polyfills/math.hpp" |  | ||||||
|  |  | ||||||
| namespace ARDUINOJSON_NAMESPACE { | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
| template <typename T> | template <typename T> | ||||||
| inline T parseFloat(const char* s) { | inline T parseFloat(const char* s) { | ||||||
|   typedef FloatTraits<T> traits; |   // try to reuse the same parameters as JsonDeserializer | ||||||
|   typedef typename traits::mantissa_type mantissa_t; |   typedef typename choose_largest<Float, T>::type TFloat; | ||||||
|   typedef typename traits::exponent_type exponent_t; |   return parseNumber<TFloat, UInt>(s).template as<T>(); | ||||||
|  |  | ||||||
|   if (!s) return 0;  // NULL |  | ||||||
|  |  | ||||||
|   bool negative_result = false; |  | ||||||
|   switch (*s) { |  | ||||||
|     case '-': |  | ||||||
|       negative_result = true; |  | ||||||
|       s++; |  | ||||||
|       break; |  | ||||||
|     case '+': |  | ||||||
|       s++; |  | ||||||
|       break; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (*s == 't') return 1;  // true |  | ||||||
|   if (*s == 'n' || *s == 'N') return traits::nan(); |  | ||||||
|   if (*s == 'i' || *s == 'I') |  | ||||||
|     return negative_result ? -traits::inf() : traits::inf(); |  | ||||||
|  |  | ||||||
|   mantissa_t mantissa = 0; |  | ||||||
|   exponent_t exponent_offset = 0; |  | ||||||
|  |  | ||||||
|   while (isdigit(*s)) { |  | ||||||
|     if (mantissa < traits::mantissa_max / 10) |  | ||||||
|       mantissa = mantissa * 10 + (*s - '0'); |  | ||||||
|     else |  | ||||||
|       exponent_offset++; |  | ||||||
|     s++; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (*s == '.') { |  | ||||||
|     s++; |  | ||||||
|     while (isdigit(*s)) { |  | ||||||
|       if (mantissa < traits::mantissa_max / 10) { |  | ||||||
|         mantissa = mantissa * 10 + (*s - '0'); |  | ||||||
|         exponent_offset--; |  | ||||||
|       } |  | ||||||
|       s++; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   int exponent = 0; |  | ||||||
|   if (*s == 'e' || *s == 'E') { |  | ||||||
|     s++; |  | ||||||
|     bool negative_exponent = false; |  | ||||||
|     if (*s == '-') { |  | ||||||
|       negative_exponent = true; |  | ||||||
|       s++; |  | ||||||
|     } else if (*s == '+') { |  | ||||||
|       s++; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     while (isdigit(*s)) { |  | ||||||
|       exponent = exponent * 10 + (*s - '0'); |  | ||||||
|       if (exponent + exponent_offset > traits::exponent_max) { |  | ||||||
|         if (negative_exponent) |  | ||||||
|           return negative_result ? -0.0f : 0.0f; |  | ||||||
|         else |  | ||||||
|           return negative_result ? -traits::inf() : traits::inf(); |  | ||||||
|       } |  | ||||||
|       s++; |  | ||||||
|     } |  | ||||||
|     if (negative_exponent) exponent = -exponent; |  | ||||||
|   } |  | ||||||
|   exponent += exponent_offset; |  | ||||||
|  |  | ||||||
|   T result = traits::make_float(static_cast<T>(mantissa), exponent); |  | ||||||
|  |  | ||||||
|   return negative_result ? -result : result; |  | ||||||
| } | } | ||||||
| }  // namespace ARDUINOJSON_NAMESPACE | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
|   | |||||||
| @@ -4,34 +4,16 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "../Configuration.hpp" | #include "../Polyfills/type_traits.hpp" | ||||||
| #include "../Polyfills/ctype.hpp" | #include "convertNumber.hpp" | ||||||
|  | #include "parseNumber.hpp" | ||||||
|  |  | ||||||
| namespace ARDUINOJSON_NAMESPACE { | namespace ARDUINOJSON_NAMESPACE { | ||||||
| template <typename T> | template <typename T> | ||||||
| T parseInteger(const char *s) { | T parseInteger(const char *s) { | ||||||
|   if (!s) return 0;  // NULL |   // try to reuse the same parameters as JsonDeserializer | ||||||
|  |   typedef typename choose_largest<UInt, typename make_unsigned<T>::type>::type | ||||||
|   if (*s == 't') return 1;  // "true" |       TUInt; | ||||||
|  |   return parseNumber<Float, TUInt>(s).template as<T>(); | ||||||
|   T result = 0; |  | ||||||
|   bool negative_result = false; |  | ||||||
|  |  | ||||||
|   switch (*s) { |  | ||||||
|     case '-': |  | ||||||
|       negative_result = true; |  | ||||||
|       s++; |  | ||||||
|       break; |  | ||||||
|     case '+': |  | ||||||
|       s++; |  | ||||||
|       break; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   while (isdigit(*s)) { |  | ||||||
|     result = T(result * 10 + T(*s - '0')); |  | ||||||
|     s++; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return negative_result ? T(~result + 1) : result; |  | ||||||
| } | } | ||||||
| }  // namespace ARDUINOJSON_NAMESPACE | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
|   | |||||||
							
								
								
									
										147
									
								
								src/ArduinoJson/Numbers/parseNumber.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								src/ArduinoJson/Numbers/parseNumber.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2019 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "../Polyfills/assert.hpp" | ||||||
|  | #include "../Polyfills/ctype.hpp" | ||||||
|  | #include "../Polyfills/math.hpp" | ||||||
|  | #include "../Polyfills/type_traits.hpp" | ||||||
|  | #include "../Variant/VariantContent.hpp" | ||||||
|  | #include "FloatTraits.hpp" | ||||||
|  | #include "convertNumber.hpp" | ||||||
|  |  | ||||||
|  | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
|  | template <typename TFloat, typename TUInt> | ||||||
|  | struct ParsedNumber { | ||||||
|  |   ParsedNumber() : uintValue(0), floatValue(0), _type(VALUE_IS_NULL) {} | ||||||
|  |  | ||||||
|  |   ParsedNumber(TUInt value, bool is_negative) | ||||||
|  |       : uintValue(value), | ||||||
|  |         floatValue(TFloat(value)), | ||||||
|  |         _type(uint8_t(is_negative ? VALUE_IS_NEGATIVE_INTEGER | ||||||
|  |                                   : VALUE_IS_POSITIVE_INTEGER)) {} | ||||||
|  |   ParsedNumber(TFloat value) : floatValue(value), _type(VALUE_IS_FLOAT) {} | ||||||
|  |  | ||||||
|  |   template <typename T> | ||||||
|  |   T as() const { | ||||||
|  |     switch (_type) { | ||||||
|  |       case VALUE_IS_NEGATIVE_INTEGER: | ||||||
|  |         return convertNegativeInteger<T>(uintValue); | ||||||
|  |       case VALUE_IS_POSITIVE_INTEGER: | ||||||
|  |         return convertPositiveInteger<T>(uintValue); | ||||||
|  |       case VALUE_IS_FLOAT: | ||||||
|  |         return convertFloat<T>(floatValue); | ||||||
|  |       default: | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   uint8_t type() const { | ||||||
|  |     return _type; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   TUInt uintValue; | ||||||
|  |   TFloat floatValue; | ||||||
|  |   uint8_t _type; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template <typename A, typename B> | ||||||
|  | struct choose_largest : conditional<(sizeof(A) > sizeof(B)), A, B> {}; | ||||||
|  |  | ||||||
|  | template <typename TFloat, typename TUInt> | ||||||
|  | inline ParsedNumber<TFloat, TUInt> parseNumber(const char *s) { | ||||||
|  |   typedef FloatTraits<TFloat> traits; | ||||||
|  |   typedef typename choose_largest<typename traits::mantissa_type, TUInt>::type | ||||||
|  |       mantissa_t; | ||||||
|  |   typedef typename traits::exponent_type exponent_t; | ||||||
|  |   typedef ParsedNumber<TFloat, TUInt> return_type; | ||||||
|  |  | ||||||
|  |   ARDUINOJSON_ASSERT(s != 0); | ||||||
|  |  | ||||||
|  |   bool is_negative = false; | ||||||
|  |   switch (*s) { | ||||||
|  |     case '-': | ||||||
|  |       is_negative = true; | ||||||
|  |       s++; | ||||||
|  |       break; | ||||||
|  |     case '+': | ||||||
|  |       s++; | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (*s == 'n' || *s == 'N') return traits::nan(); | ||||||
|  |   if (*s == 'i' || *s == 'I') | ||||||
|  |     return is_negative ? -traits::inf() : traits::inf(); | ||||||
|  |   if (!isdigit(*s) && *s != '.') return return_type(); | ||||||
|  |  | ||||||
|  |   mantissa_t mantissa = 0; | ||||||
|  |   exponent_t exponent_offset = 0; | ||||||
|  |   const mantissa_t maxUint = TUInt(-1); | ||||||
|  |  | ||||||
|  |   while (isdigit(*s)) { | ||||||
|  |     uint8_t digit = uint8_t(*s - '0'); | ||||||
|  |     if (mantissa > maxUint / 10) break; | ||||||
|  |     mantissa *= 10; | ||||||
|  |     if (mantissa > maxUint - digit) break; | ||||||
|  |     mantissa += digit; | ||||||
|  |     s++; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (*s == '\0') return return_type(TUInt(mantissa), is_negative); | ||||||
|  |  | ||||||
|  |   // avoid mantissa overflow | ||||||
|  |   while (mantissa > traits::mantissa_max) { | ||||||
|  |     mantissa /= 10; | ||||||
|  |     exponent_offset++; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // remaing digits can't fit in the mantissa | ||||||
|  |   while (isdigit(*s)) { | ||||||
|  |     exponent_offset++; | ||||||
|  |     s++; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (*s == '.') { | ||||||
|  |     s++; | ||||||
|  |     while (isdigit(*s)) { | ||||||
|  |       if (mantissa < traits::mantissa_max / 10) { | ||||||
|  |         mantissa = mantissa * 10 + uint8_t(*s - '0'); | ||||||
|  |         exponent_offset--; | ||||||
|  |       } | ||||||
|  |       s++; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   int exponent = 0; | ||||||
|  |   if (*s == 'e' || *s == 'E') { | ||||||
|  |     s++; | ||||||
|  |     bool negative_exponent = false; | ||||||
|  |     if (*s == '-') { | ||||||
|  |       negative_exponent = true; | ||||||
|  |       s++; | ||||||
|  |     } else if (*s == '+') { | ||||||
|  |       s++; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     while (isdigit(*s)) { | ||||||
|  |       exponent = exponent * 10 + (*s - '0'); | ||||||
|  |       if (exponent + exponent_offset > traits::exponent_max) { | ||||||
|  |         if (negative_exponent) | ||||||
|  |           return is_negative ? -0.0f : 0.0f; | ||||||
|  |         else | ||||||
|  |           return is_negative ? -traits::inf() : traits::inf(); | ||||||
|  |       } | ||||||
|  |       s++; | ||||||
|  |     } | ||||||
|  |     if (negative_exponent) exponent = -exponent; | ||||||
|  |   } | ||||||
|  |   exponent += exponent_offset; | ||||||
|  |  | ||||||
|  |   TFloat result = traits::make_float(static_cast<TFloat>(mantissa), exponent); | ||||||
|  |  | ||||||
|  |   return is_negative ? -result : result; | ||||||
|  | } | ||||||
|  | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
| @@ -33,3 +33,13 @@ | |||||||
| #else | #else | ||||||
| #define NOEXCEPT throw() | #define NOEXCEPT throw() | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #if defined(__has_attribute) | ||||||
|  | #if __has_attribute(no_sanitize) | ||||||
|  | #define ARDUINOJSON_NO_SANITIZE(check) __attribute__((no_sanitize(check))) | ||||||
|  | #else | ||||||
|  | #define ARDUINOJSON_NO_SANITIZE(check) | ||||||
|  | #endif | ||||||
|  | #else | ||||||
|  | #define ARDUINOJSON_NO_SANITIZE(check) | ||||||
|  | #endif | ||||||
|   | |||||||
							
								
								
									
										45
									
								
								src/ArduinoJson/Polyfills/limits.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/ArduinoJson/Polyfills/limits.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2019 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "../Polyfills/type_traits.hpp" | ||||||
|  |  | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | #pragma warning(push) | ||||||
|  | #pragma warning(disable : 4310) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
|  | // Differs from standard because we can't use the symbols "min" and "max" | ||||||
|  | template <typename T, typename Enable = void> | ||||||
|  | struct numeric_limits; | ||||||
|  |  | ||||||
|  | template <typename T> | ||||||
|  | struct numeric_limits<T, typename enable_if<is_unsigned<T>::value>::type> { | ||||||
|  |   static T lowest() { | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |   static T highest() { | ||||||
|  |     return T(-1); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template <typename T> | ||||||
|  | struct numeric_limits< | ||||||
|  |     T, typename enable_if<is_integral<T>::value && is_signed<T>::value>::type> { | ||||||
|  |   static T lowest() { | ||||||
|  |     return T(T(1) << (sizeof(T) * 8 - 1)); | ||||||
|  |   } | ||||||
|  |   static T highest() { | ||||||
|  |     return T(~lowest()); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
|  |  | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | #pragma warning(pop) | ||||||
|  | #endif | ||||||
| @@ -15,5 +15,6 @@ | |||||||
| #include "type_traits/is_same.hpp" | #include "type_traits/is_same.hpp" | ||||||
| #include "type_traits/is_signed.hpp" | #include "type_traits/is_signed.hpp" | ||||||
| #include "type_traits/is_unsigned.hpp" | #include "type_traits/is_unsigned.hpp" | ||||||
|  | #include "type_traits/make_unsigned.hpp" | ||||||
| #include "type_traits/remove_const.hpp" | #include "type_traits/remove_const.hpp" | ||||||
| #include "type_traits/remove_reference.hpp" | #include "type_traits/remove_reference.hpp" | ||||||
|   | |||||||
							
								
								
									
										49
									
								
								src/ArduinoJson/Polyfills/type_traits/make_unsigned.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/ArduinoJson/Polyfills/type_traits/make_unsigned.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2019 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "type_identity.hpp" | ||||||
|  | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
|  | template <typename T> | ||||||
|  | struct make_unsigned; | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | struct make_unsigned<char> : type_identity<unsigned char> {}; | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | struct make_unsigned<signed char> : type_identity<unsigned char> {}; | ||||||
|  | template <> | ||||||
|  | struct make_unsigned<unsigned char> : type_identity<unsigned char> {}; | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | struct make_unsigned<signed short> : type_identity<unsigned short> {}; | ||||||
|  | template <> | ||||||
|  | struct make_unsigned<unsigned short> : type_identity<unsigned short> {}; | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | struct make_unsigned<signed int> : type_identity<unsigned int> {}; | ||||||
|  | template <> | ||||||
|  | struct make_unsigned<unsigned int> : type_identity<unsigned int> {}; | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | struct make_unsigned<signed long> : type_identity<unsigned long> {}; | ||||||
|  | template <> | ||||||
|  | struct make_unsigned<unsigned long> : type_identity<unsigned long> {}; | ||||||
|  |  | ||||||
|  | #if ARDUINOJSON_HAS_LONG_LONG | ||||||
|  | template <> | ||||||
|  | struct make_unsigned<signed long long> : type_identity<unsigned long long> {}; | ||||||
|  | template <> | ||||||
|  | struct make_unsigned<unsigned long long> : type_identity<unsigned long long> {}; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #if ARDUINOJSON_HAS_INT64 | ||||||
|  | template <> | ||||||
|  | struct make_unsigned<signed __int64> : type_identity<unsigned __int64> {}; | ||||||
|  | template <> | ||||||
|  | struct make_unsigned<unsigned __int64> : type_identity<unsigned __int64> {}; | ||||||
|  | #endif | ||||||
|  | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
							
								
								
									
										15
									
								
								src/ArduinoJson/Polyfills/type_traits/type_identity.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/ArduinoJson/Polyfills/type_traits/type_identity.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2019 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "integral_constant.hpp" | ||||||
|  |  | ||||||
|  | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
|  | template <typename T> | ||||||
|  | struct type_identity { | ||||||
|  |   typedef T type; | ||||||
|  | }; | ||||||
|  | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
| @@ -5,6 +5,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "../Misc/SerializedValue.hpp" | #include "../Misc/SerializedValue.hpp" | ||||||
|  | #include "../Numbers/convertNumber.hpp" | ||||||
| #include "../Polyfills/gsl/not_null.hpp" | #include "../Polyfills/gsl/not_null.hpp" | ||||||
| #include "VariantContent.hpp" | #include "VariantContent.hpp" | ||||||
|  |  | ||||||
| @@ -63,9 +64,7 @@ class VariantData { | |||||||
|  |  | ||||||
|   const char *asString() const; |   const char *asString() const; | ||||||
|  |  | ||||||
|   bool asBoolean() const { |   bool asBoolean() const; | ||||||
|     return asIntegral<int>() != 0; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   CollectionData *asArray() { |   CollectionData *asArray() { | ||||||
|     return isArray() ? &_content.asCollection : 0; |     return isArray() ? &_content.asCollection : 0; | ||||||
| @@ -147,9 +146,18 @@ class VariantData { | |||||||
|     return (_flags & COLLECTION_MASK) != 0; |     return (_flags & COLLECTION_MASK) != 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   template <typename T> | ||||||
|   bool isInteger() const { |   bool isInteger() const { | ||||||
|     return type() == VALUE_IS_POSITIVE_INTEGER || |     switch (type()) { | ||||||
|            type() == VALUE_IS_NEGATIVE_INTEGER; |       case VALUE_IS_POSITIVE_INTEGER: | ||||||
|  |         return canStorePositiveInteger<T>(_content.asInteger); | ||||||
|  |  | ||||||
|  |       case VALUE_IS_NEGATIVE_INTEGER: | ||||||
|  |         return canStoreNegativeInteger<T>(_content.asInteger); | ||||||
|  |  | ||||||
|  |       default: | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool isFloat() const { |   bool isFloat() const { | ||||||
| @@ -225,14 +233,22 @@ class VariantData { | |||||||
|   template <typename T> |   template <typename T> | ||||||
|   void setSignedInteger(T value) { |   void setSignedInteger(T value) { | ||||||
|     if (value >= 0) { |     if (value >= 0) { | ||||||
|       setType(VALUE_IS_POSITIVE_INTEGER); |       setPositiveInteger(static_cast<UInt>(value)); | ||||||
|       _content.asInteger = static_cast<UInt>(value); |  | ||||||
|     } else { |     } else { | ||||||
|       setType(VALUE_IS_NEGATIVE_INTEGER); |       setNegativeInteger(~static_cast<UInt>(value) + 1); | ||||||
|       _content.asInteger = ~static_cast<UInt>(value) + 1; |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   void setPositiveInteger(UInt value) { | ||||||
|  |     setType(VALUE_IS_POSITIVE_INTEGER); | ||||||
|  |     _content.asInteger = value; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void setNegativeInteger(UInt value) { | ||||||
|  |     setType(VALUE_IS_NEGATIVE_INTEGER); | ||||||
|  |     _content.asInteger = value; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   void setLinkedString(const char *value) { |   void setLinkedString(const char *value) { | ||||||
|     if (value) { |     if (value) { | ||||||
|       setType(VALUE_IS_LINKED_STRING); |       setType(VALUE_IS_LINKED_STRING); | ||||||
|   | |||||||
| @@ -52,8 +52,9 @@ inline bool variantIsBoolean(const VariantData *var) { | |||||||
|   return var && var->isBoolean(); |   return var && var->isBoolean(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | template <typename T> | ||||||
| inline bool variantIsInteger(const VariantData *var) { | inline bool variantIsInteger(const VariantData *var) { | ||||||
|   return var && var->isInteger(); |   return var && var->isInteger<T>(); | ||||||
| } | } | ||||||
|  |  | ||||||
| inline bool variantIsFloat(const VariantData *var) { | inline bool variantIsFloat(const VariantData *var) { | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "../Configuration.hpp" | #include "../Configuration.hpp" | ||||||
|  | #include "../Numbers/convertNumber.hpp" | ||||||
| #include "../Numbers/parseFloat.hpp" | #include "../Numbers/parseFloat.hpp" | ||||||
| #include "../Numbers/parseInteger.hpp" | #include "../Numbers/parseInteger.hpp" | ||||||
| #include "VariantRef.hpp" | #include "VariantRef.hpp" | ||||||
| @@ -18,19 +19,35 @@ inline T VariantData::asIntegral() const { | |||||||
|   switch (type()) { |   switch (type()) { | ||||||
|     case VALUE_IS_POSITIVE_INTEGER: |     case VALUE_IS_POSITIVE_INTEGER: | ||||||
|     case VALUE_IS_BOOLEAN: |     case VALUE_IS_BOOLEAN: | ||||||
|       return T(_content.asInteger); |       return convertPositiveInteger<T>(_content.asInteger); | ||||||
|     case VALUE_IS_NEGATIVE_INTEGER: |     case VALUE_IS_NEGATIVE_INTEGER: | ||||||
|       return T(~_content.asInteger + 1); |       return convertNegativeInteger<T>(_content.asInteger); | ||||||
|     case VALUE_IS_LINKED_STRING: |     case VALUE_IS_LINKED_STRING: | ||||||
|     case VALUE_IS_OWNED_STRING: |     case VALUE_IS_OWNED_STRING: | ||||||
|       return parseInteger<T>(_content.asString); |       return parseInteger<T>(_content.asString); | ||||||
|     case VALUE_IS_FLOAT: |     case VALUE_IS_FLOAT: | ||||||
|       return T(_content.asFloat); |       return convertFloat<T>(_content.asFloat); | ||||||
|     default: |     default: | ||||||
|       return 0; |       return 0; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | inline bool VariantData::asBoolean() const { | ||||||
|  |   switch (type()) { | ||||||
|  |     case VALUE_IS_POSITIVE_INTEGER: | ||||||
|  |     case VALUE_IS_BOOLEAN: | ||||||
|  |     case VALUE_IS_NEGATIVE_INTEGER: | ||||||
|  |       return _content.asInteger != 0; | ||||||
|  |     case VALUE_IS_FLOAT: | ||||||
|  |       return _content.asFloat != 0; | ||||||
|  |     case VALUE_IS_LINKED_STRING: | ||||||
|  |     case VALUE_IS_OWNED_STRING: | ||||||
|  |       return strcmp("true", _content.asString) == 0; | ||||||
|  |     default: | ||||||
|  |       return false; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| // T = float/double | // T = float/double | ||||||
| template <typename T> | template <typename T> | ||||||
| inline T VariantData::asFloat() const { | inline T VariantData::asFloat() const { | ||||||
|   | |||||||
| @@ -9,8 +9,6 @@ | |||||||
|  |  | ||||||
| #include "../Memory/MemoryPool.hpp" | #include "../Memory/MemoryPool.hpp" | ||||||
| #include "../Misc/Visitable.hpp" | #include "../Misc/Visitable.hpp" | ||||||
| #include "../Numbers/parseFloat.hpp" |  | ||||||
| #include "../Numbers/parseInteger.hpp" |  | ||||||
| #include "../Operators/VariantOperators.hpp" | #include "../Operators/VariantOperators.hpp" | ||||||
| #include "../Polyfills/type_traits.hpp" | #include "../Polyfills/type_traits.hpp" | ||||||
| #include "VariantAs.hpp" | #include "VariantAs.hpp" | ||||||
| @@ -45,7 +43,7 @@ class VariantRefBase { | |||||||
|   template <typename T> |   template <typename T> | ||||||
|   FORCE_INLINE typename enable_if<is_integral<T>::value, bool>::type is() |   FORCE_INLINE typename enable_if<is_integral<T>::value, bool>::type is() | ||||||
|       const { |       const { | ||||||
|     return variantIsInteger(_data); |     return variantIsInteger<T>(_data); | ||||||
|   } |   } | ||||||
|   // |   // | ||||||
|   // bool is<double>() const; |   // bool is<double>() const; | ||||||
|   | |||||||
| @@ -22,7 +22,8 @@ TEST_CASE("JsonArray::operator[]") { | |||||||
|   SECTION("long long") { |   SECTION("long long") { | ||||||
|     array[0] = 9223372036854775807; |     array[0] = 9223372036854775807; | ||||||
|     REQUIRE(9223372036854775807 == array[0].as<int64_t>()); |     REQUIRE(9223372036854775807 == array[0].as<int64_t>()); | ||||||
|     REQUIRE(true == array[0].is<int>()); |     REQUIRE(true == array[0].is<int64_t>()); | ||||||
|  |     REQUIRE(false == array[0].is<int32_t>()); | ||||||
|     REQUIRE(false == array[0].is<bool>()); |     REQUIRE(false == array[0].is<bool>()); | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -3,17 +3,18 @@ | |||||||
| # MIT License | # MIT License | ||||||
|  |  | ||||||
| add_executable(JsonDeserializerTests | add_executable(JsonDeserializerTests | ||||||
|  | 	array.cpp | ||||||
|  | 	array_static.cpp | ||||||
| 	DeserializationError.cpp | 	DeserializationError.cpp | ||||||
| 	deserializeJsonArray.cpp |  | ||||||
| 	deserializeJsonArrayStatic.cpp |  | ||||||
| 	deserializeJsonObject.cpp |  | ||||||
| 	deserializeJsonObjectStatic.cpp |  | ||||||
| 	deserializeJsonValue.cpp |  | ||||||
| 	deserializeJsonString.cpp |  | ||||||
| 	input_types.cpp |  | ||||||
| 	incomplete_input.cpp | 	incomplete_input.cpp | ||||||
|  | 	input_types.cpp | ||||||
|  | 	number.cpp | ||||||
| 	invalid_input.cpp | 	invalid_input.cpp | ||||||
|  | 	misc.cpp | ||||||
| 	nestingLimit.cpp | 	nestingLimit.cpp | ||||||
|  | 	object.cpp | ||||||
|  | 	object_static.cpp | ||||||
|  | 	string.cpp | ||||||
| ) | ) | ||||||
|  |  | ||||||
| target_link_libraries(JsonDeserializerTests catch) | target_link_libraries(JsonDeserializerTests catch) | ||||||
|   | |||||||
| @@ -7,11 +7,6 @@ | |||||||
| 
 | 
 | ||||||
| using namespace Catch::Matchers; | using namespace Catch::Matchers; | ||||||
| 
 | 
 | ||||||
| namespace my { |  | ||||||
| using ARDUINOJSON_NAMESPACE::isinf; |  | ||||||
| using ARDUINOJSON_NAMESPACE::isnan; |  | ||||||
| }  // namespace my
 |  | ||||||
| 
 |  | ||||||
| TEST_CASE("deserializeJson(DynamicJsonDocument&)") { | TEST_CASE("deserializeJson(DynamicJsonDocument&)") { | ||||||
|   DynamicJsonDocument doc(4096); |   DynamicJsonDocument doc(4096); | ||||||
| 
 | 
 | ||||||
| @@ -48,64 +43,6 @@ TEST_CASE("deserializeJson(DynamicJsonDocument&)") { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   SECTION("Integers") { |  | ||||||
|     SECTION("0") { |  | ||||||
|       DeserializationError err = deserializeJson(doc, "0"); |  | ||||||
|       REQUIRE(err == DeserializationError::Ok); |  | ||||||
|       REQUIRE(doc.is<int>() == true); |  | ||||||
|       REQUIRE(doc.as<int>() == 0); |  | ||||||
|       REQUIRE(doc.as<std::string>() == "0");  // issue #808
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     SECTION("Negative") { |  | ||||||
|       DeserializationError err = deserializeJson(doc, "-42"); |  | ||||||
| 
 |  | ||||||
|       REQUIRE(err == DeserializationError::Ok); |  | ||||||
|       REQUIRE(doc.is<int>()); |  | ||||||
|       REQUIRE_FALSE(doc.is<bool>()); |  | ||||||
|       REQUIRE(doc.as<int>() == -42); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   SECTION("Floats") { |  | ||||||
|     SECTION("Double") { |  | ||||||
|       DeserializationError err = deserializeJson(doc, "-1.23e+4"); |  | ||||||
| 
 |  | ||||||
|       REQUIRE(err == DeserializationError::Ok); |  | ||||||
|       REQUIRE_FALSE(doc.is<int>()); |  | ||||||
|       REQUIRE(doc.is<double>()); |  | ||||||
|       REQUIRE(doc.as<double>() == Approx(-1.23e+4)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     SECTION("NaN") { |  | ||||||
|       DeserializationError err = deserializeJson(doc, "NaN"); |  | ||||||
|       REQUIRE(err == DeserializationError::Ok); |  | ||||||
|       REQUIRE(doc.is<float>() == true); |  | ||||||
|       REQUIRE(my::isnan(doc.as<float>())); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     SECTION("Infinity") { |  | ||||||
|       DeserializationError err = deserializeJson(doc, "Infinity"); |  | ||||||
|       REQUIRE(err == DeserializationError::Ok); |  | ||||||
|       REQUIRE(doc.is<float>() == true); |  | ||||||
|       REQUIRE(my::isinf(doc.as<float>())); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     SECTION("+Infinity") { |  | ||||||
|       DeserializationError err = deserializeJson(doc, "+Infinity"); |  | ||||||
|       REQUIRE(err == DeserializationError::Ok); |  | ||||||
|       REQUIRE(doc.is<float>() == true); |  | ||||||
|       REQUIRE(my::isinf(doc.as<float>())); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     SECTION("-Infinity") { |  | ||||||
|       DeserializationError err = deserializeJson(doc, "-Infinity"); |  | ||||||
|       REQUIRE(err == DeserializationError::Ok); |  | ||||||
|       REQUIRE(doc.is<float>() == true); |  | ||||||
|       REQUIRE(my::isinf(doc.as<float>())); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   SECTION("Booleans") { |   SECTION("Booleans") { | ||||||
|     SECTION("True") { |     SECTION("True") { | ||||||
|       DeserializationError err = deserializeJson(doc, "true"); |       DeserializationError err = deserializeJson(doc, "true"); | ||||||
							
								
								
									
										131
									
								
								test/JsonDeserializer/number.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								test/JsonDeserializer/number.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,131 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2019 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #define ARDUINOJSON_USE_LONG_LONG 0 | ||||||
|  |  | ||||||
|  | #include <ArduinoJson.h> | ||||||
|  | #include <limits.h> | ||||||
|  | #include <catch.hpp> | ||||||
|  |  | ||||||
|  | namespace my { | ||||||
|  | using ARDUINOJSON_NAMESPACE::isinf; | ||||||
|  | using ARDUINOJSON_NAMESPACE::isnan; | ||||||
|  | }  // namespace my | ||||||
|  |  | ||||||
|  | TEST_CASE("deserialize an integer") { | ||||||
|  |   DynamicJsonDocument doc(4096); | ||||||
|  |  | ||||||
|  |   SECTION("Integer") { | ||||||
|  |     SECTION("0") { | ||||||
|  |       DeserializationError err = deserializeJson(doc, "0"); | ||||||
|  |       REQUIRE(err == DeserializationError::Ok); | ||||||
|  |       REQUIRE(doc.is<int>() == true); | ||||||
|  |       REQUIRE(doc.as<int>() == 0); | ||||||
|  |       REQUIRE(doc.as<std::string>() == "0");  // issue #808 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SECTION("Negative") { | ||||||
|  |       DeserializationError err = deserializeJson(doc, "-42"); | ||||||
|  |  | ||||||
|  |       REQUIRE(err == DeserializationError::Ok); | ||||||
|  |       REQUIRE(doc.is<int>()); | ||||||
|  |       REQUIRE_FALSE(doc.is<bool>()); | ||||||
|  |       REQUIRE(doc.as<int>() == -42); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | #if LONG_MAX == 2147483647 | ||||||
|  |     SECTION("LONG_MAX") { | ||||||
|  |       DeserializationError err = deserializeJson(doc, "2147483647"); | ||||||
|  |  | ||||||
|  |       REQUIRE(err == DeserializationError::Ok); | ||||||
|  |       REQUIRE(doc.is<long>() == true); | ||||||
|  |       REQUIRE(doc.as<long>() == LONG_MAX); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SECTION("LONG_MAX + 1") { | ||||||
|  |       DeserializationError err = deserializeJson(doc, "2147483648"); | ||||||
|  |  | ||||||
|  |       CAPTURE(LONG_MIN); | ||||||
|  |       REQUIRE(err == DeserializationError::Ok); | ||||||
|  |       REQUIRE(doc.is<long>() == false); | ||||||
|  |       REQUIRE(doc.is<float>() == true); | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #if LONG_MIN == -2147483648 | ||||||
|  |     SECTION("LONG_MIN") { | ||||||
|  |       DeserializationError err = deserializeJson(doc, "-2147483648"); | ||||||
|  |       REQUIRE(err == DeserializationError::Ok); | ||||||
|  |       REQUIRE(doc.is<long>() == true); | ||||||
|  |       REQUIRE(doc.as<long>() == LONG_MIN); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SECTION("LONG_MIN - 1") { | ||||||
|  |       DeserializationError err = deserializeJson(doc, "-2147483649"); | ||||||
|  |  | ||||||
|  |       REQUIRE(err == DeserializationError::Ok); | ||||||
|  |       REQUIRE(doc.is<long>() == false); | ||||||
|  |       REQUIRE(doc.is<float>() == true); | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #if ULONG_MAX == 4294967295 | ||||||
|  |     SECTION("ULONG_MAX") { | ||||||
|  |       DeserializationError err = deserializeJson(doc, "4294967295"); | ||||||
|  |  | ||||||
|  |       REQUIRE(err == DeserializationError::Ok); | ||||||
|  |       REQUIRE(doc.is<unsigned long>() == true); | ||||||
|  |       REQUIRE(doc.as<unsigned long>() == ULONG_MAX); | ||||||
|  |       REQUIRE(doc.is<long>() == false); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SECTION("ULONG_MAX + 1") { | ||||||
|  |       DeserializationError err = deserializeJson(doc, "4294967296"); | ||||||
|  |  | ||||||
|  |       REQUIRE(err == DeserializationError::Ok); | ||||||
|  |       REQUIRE(doc.is<unsigned long>() == false); | ||||||
|  |       REQUIRE(doc.is<float>() == true); | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Floats") { | ||||||
|  |     SECTION("Double") { | ||||||
|  |       DeserializationError err = deserializeJson(doc, "-1.23e+4"); | ||||||
|  |  | ||||||
|  |       REQUIRE(err == DeserializationError::Ok); | ||||||
|  |       REQUIRE_FALSE(doc.is<int>()); | ||||||
|  |       REQUIRE(doc.is<double>()); | ||||||
|  |       REQUIRE(doc.as<double>() == Approx(-1.23e+4)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SECTION("NaN") { | ||||||
|  |       DeserializationError err = deserializeJson(doc, "NaN"); | ||||||
|  |       REQUIRE(err == DeserializationError::Ok); | ||||||
|  |       REQUIRE(doc.is<float>() == true); | ||||||
|  |       REQUIRE(my::isnan(doc.as<float>())); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SECTION("Infinity") { | ||||||
|  |       DeserializationError err = deserializeJson(doc, "Infinity"); | ||||||
|  |       REQUIRE(err == DeserializationError::Ok); | ||||||
|  |       REQUIRE(doc.is<float>() == true); | ||||||
|  |       REQUIRE(my::isinf(doc.as<float>())); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SECTION("+Infinity") { | ||||||
|  |       DeserializationError err = deserializeJson(doc, "+Infinity"); | ||||||
|  |       REQUIRE(err == DeserializationError::Ok); | ||||||
|  |       REQUIRE(doc.is<float>() == true); | ||||||
|  |       REQUIRE(my::isinf(doc.as<float>())); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SECTION("-Infinity") { | ||||||
|  |       DeserializationError err = deserializeJson(doc, "-Infinity"); | ||||||
|  |       REQUIRE(err == DeserializationError::Ok); | ||||||
|  |       REQUIRE(doc.is<float>() == true); | ||||||
|  |       REQUIRE(my::isinf(doc.as<float>())); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -15,6 +15,7 @@ add_executable(JsonVariantTests | |||||||
| 	misc.cpp | 	misc.cpp | ||||||
| 	nesting.cpp | 	nesting.cpp | ||||||
| 	or.cpp | 	or.cpp | ||||||
|  | 	overflow.cpp | ||||||
| 	remove.cpp | 	remove.cpp | ||||||
| 	set.cpp | 	set.cpp | ||||||
| 	subscript.cpp | 	subscript.cpp | ||||||
|   | |||||||
| @@ -6,6 +6,10 @@ | |||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <catch.hpp> | #include <catch.hpp> | ||||||
|  |  | ||||||
|  | namespace my { | ||||||
|  | using ARDUINOJSON_NAMESPACE::isinf; | ||||||
|  | }  // namespace my | ||||||
|  |  | ||||||
| static const char* null = 0; | static const char* null = 0; | ||||||
|  |  | ||||||
| TEST_CASE("JsonVariant::as()") { | TEST_CASE("JsonVariant::as()") { | ||||||
| @@ -94,7 +98,6 @@ TEST_CASE("JsonVariant::as()") { | |||||||
|   SECTION("set(\"42\")") { |   SECTION("set(\"42\")") { | ||||||
|     variant.set("42"); |     variant.set("42"); | ||||||
|  |  | ||||||
|     REQUIRE(variant.as<bool>()); |  | ||||||
|     REQUIRE(variant.as<long>() == 42L); |     REQUIRE(variant.as<long>() == 42L); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -111,7 +114,6 @@ TEST_CASE("JsonVariant::as()") { | |||||||
|   SECTION("set(std::string(\"4.2\"))") { |   SECTION("set(std::string(\"4.2\"))") { | ||||||
|     variant.set(std::string("4.2")); |     variant.set(std::string("4.2")); | ||||||
|  |  | ||||||
|     REQUIRE(variant.as<bool>() == true); |  | ||||||
|     REQUIRE(variant.as<long>() == 4L); |     REQUIRE(variant.as<long>() == 4L); | ||||||
|     REQUIRE(variant.as<double>() == 4.2); |     REQUIRE(variant.as<double>() == 4.2); | ||||||
|     REQUIRE(variant.as<char*>() == std::string("4.2")); |     REQUIRE(variant.as<char*>() == std::string("4.2")); | ||||||
| @@ -121,8 +123,31 @@ TEST_CASE("JsonVariant::as()") { | |||||||
|   SECTION("set(\"true\")") { |   SECTION("set(\"true\")") { | ||||||
|     variant.set("true"); |     variant.set("true"); | ||||||
|  |  | ||||||
|     REQUIRE(variant.as<bool>()); |     REQUIRE(variant.as<bool>() == true); | ||||||
|     REQUIRE(variant.as<long>() == 1L); |     REQUIRE(variant.as<int>() == 0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("set(-1e300)") { | ||||||
|  |     variant.set(-1e300); | ||||||
|  |  | ||||||
|  |     REQUIRE(variant.as<double>() == -1e300); | ||||||
|  |     REQUIRE(variant.as<float>() < 0); | ||||||
|  |     REQUIRE(my::isinf(variant.as<float>())); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("set(1e300)") { | ||||||
|  |     variant.set(1e300); | ||||||
|  |  | ||||||
|  |     REQUIRE(variant.as<double>() == 1e300); | ||||||
|  |     REQUIRE(variant.as<float>() > 0); | ||||||
|  |     REQUIRE(my::isinf(variant.as<float>())); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("set(1e300)") { | ||||||
|  |     variant.set(1e-300); | ||||||
|  |  | ||||||
|  |     REQUIRE(variant.as<double>() == 1e-300); | ||||||
|  |     REQUIRE(variant.as<float>() == 0); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("to<JsonObject>()") { |   SECTION("to<JsonObject>()") { | ||||||
|   | |||||||
							
								
								
									
										72
									
								
								test/JsonVariant/overflow.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								test/JsonVariant/overflow.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2019 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #include <ArduinoJson.h> | ||||||
|  | #include <catch.hpp> | ||||||
|  |  | ||||||
|  | template <typename TOut, typename TIn> | ||||||
|  | void shouldBeOk(TIn value) { | ||||||
|  |   StaticJsonDocument<1> doc; | ||||||
|  |   JsonVariant var = doc.to<JsonVariant>(); | ||||||
|  |   var.set(value); | ||||||
|  |   REQUIRE(var.as<TOut>() == TOut(value)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename TOut, typename TIn> | ||||||
|  | void shouldOverflow(TIn value) { | ||||||
|  |   StaticJsonDocument<1> doc; | ||||||
|  |   JsonVariant var = doc.to<JsonVariant>(); | ||||||
|  |   var.set(value); | ||||||
|  |   REQUIRE(var.as<TOut>() == 0); | ||||||
|  |   REQUIRE(var.is<TOut>() == false); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_CASE("Handle integer overflow in stored integer") { | ||||||
|  |   SECTION("int8_t") { | ||||||
|  |     // ok | ||||||
|  |     shouldBeOk<int8_t>(-128); | ||||||
|  |     shouldBeOk<int8_t>(42.0); | ||||||
|  |     shouldBeOk<int8_t>(127); | ||||||
|  |  | ||||||
|  |     // too low | ||||||
|  |     shouldOverflow<int8_t>(-128.1); | ||||||
|  |     shouldOverflow<int8_t>(-129); | ||||||
|  |  | ||||||
|  |     // too high | ||||||
|  |     shouldOverflow<int8_t>(128); | ||||||
|  |     shouldOverflow<int8_t>(127.1); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("int16_t") { | ||||||
|  |     // ok | ||||||
|  |     shouldBeOk<int16_t>(-32768); | ||||||
|  |     shouldBeOk<int16_t>(-32767.9); | ||||||
|  |     shouldBeOk<int16_t>(32766.9); | ||||||
|  |     shouldBeOk<int16_t>(32767); | ||||||
|  |  | ||||||
|  |     // too low | ||||||
|  |     shouldOverflow<int16_t>(-32768.1); | ||||||
|  |     shouldOverflow<int16_t>(-32769); | ||||||
|  |  | ||||||
|  |     // too high | ||||||
|  |     shouldOverflow<int16_t>(32767.1); | ||||||
|  |     shouldOverflow<int16_t>(32768); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("uint8_t") { | ||||||
|  |     // ok | ||||||
|  |     shouldBeOk<uint8_t>(1); | ||||||
|  |     shouldBeOk<uint8_t>(42.0); | ||||||
|  |     shouldBeOk<uint8_t>(255); | ||||||
|  |  | ||||||
|  |     // too low | ||||||
|  |     shouldOverflow<uint8_t>(-1); | ||||||
|  |     shouldOverflow<uint8_t>(-0.1); | ||||||
|  |  | ||||||
|  |     // to high | ||||||
|  |     shouldOverflow<uint8_t>(255.1); | ||||||
|  |     shouldOverflow<uint8_t>(256); | ||||||
|  |     shouldOverflow<uint8_t>(257); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -16,6 +16,14 @@ static void check(const char* input, U expected) { | |||||||
|   REQUIRE(doc.as<T>() == expected); |   REQUIRE(doc.as<T>() == expected); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #if ARDUINOJSON_USE_LONG_LONG == 0 | ||||||
|  | static void checkNotSupported(const char* input) { | ||||||
|  |   DynamicJsonDocument doc(4096); | ||||||
|  |   DeserializationError error = deserializeMsgPack(doc, input); | ||||||
|  |   REQUIRE(error == DeserializationError::NotSupported); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| static void checkIsNull(const char* input) { | static void checkIsNull(const char* input) { | ||||||
|   DynamicJsonDocument doc(4096); |   DynamicJsonDocument doc(4096); | ||||||
|  |  | ||||||
| @@ -70,9 +78,9 @@ TEST_CASE("deserialize MsgPack value") { | |||||||
|     check<uint64_t>("\xCF\x12\x34\x56\x78\x9A\xBC\xDE\xF0", |     check<uint64_t>("\xCF\x12\x34\x56\x78\x9A\xBC\xDE\xF0", | ||||||
|                     0x123456789ABCDEF0U); |                     0x123456789ABCDEF0U); | ||||||
| #else | #else | ||||||
|     check<uint32_t>("\xCF\x00\x00\x00\x00\x00\x00\x00\x00", 0U); |     checkNotSupported("\xCF\x00\x00\x00\x00\x00\x00\x00\x00"); | ||||||
|     check<uint32_t>("\xCF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0xFFFFFFFF); |     checkNotSupported("\xCF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"); | ||||||
|     check<uint32_t>("\xCF\x12\x34\x56\x78\x9A\xBC\xDE\xF0", 0x9ABCDEF0); |     checkNotSupported("\xCF\x12\x34\x56\x78\x9A\xBC\xDE\xF0"); | ||||||
| #endif | #endif | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -95,15 +103,15 @@ TEST_CASE("deserialize MsgPack value") { | |||||||
|  |  | ||||||
|   SECTION("int 64") { |   SECTION("int 64") { | ||||||
| #if ARDUINOJSON_USE_LONG_LONG | #if ARDUINOJSON_USE_LONG_LONG | ||||||
|     check<uint64_t>("\xD3\x00\x00\x00\x00\x00\x00\x00\x00", 0U); |     check<int64_t>("\xD3\x00\x00\x00\x00\x00\x00\x00\x00", int64_t(0U)); | ||||||
|     check<uint64_t>("\xD3\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", |     check<int64_t>("\xD3\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", | ||||||
|                     0xFFFFFFFFFFFFFFFFU); |                    int64_t(0xFFFFFFFFFFFFFFFFU)); | ||||||
|     check<uint64_t>("\xD3\x12\x34\x56\x78\x9A\xBC\xDE\xF0", |     check<int64_t>("\xD3\x12\x34\x56\x78\x9A\xBC\xDE\xF0", | ||||||
|                     0x123456789ABCDEF0U); |                    int64_t(0x123456789ABCDEF0)); | ||||||
| #else | #else | ||||||
|     check<uint32_t>("\xD3\x00\x00\x00\x00\x00\x00\x00\x00", 0U); |     checkNotSupported("\xD3\x00\x00\x00\x00\x00\x00\x00\x00"); | ||||||
|     check<uint32_t>("\xD3\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0xFFFFFFFF); |     checkNotSupported("\xD3\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"); | ||||||
|     check<uint32_t>("\xD3\x12\x34\x56\x78\x9A\xBC\xDE\xF0", 0x9ABCDEF0); |     checkNotSupported("\xD3\x12\x34\x56\x78\x9A\xBC\xDE\xF0"); | ||||||
| #endif | #endif | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -60,9 +60,11 @@ TEST_CASE("deserializeMsgPack() returns IncompleteInput") { | |||||||
|     checkAllSizes("\xCE\x00\x00\x00\x01", 5); |     checkAllSizes("\xCE\x00\x00\x00\x01", 5); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | #if ARDUINOJSON_USE_LONG_LONG | ||||||
|   SECTION("uint 64") { |   SECTION("uint 64") { | ||||||
|     checkAllSizes("\xCF\x00\x00\x00\x00\x00\x00\x00\x00", 9); |     checkAllSizes("\xCF\x00\x00\x00\x00\x00\x00\x00\x00", 9); | ||||||
|   } |   } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|   SECTION("int 8") { |   SECTION("int 8") { | ||||||
|     checkAllSizes("\xD0\x01", 2); |     checkAllSizes("\xD0\x01", 2); | ||||||
| @@ -76,9 +78,11 @@ TEST_CASE("deserializeMsgPack() returns IncompleteInput") { | |||||||
|     checkAllSizes("\xD2\x00\x00\x00\x01", 5); |     checkAllSizes("\xD2\x00\x00\x00\x01", 5); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | #if ARDUINOJSON_USE_LONG_LONG | ||||||
|   SECTION("int 64") { |   SECTION("int 64") { | ||||||
|     checkAllSizes("\xD3\x00\x00\x00\x00\x00\x00\x00\x00", 9); |     checkAllSizes("\xD3\x00\x00\x00\x00\x00\x00\x00\x00", 9); | ||||||
|   } |   } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|   SECTION("float 32") { |   SECTION("float 32") { | ||||||
|     checkAllSizes("\xCA\x40\x48\xF5\xC3", 5); |     checkAllSizes("\xCA\x40\x48\xF5\xC3", 5); | ||||||
|   | |||||||
| @@ -3,10 +3,9 @@ | |||||||
| # MIT License | # MIT License | ||||||
|  |  | ||||||
| add_executable(NumbersTests  | add_executable(NumbersTests  | ||||||
| 	isFloat.cpp |  | ||||||
| 	isInteger.cpp |  | ||||||
| 	parseFloat.cpp | 	parseFloat.cpp | ||||||
| 	parseInteger.cpp | 	parseInteger.cpp | ||||||
|  | 	parseNumber.cpp | ||||||
| ) | ) | ||||||
|  |  | ||||||
| target_link_libraries(NumbersTests catch) | target_link_libraries(NumbersTests catch) | ||||||
|   | |||||||
| @@ -1,80 +0,0 @@ | |||||||
| // ArduinoJson - arduinojson.org |  | ||||||
| // Copyright Benoit Blanchon 2014-2019 |  | ||||||
| // MIT License |  | ||||||
|  |  | ||||||
| #include <ArduinoJson/Numbers/isFloat.hpp> |  | ||||||
| #include <catch.hpp> |  | ||||||
|  |  | ||||||
| using namespace ARDUINOJSON_NAMESPACE; |  | ||||||
|  |  | ||||||
| TEST_CASE("isFloat()") { |  | ||||||
|   SECTION("Input is NULL") { |  | ||||||
|     REQUIRE(isFloat(NULL) == false); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Empty string") { |  | ||||||
|     REQUIRE(isFloat("") == false); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("NoExponent") { |  | ||||||
|     REQUIRE(isFloat("3.14") == true); |  | ||||||
|     REQUIRE(isFloat("-3.14") == true); |  | ||||||
|     REQUIRE(isFloat("+3.14") == true); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("IntegralPartMissing") { |  | ||||||
|     REQUIRE(isFloat(".14") == true); |  | ||||||
|     REQUIRE(isFloat("-.14") == true); |  | ||||||
|     REQUIRE(isFloat("+.14") == true); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("FractionalPartMissing") { |  | ||||||
|     REQUIRE(isFloat("3.") == true); |  | ||||||
|     REQUIRE(isFloat("-3.e14") == true); |  | ||||||
|     REQUIRE(isFloat("+3.e-14") == true); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("NoDot") { |  | ||||||
|     REQUIRE(isFloat("3e14") == true); |  | ||||||
|     REQUIRE(isFloat("3e-14") == true); |  | ||||||
|     REQUIRE(isFloat("3e+14") == true); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Integer") { |  | ||||||
|     REQUIRE(isFloat("14") == true); |  | ||||||
|     REQUIRE(isFloat("-14") == true); |  | ||||||
|     REQUIRE(isFloat("+14") == true); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("ExponentMissing") { |  | ||||||
|     REQUIRE(isFloat("3.14e") == false); |  | ||||||
|     REQUIRE(isFloat("3.14e-") == false); |  | ||||||
|     REQUIRE(isFloat("3.14e+") == false); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("JustASign") { |  | ||||||
|     REQUIRE(isFloat("-") == false); |  | ||||||
|     REQUIRE(isFloat("+") == false); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Empty") { |  | ||||||
|     REQUIRE(isFloat("") == false); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("NaN") { |  | ||||||
|     REQUIRE(isFloat("NaN") == true); |  | ||||||
|     REQUIRE(isFloat("n") == false); |  | ||||||
|     REQUIRE(isFloat("N") == false); |  | ||||||
|     REQUIRE(isFloat("nan") == false); |  | ||||||
|     REQUIRE(isFloat("-NaN") == false); |  | ||||||
|     REQUIRE(isFloat("+NaN") == false); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Infinity") { |  | ||||||
|     REQUIRE(isFloat("Infinity") == true); |  | ||||||
|     REQUIRE(isFloat("+Infinity") == true); |  | ||||||
|     REQUIRE(isFloat("-Infinity") == true); |  | ||||||
|     REQUIRE(isFloat("infinity") == false); |  | ||||||
|     REQUIRE(isFloat("Inf") == false); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1,40 +0,0 @@ | |||||||
| // ArduinoJson - arduinojson.org |  | ||||||
| // Copyright Benoit Blanchon 2014-2019 |  | ||||||
| // MIT License |  | ||||||
|  |  | ||||||
| #include <ArduinoJson/Numbers/isInteger.hpp> |  | ||||||
| #include <catch.hpp> |  | ||||||
|  |  | ||||||
| using namespace ARDUINOJSON_NAMESPACE; |  | ||||||
|  |  | ||||||
| TEST_CASE("isInteger()") { |  | ||||||
|   SECTION("Null") { |  | ||||||
|     REQUIRE(isInteger(NULL) == false); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Empty string") { |  | ||||||
|     REQUIRE(isInteger("") == false); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("FloatNotInteger") { |  | ||||||
|     REQUIRE(isInteger("3.14") == false); |  | ||||||
|     REQUIRE(isInteger("-3.14") == false); |  | ||||||
|     REQUIRE(isInteger("+3.14") == false); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Spaces") { |  | ||||||
|     REQUIRE(isInteger("42 ") == false); |  | ||||||
|     REQUIRE(isInteger(" 42") == false); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Valid") { |  | ||||||
|     REQUIRE(isInteger("42") == true); |  | ||||||
|     REQUIRE(isInteger("-42") == true); |  | ||||||
|     REQUIRE(isInteger("+42") == true); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("ExtraSign") { |  | ||||||
|     REQUIRE(isInteger("--42") == false); |  | ||||||
|     REQUIRE(isInteger("++42") == false); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -2,6 +2,8 @@ | |||||||
| // Copyright Benoit Blanchon 2014-2019 | // Copyright Benoit Blanchon 2014-2019 | ||||||
| // MIT License | // MIT License | ||||||
|  |  | ||||||
|  | #define ARDUINOJSON_USE_DOUBLE 0 | ||||||
|  |  | ||||||
| #include <ArduinoJson/Numbers/parseFloat.hpp> | #include <ArduinoJson/Numbers/parseFloat.hpp> | ||||||
| #include <catch.hpp> | #include <catch.hpp> | ||||||
|  |  | ||||||
| @@ -33,10 +35,6 @@ void checkInf(const char* input, bool negative) { | |||||||
| } | } | ||||||
|  |  | ||||||
| TEST_CASE("parseFloat<float>()") { | TEST_CASE("parseFloat<float>()") { | ||||||
|   SECTION("Null") { |  | ||||||
|     check<float>(NULL, 0); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Float_Short_NoExponent") { |   SECTION("Float_Short_NoExponent") { | ||||||
|     check<float>("3.14", 3.14f); |     check<float>("3.14", 3.14f); | ||||||
|     check<float>("-3.14", -3.14f); |     check<float>("-3.14", -3.14f); | ||||||
| @@ -97,19 +95,13 @@ TEST_CASE("parseFloat<float>()") { | |||||||
|     checkInf<float>("inf", false); |     checkInf<float>("inf", false); | ||||||
|     checkInf<float>("+inf", false); |     checkInf<float>("+inf", false); | ||||||
|     checkInf<float>("-inf", true); |     checkInf<float>("-inf", true); | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Boolean") { |     checkInf<float>("1e300", false); | ||||||
|     check<float>("false", 0.0f); |     checkInf<float>("-1e300", true); | ||||||
|     check<float>("true", 1.0f); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| TEST_CASE("parseFloat<double>()") { | TEST_CASE("parseFloat<double>()") { | ||||||
|   SECTION("Null") { |  | ||||||
|     check<double>(NULL, 0); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Short_NoExponent") { |   SECTION("Short_NoExponent") { | ||||||
|     check<double>("3.14", 3.14); |     check<double>("3.14", 3.14); | ||||||
|     check<double>("-3.14", -3.14); |     check<double>("-3.14", -3.14); | ||||||
| @@ -169,9 +161,4 @@ TEST_CASE("parseFloat<double>()") { | |||||||
|     checkNaN<double>("NaN"); |     checkNaN<double>("NaN"); | ||||||
|     checkNaN<double>("nan"); |     checkNaN<double>("nan"); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("Boolean") { |  | ||||||
|     check<double>("false", 0.0); |  | ||||||
|     check<double>("true", 1.0); |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -21,11 +21,8 @@ TEST_CASE("parseInteger<int8_t>()") { | |||||||
|   check<int8_t>("+127", 127); |   check<int8_t>("+127", 127); | ||||||
|   check<int8_t>("3.14", 3); |   check<int8_t>("3.14", 3); | ||||||
|   check<int8_t>("x42", 0); |   check<int8_t>("x42", 0); | ||||||
|   check<int8_t>("128", -128); |   check<int8_t>("128", 0);   // overflow | ||||||
|   check<int8_t>("-129", 127); |   check<int8_t>("-129", 0);  // overflow | ||||||
|   check<int8_t>(NULL, 0); |  | ||||||
|   check<int8_t>("true", 1); |  | ||||||
|   check<int8_t>("false", 0); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| TEST_CASE("parseInteger<int16_t>()") { | TEST_CASE("parseInteger<int16_t>()") { | ||||||
| @@ -34,11 +31,8 @@ TEST_CASE("parseInteger<int16_t>()") { | |||||||
|   check<int16_t>("+32767", 32767); |   check<int16_t>("+32767", 32767); | ||||||
|   check<int16_t>("3.14", 3); |   check<int16_t>("3.14", 3); | ||||||
|   check<int16_t>("x42", 0); |   check<int16_t>("x42", 0); | ||||||
|   check<int16_t>("-32769", 32767); |   check<int16_t>("-32769", 0);  // overflow | ||||||
|   check<int16_t>("32768", -32768); |   check<int16_t>("32768", 0);   // overflow | ||||||
|   check<int16_t>(NULL, 0); |  | ||||||
|   check<int16_t>("true", 1); |  | ||||||
|   check<int16_t>("false", 0); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| TEST_CASE("parseInteger<int32_t>()") { | TEST_CASE("parseInteger<int32_t>()") { | ||||||
| @@ -47,10 +41,8 @@ TEST_CASE("parseInteger<int32_t>()") { | |||||||
|   check<int32_t>("+2147483647", 2147483647); |   check<int32_t>("+2147483647", 2147483647); | ||||||
|   check<int32_t>("3.14", 3); |   check<int32_t>("3.14", 3); | ||||||
|   check<int32_t>("x42", 0); |   check<int32_t>("x42", 0); | ||||||
|   check<int32_t>("-2147483649", 2147483647); |   check<int32_t>("-2147483649", 0);  // overflow | ||||||
|   check<int32_t>("2147483648", (-2147483647 - 1)); |   check<int32_t>("2147483648", 0);   // overflow | ||||||
|   check<int32_t>("true", 1); |  | ||||||
|   check<int32_t>("false", 0); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| TEST_CASE("parseInteger<uint8_t>()") { | TEST_CASE("parseInteger<uint8_t>()") { | ||||||
| @@ -59,10 +51,8 @@ TEST_CASE("parseInteger<uint8_t>()") { | |||||||
|   check<uint8_t>("+255", 255); |   check<uint8_t>("+255", 255); | ||||||
|   check<uint8_t>("3.14", 3); |   check<uint8_t>("3.14", 3); | ||||||
|   check<uint8_t>("x42", 0); |   check<uint8_t>("x42", 0); | ||||||
|   check<uint8_t>("-1", 255); |   check<uint8_t>("-1", 0); | ||||||
|   check<uint8_t>("256", 0); |   check<uint8_t>("256", 0); | ||||||
|   check<uint8_t>("true", 1); |  | ||||||
|   check<uint8_t>("false", 0); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| TEST_CASE("parseInteger<uint16_t>()") { | TEST_CASE("parseInteger<uint16_t>()") { | ||||||
| @@ -72,8 +62,6 @@ TEST_CASE("parseInteger<uint16_t>()") { | |||||||
|   check<uint16_t>("3.14", 3); |   check<uint16_t>("3.14", 3); | ||||||
|   // check<uint16_t>(" 42", 0); |   // check<uint16_t>(" 42", 0); | ||||||
|   check<uint16_t>("x42", 0); |   check<uint16_t>("x42", 0); | ||||||
|   check<uint16_t>("-1", 65535); |   check<uint16_t>("-1", 0); | ||||||
|   check<uint16_t>("65536", 0); |   check<uint16_t>("65536", 0); | ||||||
|   check<uint16_t>("true", 1); |  | ||||||
|   check<uint16_t>("false", 0); |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								test/Numbers/parseNumber.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								test/Numbers/parseNumber.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2019 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #include <ArduinoJson/Numbers/parseNumber.hpp> | ||||||
|  | #include <catch.hpp> | ||||||
|  |  | ||||||
|  | using namespace ARDUINOJSON_NAMESPACE; | ||||||
|  |  | ||||||
|  | TEST_CASE("Test uint32_t overflow") { | ||||||
|  |   ParsedNumber<float, uint32_t> first = | ||||||
|  |       parseNumber<float, uint32_t>("4294967295"); | ||||||
|  |   ParsedNumber<float, uint32_t> second = | ||||||
|  |       parseNumber<float, uint32_t>("4294967296"); | ||||||
|  |  | ||||||
|  |   REQUIRE(first.type() == uint8_t(VALUE_IS_POSITIVE_INTEGER)); | ||||||
|  |   REQUIRE(second.type() == uint8_t(VALUE_IS_FLOAT)); | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user