diff --git a/CHANGELOG.md b/CHANGELOG.md index 9144c7a4..e2009f11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ ArduinoJson: change log ======================= +HEAD +---- + +* Fixed an integer overflow in the JSON deserializer +* Added overflow handling in `JsonVariant::as()` and `JsonVariant::is()`. + - `as()` returns `0` if the integer `T` overflows + - `is()` returns `false` if the integer `T` overflows + v6.9.1 (2019-03-01) ------ diff --git a/fuzzing/json_seed_corpus/IntegerOverflow.json b/fuzzing/json_seed_corpus/IntegerOverflow.json new file mode 100644 index 00000000..3a33cb89 --- /dev/null +++ b/fuzzing/json_seed_corpus/IntegerOverflow.json @@ -0,0 +1 @@ +9720730739393920739 diff --git a/scripts/travis/fuzz.sh b/scripts/travis/fuzz.sh index 48e9728e..1d4e32d9 100755 --- a/scripts/travis/fuzz.sh +++ b/scripts/travis/fuzz.sh @@ -3,7 +3,7 @@ ROOT_DIR=$(dirname $0)/../../ INCLUDE_DIR=${ROOT_DIR}/src/ 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() { NAME="$1" diff --git a/src/ArduinoJson/Json/JsonDeserializer.hpp b/src/ArduinoJson/Json/JsonDeserializer.hpp index 415b807b..ba9ab03c 100644 --- a/src/ArduinoJson/Json/JsonDeserializer.hpp +++ b/src/ArduinoJson/Json/JsonDeserializer.hpp @@ -6,8 +6,7 @@ #include "../Deserialization/deserialize.hpp" #include "../Memory/MemoryPool.hpp" -#include "../Numbers/isFloat.hpp" -#include "../Numbers/isInteger.hpp" +#include "../Numbers/parseNumber.hpp" #include "../Polyfills/type_traits.hpp" #include "../Variant/VariantData.hpp" #include "EscapeSequence.hpp" @@ -251,14 +250,6 @@ class JsonDeserializer { } buffer[n] = 0; - if (isInteger(buffer)) { - result.setInteger(parseInteger(buffer)); - return DeserializationError::Ok; - } - if (isFloat(buffer)) { - result.setFloat(parseFloat(buffer)); - return DeserializationError::Ok; - } c = buffer[0]; if (c == 't') { // true result.setBoolean(true); @@ -275,6 +266,23 @@ class JsonDeserializer { return n == 4 ? DeserializationError::Ok : DeserializationError::IncompleteInput; } + + ParsedNumber num = parseNumber(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; } diff --git a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp index 9244ecb2..70c9e94f 100644 --- a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -79,8 +79,7 @@ class MsgPackDeserializer { #if ARDUINOJSON_USE_LONG_LONG return readInteger(variant); #else - readInteger(); - return readInteger(variant); + return DeserializationError::NotSupported; #endif case 0xd0: @@ -96,8 +95,7 @@ class MsgPackDeserializer { #if ARDUINOJSON_USE_LONG_LONG return readInteger(variant); #else - if (!skip(4)) return DeserializationError::IncompleteInput; - return readInteger(variant); + return DeserializationError::NotSupported; #endif case 0xca: diff --git a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp index 8c9f3b22..10edb107 100644 --- a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp @@ -24,7 +24,8 @@ class MsgPackSerializer { } template - typename enable_if::type visitFloat(T value64) { + typename enable_if::type visitFloat(T value64) + ARDUINOJSON_NO_SANITIZE("float-cast-overflow") { float value32 = float(value64); if (value32 == value64) { writeByte(0xCA); diff --git a/src/ArduinoJson/Numbers/FloatTraits.hpp b/src/ArduinoJson/Numbers/FloatTraits.hpp index 73c8bbd8..2e92fb7c 100644 --- a/src/ArduinoJson/Numbers/FloatTraits.hpp +++ b/src/ArduinoJson/Numbers/FloatTraits.hpp @@ -17,10 +17,10 @@ struct FloatTraits {}; template struct FloatTraits { - typedef int64_t mantissa_type; + typedef uint64_t mantissa_type; static const short mantissa_bits = 52; static const mantissa_type mantissa_max = - (static_cast(1) << mantissa_bits) - 1; + (mantissa_type(1) << mantissa_bits) - 1; typedef int16_t exponent_type; static const exponent_type exponent_max = 308; @@ -95,6 +95,14 @@ struct FloatTraits { 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 // we use this function to workaround platforms with single precision literals // (for example, when -fsingle-precision-constant is passed to GCC) @@ -105,10 +113,10 @@ struct FloatTraits { template struct FloatTraits { - typedef int32_t mantissa_type; + typedef uint32_t mantissa_type; static const short mantissa_bits = 23; static const mantissa_type mantissa_max = - (static_cast(1) << mantissa_bits) - 1; + (mantissa_type(1) << mantissa_bits) - 1; typedef int8_t exponent_type; static const exponent_type exponent_max = 38; @@ -156,5 +164,13 @@ struct FloatTraits { static T inf() { return forge(0x7f800000); } + + static T highest() { + return forge(0x7f7fffff); + } + + static T lowest() { + return forge(0xFf7fffff); + } }; } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Numbers/convertNumber.hpp b/src/ArduinoJson/Numbers/convertNumber.hpp new file mode 100644 index 00000000..4d92822d --- /dev/null +++ b/src/ArduinoJson/Numbers/convertNumber.hpp @@ -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 enable_if::value && sizeof(TOut) <= sizeof(TIn), + bool>::type +canStorePositiveInteger(TIn value) { + return value <= TIn(numeric_limits::highest()); +} + +template +typename enable_if::value && sizeof(TIn) < sizeof(TOut), + bool>::type +canStorePositiveInteger(TIn) { + return true; +} + +template +typename enable_if::value, bool>::type +canStorePositiveInteger(TIn) { + return true; +} + +template +typename enable_if::value, bool>::type +canStoreNegativeInteger(TIn) { + return true; +} + +template +typename enable_if::value && is_signed::value && + sizeof(TOut) <= sizeof(TIn), + bool>::type +canStoreNegativeInteger(TIn value) { + return value <= TIn(numeric_limits::highest()) + 1; +} + +template +typename enable_if::value && is_signed::value && + sizeof(TIn) < sizeof(TOut), + bool>::type +canStoreNegativeInteger(TIn) { + return true; +} + +template +typename enable_if::value && is_unsigned::value, + bool>::type +canStoreNegativeInteger(TIn) { + return false; +} + +template +TOut convertPositiveInteger(TIn value) { + return canStorePositiveInteger(value) ? TOut(value) : 0; +} + +template +TOut convertNegativeInteger(TIn value) { + return canStoreNegativeInteger(value) ? TOut(~value + 1) : 0; +} + +template +typename enable_if::value, TOut>::type convertFloat( + TIn value) { + return TOut(value); +} + +template +typename enable_if::value, TOut>::type convertFloat( + TIn value) { + return value >= numeric_limits::lowest() && + value <= numeric_limits::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 diff --git a/src/ArduinoJson/Numbers/isFloat.hpp b/src/ArduinoJson/Numbers/isFloat.hpp deleted file mode 100644 index efaa55bc..00000000 --- a/src/ArduinoJson/Numbers/isFloat.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2019 -// MIT License - -#pragma once - -#include // 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 diff --git a/src/ArduinoJson/Numbers/isInteger.hpp b/src/ArduinoJson/Numbers/isInteger.hpp deleted file mode 100644 index 620f381b..00000000 --- a/src/ArduinoJson/Numbers/isInteger.hpp +++ /dev/null @@ -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 diff --git a/src/ArduinoJson/Numbers/parseFloat.hpp b/src/ArduinoJson/Numbers/parseFloat.hpp index 3f30885f..ea89c7ee 100644 --- a/src/ArduinoJson/Numbers/parseFloat.hpp +++ b/src/ArduinoJson/Numbers/parseFloat.hpp @@ -4,85 +4,15 @@ #pragma once -#include "../Numbers/FloatTraits.hpp" -#include "../Polyfills/ctype.hpp" -#include "../Polyfills/math.hpp" +#include "convertNumber.hpp" +#include "parseNumber.hpp" namespace ARDUINOJSON_NAMESPACE { template inline T parseFloat(const char* s) { - typedef FloatTraits traits; - typedef typename traits::mantissa_type mantissa_t; - typedef typename traits::exponent_type exponent_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(mantissa), exponent); - - return negative_result ? -result : result; + // try to reuse the same parameters as JsonDeserializer + typedef typename choose_largest::type TFloat; + return parseNumber(s).template as(); } } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Numbers/parseInteger.hpp b/src/ArduinoJson/Numbers/parseInteger.hpp index 9bc40910..d3749a5c 100644 --- a/src/ArduinoJson/Numbers/parseInteger.hpp +++ b/src/ArduinoJson/Numbers/parseInteger.hpp @@ -4,34 +4,16 @@ #pragma once -#include "../Configuration.hpp" -#include "../Polyfills/ctype.hpp" +#include "../Polyfills/type_traits.hpp" +#include "convertNumber.hpp" +#include "parseNumber.hpp" namespace ARDUINOJSON_NAMESPACE { template T parseInteger(const char *s) { - if (!s) return 0; // NULL - - if (*s == 't') return 1; // "true" - - 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; + // try to reuse the same parameters as JsonDeserializer + typedef typename choose_largest::type>::type + TUInt; + return parseNumber(s).template as(); } } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Numbers/parseNumber.hpp b/src/ArduinoJson/Numbers/parseNumber.hpp new file mode 100644 index 00000000..35fb3468 --- /dev/null +++ b/src/ArduinoJson/Numbers/parseNumber.hpp @@ -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 +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 + T as() const { + switch (_type) { + case VALUE_IS_NEGATIVE_INTEGER: + return convertNegativeInteger(uintValue); + case VALUE_IS_POSITIVE_INTEGER: + return convertPositiveInteger(uintValue); + case VALUE_IS_FLOAT: + return convertFloat(floatValue); + default: + return 0; + } + } + + uint8_t type() const { + return _type; + } + + TUInt uintValue; + TFloat floatValue; + uint8_t _type; +}; + +template +struct choose_largest : conditional<(sizeof(A) > sizeof(B)), A, B> {}; + +template +inline ParsedNumber parseNumber(const char *s) { + typedef FloatTraits traits; + typedef typename choose_largest::type + mantissa_t; + typedef typename traits::exponent_type exponent_t; + typedef ParsedNumber 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(mantissa), exponent); + + return is_negative ? -result : result; +} +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Polyfills/attributes.hpp b/src/ArduinoJson/Polyfills/attributes.hpp index f0c45be7..e5beb227 100644 --- a/src/ArduinoJson/Polyfills/attributes.hpp +++ b/src/ArduinoJson/Polyfills/attributes.hpp @@ -33,3 +33,13 @@ #else #define NOEXCEPT throw() #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 diff --git a/src/ArduinoJson/Polyfills/limits.hpp b/src/ArduinoJson/Polyfills/limits.hpp new file mode 100644 index 00000000..0a7b9347 --- /dev/null +++ b/src/ArduinoJson/Polyfills/limits.hpp @@ -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 +struct numeric_limits; + +template +struct numeric_limits::value>::type> { + static T lowest() { + return 0; + } + static T highest() { + return T(-1); + } +}; + +template +struct numeric_limits< + T, typename enable_if::value && is_signed::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 diff --git a/src/ArduinoJson/Polyfills/type_traits.hpp b/src/ArduinoJson/Polyfills/type_traits.hpp index b4e1a3f4..5ccc7240 100644 --- a/src/ArduinoJson/Polyfills/type_traits.hpp +++ b/src/ArduinoJson/Polyfills/type_traits.hpp @@ -15,5 +15,6 @@ #include "type_traits/is_same.hpp" #include "type_traits/is_signed.hpp" #include "type_traits/is_unsigned.hpp" +#include "type_traits/make_unsigned.hpp" #include "type_traits/remove_const.hpp" #include "type_traits/remove_reference.hpp" diff --git a/src/ArduinoJson/Polyfills/type_traits/make_unsigned.hpp b/src/ArduinoJson/Polyfills/type_traits/make_unsigned.hpp new file mode 100644 index 00000000..b7fcf2f2 --- /dev/null +++ b/src/ArduinoJson/Polyfills/type_traits/make_unsigned.hpp @@ -0,0 +1,49 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#include "type_identity.hpp" +namespace ARDUINOJSON_NAMESPACE { + +template +struct make_unsigned; + +template <> +struct make_unsigned : type_identity {}; + +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; + +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; + +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; + +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; + +#if ARDUINOJSON_HAS_LONG_LONG +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; +#endif + +#if ARDUINOJSON_HAS_INT64 +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; +#endif +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Polyfills/type_traits/type_identity.hpp b/src/ArduinoJson/Polyfills/type_traits/type_identity.hpp new file mode 100644 index 00000000..c493ed7c --- /dev/null +++ b/src/ArduinoJson/Polyfills/type_traits/type_identity.hpp @@ -0,0 +1,15 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#include "integral_constant.hpp" + +namespace ARDUINOJSON_NAMESPACE { + +template +struct type_identity { + typedef T type; +}; +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Variant/VariantData.hpp b/src/ArduinoJson/Variant/VariantData.hpp index 6e3799aa..e471c8a5 100644 --- a/src/ArduinoJson/Variant/VariantData.hpp +++ b/src/ArduinoJson/Variant/VariantData.hpp @@ -5,6 +5,7 @@ #pragma once #include "../Misc/SerializedValue.hpp" +#include "../Numbers/convertNumber.hpp" #include "../Polyfills/gsl/not_null.hpp" #include "VariantContent.hpp" @@ -63,9 +64,7 @@ class VariantData { const char *asString() const; - bool asBoolean() const { - return asIntegral() != 0; - } + bool asBoolean() const; CollectionData *asArray() { return isArray() ? &_content.asCollection : 0; @@ -147,9 +146,18 @@ class VariantData { return (_flags & COLLECTION_MASK) != 0; } + template bool isInteger() const { - return type() == VALUE_IS_POSITIVE_INTEGER || - type() == VALUE_IS_NEGATIVE_INTEGER; + switch (type()) { + case VALUE_IS_POSITIVE_INTEGER: + return canStorePositiveInteger(_content.asInteger); + + case VALUE_IS_NEGATIVE_INTEGER: + return canStoreNegativeInteger(_content.asInteger); + + default: + return false; + } } bool isFloat() const { @@ -225,14 +233,22 @@ class VariantData { template void setSignedInteger(T value) { if (value >= 0) { - setType(VALUE_IS_POSITIVE_INTEGER); - _content.asInteger = static_cast(value); + setPositiveInteger(static_cast(value)); } else { - setType(VALUE_IS_NEGATIVE_INTEGER); - _content.asInteger = ~static_cast(value) + 1; + setNegativeInteger(~static_cast(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) { if (value) { setType(VALUE_IS_LINKED_STRING); diff --git a/src/ArduinoJson/Variant/VariantFunctions.hpp b/src/ArduinoJson/Variant/VariantFunctions.hpp index 62de5ba6..7fd9bd6e 100644 --- a/src/ArduinoJson/Variant/VariantFunctions.hpp +++ b/src/ArduinoJson/Variant/VariantFunctions.hpp @@ -52,8 +52,9 @@ inline bool variantIsBoolean(const VariantData *var) { return var && var->isBoolean(); } +template inline bool variantIsInteger(const VariantData *var) { - return var && var->isInteger(); + return var && var->isInteger(); } inline bool variantIsFloat(const VariantData *var) { diff --git a/src/ArduinoJson/Variant/VariantImpl.hpp b/src/ArduinoJson/Variant/VariantImpl.hpp index 6dfddd7f..a925b7bb 100644 --- a/src/ArduinoJson/Variant/VariantImpl.hpp +++ b/src/ArduinoJson/Variant/VariantImpl.hpp @@ -5,6 +5,7 @@ #pragma once #include "../Configuration.hpp" +#include "../Numbers/convertNumber.hpp" #include "../Numbers/parseFloat.hpp" #include "../Numbers/parseInteger.hpp" #include "VariantRef.hpp" @@ -18,19 +19,35 @@ inline T VariantData::asIntegral() const { switch (type()) { case VALUE_IS_POSITIVE_INTEGER: case VALUE_IS_BOOLEAN: - return T(_content.asInteger); + return convertPositiveInteger(_content.asInteger); case VALUE_IS_NEGATIVE_INTEGER: - return T(~_content.asInteger + 1); + return convertNegativeInteger(_content.asInteger); case VALUE_IS_LINKED_STRING: case VALUE_IS_OWNED_STRING: return parseInteger(_content.asString); case VALUE_IS_FLOAT: - return T(_content.asFloat); + return convertFloat(_content.asFloat); default: 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 template inline T VariantData::asFloat() const { diff --git a/src/ArduinoJson/Variant/VariantRef.hpp b/src/ArduinoJson/Variant/VariantRef.hpp index 9a655263..5dd29315 100644 --- a/src/ArduinoJson/Variant/VariantRef.hpp +++ b/src/ArduinoJson/Variant/VariantRef.hpp @@ -9,8 +9,6 @@ #include "../Memory/MemoryPool.hpp" #include "../Misc/Visitable.hpp" -#include "../Numbers/parseFloat.hpp" -#include "../Numbers/parseInteger.hpp" #include "../Operators/VariantOperators.hpp" #include "../Polyfills/type_traits.hpp" #include "VariantAs.hpp" @@ -45,7 +43,7 @@ class VariantRefBase { template FORCE_INLINE typename enable_if::value, bool>::type is() const { - return variantIsInteger(_data); + return variantIsInteger(_data); } // // bool is() const; diff --git a/test/JsonArray/subscript.cpp b/test/JsonArray/subscript.cpp index c9d2d381..92051cf1 100644 --- a/test/JsonArray/subscript.cpp +++ b/test/JsonArray/subscript.cpp @@ -22,7 +22,8 @@ TEST_CASE("JsonArray::operator[]") { SECTION("long long") { array[0] = 9223372036854775807; REQUIRE(9223372036854775807 == array[0].as()); - REQUIRE(true == array[0].is()); + REQUIRE(true == array[0].is()); + REQUIRE(false == array[0].is()); REQUIRE(false == array[0].is()); } #endif diff --git a/test/JsonDeserializer/CMakeLists.txt b/test/JsonDeserializer/CMakeLists.txt index 43da0adc..70e71db0 100644 --- a/test/JsonDeserializer/CMakeLists.txt +++ b/test/JsonDeserializer/CMakeLists.txt @@ -3,17 +3,18 @@ # MIT License add_executable(JsonDeserializerTests + array.cpp + array_static.cpp DeserializationError.cpp - deserializeJsonArray.cpp - deserializeJsonArrayStatic.cpp - deserializeJsonObject.cpp - deserializeJsonObjectStatic.cpp - deserializeJsonValue.cpp - deserializeJsonString.cpp - input_types.cpp incomplete_input.cpp + input_types.cpp + number.cpp invalid_input.cpp + misc.cpp nestingLimit.cpp + object.cpp + object_static.cpp + string.cpp ) target_link_libraries(JsonDeserializerTests catch) diff --git a/test/JsonDeserializer/deserializeJsonArray.cpp b/test/JsonDeserializer/array.cpp similarity index 100% rename from test/JsonDeserializer/deserializeJsonArray.cpp rename to test/JsonDeserializer/array.cpp diff --git a/test/JsonDeserializer/deserializeJsonArrayStatic.cpp b/test/JsonDeserializer/array_static.cpp similarity index 100% rename from test/JsonDeserializer/deserializeJsonArrayStatic.cpp rename to test/JsonDeserializer/array_static.cpp diff --git a/test/JsonDeserializer/deserializeJsonValue.cpp b/test/JsonDeserializer/misc.cpp similarity index 67% rename from test/JsonDeserializer/deserializeJsonValue.cpp rename to test/JsonDeserializer/misc.cpp index 25126638..d8c6e010 100644 --- a/test/JsonDeserializer/deserializeJsonValue.cpp +++ b/test/JsonDeserializer/misc.cpp @@ -7,11 +7,6 @@ using namespace Catch::Matchers; -namespace my { -using ARDUINOJSON_NAMESPACE::isinf; -using ARDUINOJSON_NAMESPACE::isnan; -} // namespace my - TEST_CASE("deserializeJson(DynamicJsonDocument&)") { 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() == true); - REQUIRE(doc.as() == 0); - REQUIRE(doc.as() == "0"); // issue #808 - } - - SECTION("Negative") { - DeserializationError err = deserializeJson(doc, "-42"); - - REQUIRE(err == DeserializationError::Ok); - REQUIRE(doc.is()); - REQUIRE_FALSE(doc.is()); - REQUIRE(doc.as() == -42); - } - } - - SECTION("Floats") { - SECTION("Double") { - DeserializationError err = deserializeJson(doc, "-1.23e+4"); - - REQUIRE(err == DeserializationError::Ok); - REQUIRE_FALSE(doc.is()); - REQUIRE(doc.is()); - REQUIRE(doc.as() == Approx(-1.23e+4)); - } - - SECTION("NaN") { - DeserializationError err = deserializeJson(doc, "NaN"); - REQUIRE(err == DeserializationError::Ok); - REQUIRE(doc.is() == true); - REQUIRE(my::isnan(doc.as())); - } - - SECTION("Infinity") { - DeserializationError err = deserializeJson(doc, "Infinity"); - REQUIRE(err == DeserializationError::Ok); - REQUIRE(doc.is() == true); - REQUIRE(my::isinf(doc.as())); - } - - SECTION("+Infinity") { - DeserializationError err = deserializeJson(doc, "+Infinity"); - REQUIRE(err == DeserializationError::Ok); - REQUIRE(doc.is() == true); - REQUIRE(my::isinf(doc.as())); - } - - SECTION("-Infinity") { - DeserializationError err = deserializeJson(doc, "-Infinity"); - REQUIRE(err == DeserializationError::Ok); - REQUIRE(doc.is() == true); - REQUIRE(my::isinf(doc.as())); - } - } - SECTION("Booleans") { SECTION("True") { DeserializationError err = deserializeJson(doc, "true"); diff --git a/test/JsonDeserializer/number.cpp b/test/JsonDeserializer/number.cpp new file mode 100644 index 00000000..1a33074d --- /dev/null +++ b/test/JsonDeserializer/number.cpp @@ -0,0 +1,131 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#define ARDUINOJSON_USE_LONG_LONG 0 + +#include +#include +#include + +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() == true); + REQUIRE(doc.as() == 0); + REQUIRE(doc.as() == "0"); // issue #808 + } + + SECTION("Negative") { + DeserializationError err = deserializeJson(doc, "-42"); + + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.is()); + REQUIRE_FALSE(doc.is()); + REQUIRE(doc.as() == -42); + } + +#if LONG_MAX == 2147483647 + SECTION("LONG_MAX") { + DeserializationError err = deserializeJson(doc, "2147483647"); + + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.is() == true); + REQUIRE(doc.as() == LONG_MAX); + } + + SECTION("LONG_MAX + 1") { + DeserializationError err = deserializeJson(doc, "2147483648"); + + CAPTURE(LONG_MIN); + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.is() == false); + REQUIRE(doc.is() == true); + } +#endif + +#if LONG_MIN == -2147483648 + SECTION("LONG_MIN") { + DeserializationError err = deserializeJson(doc, "-2147483648"); + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.is() == true); + REQUIRE(doc.as() == LONG_MIN); + } + + SECTION("LONG_MIN - 1") { + DeserializationError err = deserializeJson(doc, "-2147483649"); + + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.is() == false); + REQUIRE(doc.is() == true); + } +#endif + +#if ULONG_MAX == 4294967295 + SECTION("ULONG_MAX") { + DeserializationError err = deserializeJson(doc, "4294967295"); + + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.is() == true); + REQUIRE(doc.as() == ULONG_MAX); + REQUIRE(doc.is() == false); + } + + SECTION("ULONG_MAX + 1") { + DeserializationError err = deserializeJson(doc, "4294967296"); + + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.is() == false); + REQUIRE(doc.is() == true); + } +#endif + } + + SECTION("Floats") { + SECTION("Double") { + DeserializationError err = deserializeJson(doc, "-1.23e+4"); + + REQUIRE(err == DeserializationError::Ok); + REQUIRE_FALSE(doc.is()); + REQUIRE(doc.is()); + REQUIRE(doc.as() == Approx(-1.23e+4)); + } + + SECTION("NaN") { + DeserializationError err = deserializeJson(doc, "NaN"); + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.is() == true); + REQUIRE(my::isnan(doc.as())); + } + + SECTION("Infinity") { + DeserializationError err = deserializeJson(doc, "Infinity"); + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.is() == true); + REQUIRE(my::isinf(doc.as())); + } + + SECTION("+Infinity") { + DeserializationError err = deserializeJson(doc, "+Infinity"); + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.is() == true); + REQUIRE(my::isinf(doc.as())); + } + + SECTION("-Infinity") { + DeserializationError err = deserializeJson(doc, "-Infinity"); + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.is() == true); + REQUIRE(my::isinf(doc.as())); + } + } +} diff --git a/test/JsonDeserializer/deserializeJsonObject.cpp b/test/JsonDeserializer/object.cpp similarity index 100% rename from test/JsonDeserializer/deserializeJsonObject.cpp rename to test/JsonDeserializer/object.cpp diff --git a/test/JsonDeserializer/deserializeJsonObjectStatic.cpp b/test/JsonDeserializer/object_static.cpp similarity index 100% rename from test/JsonDeserializer/deserializeJsonObjectStatic.cpp rename to test/JsonDeserializer/object_static.cpp diff --git a/test/JsonDeserializer/deserializeJsonString.cpp b/test/JsonDeserializer/string.cpp similarity index 100% rename from test/JsonDeserializer/deserializeJsonString.cpp rename to test/JsonDeserializer/string.cpp diff --git a/test/JsonVariant/CMakeLists.txt b/test/JsonVariant/CMakeLists.txt index bee1b6ed..64a78f78 100644 --- a/test/JsonVariant/CMakeLists.txt +++ b/test/JsonVariant/CMakeLists.txt @@ -15,6 +15,7 @@ add_executable(JsonVariantTests misc.cpp nesting.cpp or.cpp + overflow.cpp remove.cpp set.cpp subscript.cpp diff --git a/test/JsonVariant/as.cpp b/test/JsonVariant/as.cpp index eeb9c514..21d2c987 100644 --- a/test/JsonVariant/as.cpp +++ b/test/JsonVariant/as.cpp @@ -6,6 +6,10 @@ #include #include +namespace my { +using ARDUINOJSON_NAMESPACE::isinf; +} // namespace my + static const char* null = 0; TEST_CASE("JsonVariant::as()") { @@ -94,7 +98,6 @@ TEST_CASE("JsonVariant::as()") { SECTION("set(\"42\")") { variant.set("42"); - REQUIRE(variant.as()); REQUIRE(variant.as() == 42L); } @@ -111,7 +114,6 @@ TEST_CASE("JsonVariant::as()") { SECTION("set(std::string(\"4.2\"))") { variant.set(std::string("4.2")); - REQUIRE(variant.as() == true); REQUIRE(variant.as() == 4L); REQUIRE(variant.as() == 4.2); REQUIRE(variant.as() == std::string("4.2")); @@ -121,8 +123,31 @@ TEST_CASE("JsonVariant::as()") { SECTION("set(\"true\")") { variant.set("true"); - REQUIRE(variant.as()); - REQUIRE(variant.as() == 1L); + REQUIRE(variant.as() == true); + REQUIRE(variant.as() == 0); + } + + SECTION("set(-1e300)") { + variant.set(-1e300); + + REQUIRE(variant.as() == -1e300); + REQUIRE(variant.as() < 0); + REQUIRE(my::isinf(variant.as())); + } + + SECTION("set(1e300)") { + variant.set(1e300); + + REQUIRE(variant.as() == 1e300); + REQUIRE(variant.as() > 0); + REQUIRE(my::isinf(variant.as())); + } + + SECTION("set(1e300)") { + variant.set(1e-300); + + REQUIRE(variant.as() == 1e-300); + REQUIRE(variant.as() == 0); } SECTION("to()") { diff --git a/test/JsonVariant/overflow.cpp b/test/JsonVariant/overflow.cpp new file mode 100644 index 00000000..8c356c9c --- /dev/null +++ b/test/JsonVariant/overflow.cpp @@ -0,0 +1,72 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#include +#include + +template +void shouldBeOk(TIn value) { + StaticJsonDocument<1> doc; + JsonVariant var = doc.to(); + var.set(value); + REQUIRE(var.as() == TOut(value)); +} + +template +void shouldOverflow(TIn value) { + StaticJsonDocument<1> doc; + JsonVariant var = doc.to(); + var.set(value); + REQUIRE(var.as() == 0); + REQUIRE(var.is() == false); +} + +TEST_CASE("Handle integer overflow in stored integer") { + SECTION("int8_t") { + // ok + shouldBeOk(-128); + shouldBeOk(42.0); + shouldBeOk(127); + + // too low + shouldOverflow(-128.1); + shouldOverflow(-129); + + // too high + shouldOverflow(128); + shouldOverflow(127.1); + } + + SECTION("int16_t") { + // ok + shouldBeOk(-32768); + shouldBeOk(-32767.9); + shouldBeOk(32766.9); + shouldBeOk(32767); + + // too low + shouldOverflow(-32768.1); + shouldOverflow(-32769); + + // too high + shouldOverflow(32767.1); + shouldOverflow(32768); + } + + SECTION("uint8_t") { + // ok + shouldBeOk(1); + shouldBeOk(42.0); + shouldBeOk(255); + + // too low + shouldOverflow(-1); + shouldOverflow(-0.1); + + // to high + shouldOverflow(255.1); + shouldOverflow(256); + shouldOverflow(257); + } +} diff --git a/test/MsgPackDeserializer/deserializeVariant.cpp b/test/MsgPackDeserializer/deserializeVariant.cpp index 8bcdc99a..1d8b6f08 100644 --- a/test/MsgPackDeserializer/deserializeVariant.cpp +++ b/test/MsgPackDeserializer/deserializeVariant.cpp @@ -16,6 +16,14 @@ static void check(const char* input, U expected) { REQUIRE(doc.as() == 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) { DynamicJsonDocument doc(4096); @@ -70,9 +78,9 @@ TEST_CASE("deserialize MsgPack value") { check("\xCF\x12\x34\x56\x78\x9A\xBC\xDE\xF0", 0x123456789ABCDEF0U); #else - check("\xCF\x00\x00\x00\x00\x00\x00\x00\x00", 0U); - check("\xCF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0xFFFFFFFF); - check("\xCF\x12\x34\x56\x78\x9A\xBC\xDE\xF0", 0x9ABCDEF0); + checkNotSupported("\xCF\x00\x00\x00\x00\x00\x00\x00\x00"); + checkNotSupported("\xCF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"); + checkNotSupported("\xCF\x12\x34\x56\x78\x9A\xBC\xDE\xF0"); #endif } @@ -95,15 +103,15 @@ TEST_CASE("deserialize MsgPack value") { SECTION("int 64") { #if ARDUINOJSON_USE_LONG_LONG - check("\xD3\x00\x00\x00\x00\x00\x00\x00\x00", 0U); - check("\xD3\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", - 0xFFFFFFFFFFFFFFFFU); - check("\xD3\x12\x34\x56\x78\x9A\xBC\xDE\xF0", - 0x123456789ABCDEF0U); + check("\xD3\x00\x00\x00\x00\x00\x00\x00\x00", int64_t(0U)); + check("\xD3\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", + int64_t(0xFFFFFFFFFFFFFFFFU)); + check("\xD3\x12\x34\x56\x78\x9A\xBC\xDE\xF0", + int64_t(0x123456789ABCDEF0)); #else - check("\xD3\x00\x00\x00\x00\x00\x00\x00\x00", 0U); - check("\xD3\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0xFFFFFFFF); - check("\xD3\x12\x34\x56\x78\x9A\xBC\xDE\xF0", 0x9ABCDEF0); + checkNotSupported("\xD3\x00\x00\x00\x00\x00\x00\x00\x00"); + checkNotSupported("\xD3\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"); + checkNotSupported("\xD3\x12\x34\x56\x78\x9A\xBC\xDE\xF0"); #endif } diff --git a/test/MsgPackDeserializer/incompleteInput.cpp b/test/MsgPackDeserializer/incompleteInput.cpp index 9c47b64e..883f7d9a 100644 --- a/test/MsgPackDeserializer/incompleteInput.cpp +++ b/test/MsgPackDeserializer/incompleteInput.cpp @@ -60,9 +60,11 @@ TEST_CASE("deserializeMsgPack() returns IncompleteInput") { checkAllSizes("\xCE\x00\x00\x00\x01", 5); } +#if ARDUINOJSON_USE_LONG_LONG SECTION("uint 64") { checkAllSizes("\xCF\x00\x00\x00\x00\x00\x00\x00\x00", 9); } +#endif SECTION("int 8") { checkAllSizes("\xD0\x01", 2); @@ -76,9 +78,11 @@ TEST_CASE("deserializeMsgPack() returns IncompleteInput") { checkAllSizes("\xD2\x00\x00\x00\x01", 5); } +#if ARDUINOJSON_USE_LONG_LONG SECTION("int 64") { checkAllSizes("\xD3\x00\x00\x00\x00\x00\x00\x00\x00", 9); } +#endif SECTION("float 32") { checkAllSizes("\xCA\x40\x48\xF5\xC3", 5); diff --git a/test/Numbers/CMakeLists.txt b/test/Numbers/CMakeLists.txt index ef142ef7..4e39bd9b 100644 --- a/test/Numbers/CMakeLists.txt +++ b/test/Numbers/CMakeLists.txt @@ -3,10 +3,9 @@ # MIT License add_executable(NumbersTests - isFloat.cpp - isInteger.cpp parseFloat.cpp parseInteger.cpp + parseNumber.cpp ) target_link_libraries(NumbersTests catch) diff --git a/test/Numbers/isFloat.cpp b/test/Numbers/isFloat.cpp deleted file mode 100644 index 7ca87b53..00000000 --- a/test/Numbers/isFloat.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2019 -// MIT License - -#include -#include - -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); - } -} diff --git a/test/Numbers/isInteger.cpp b/test/Numbers/isInteger.cpp deleted file mode 100644 index 12aabe68..00000000 --- a/test/Numbers/isInteger.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2019 -// MIT License - -#include -#include - -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); - } -} diff --git a/test/Numbers/parseFloat.cpp b/test/Numbers/parseFloat.cpp index da94b5bd..4313c0d0 100644 --- a/test/Numbers/parseFloat.cpp +++ b/test/Numbers/parseFloat.cpp @@ -2,6 +2,8 @@ // Copyright Benoit Blanchon 2014-2019 // MIT License +#define ARDUINOJSON_USE_DOUBLE 0 + #include #include @@ -33,10 +35,6 @@ void checkInf(const char* input, bool negative) { } TEST_CASE("parseFloat()") { - SECTION("Null") { - check(NULL, 0); - } - SECTION("Float_Short_NoExponent") { check("3.14", 3.14f); check("-3.14", -3.14f); @@ -97,19 +95,13 @@ TEST_CASE("parseFloat()") { checkInf("inf", false); checkInf("+inf", false); checkInf("-inf", true); - } - SECTION("Boolean") { - check("false", 0.0f); - check("true", 1.0f); + checkInf("1e300", false); + checkInf("-1e300", true); } } TEST_CASE("parseFloat()") { - SECTION("Null") { - check(NULL, 0); - } - SECTION("Short_NoExponent") { check("3.14", 3.14); check("-3.14", -3.14); @@ -169,9 +161,4 @@ TEST_CASE("parseFloat()") { checkNaN("NaN"); checkNaN("nan"); } - - SECTION("Boolean") { - check("false", 0.0); - check("true", 1.0); - } } diff --git a/test/Numbers/parseInteger.cpp b/test/Numbers/parseInteger.cpp index 40d33f28..e8b3b37b 100644 --- a/test/Numbers/parseInteger.cpp +++ b/test/Numbers/parseInteger.cpp @@ -21,11 +21,8 @@ TEST_CASE("parseInteger()") { check("+127", 127); check("3.14", 3); check("x42", 0); - check("128", -128); - check("-129", 127); - check(NULL, 0); - check("true", 1); - check("false", 0); + check("128", 0); // overflow + check("-129", 0); // overflow } TEST_CASE("parseInteger()") { @@ -34,11 +31,8 @@ TEST_CASE("parseInteger()") { check("+32767", 32767); check("3.14", 3); check("x42", 0); - check("-32769", 32767); - check("32768", -32768); - check(NULL, 0); - check("true", 1); - check("false", 0); + check("-32769", 0); // overflow + check("32768", 0); // overflow } TEST_CASE("parseInteger()") { @@ -47,10 +41,8 @@ TEST_CASE("parseInteger()") { check("+2147483647", 2147483647); check("3.14", 3); check("x42", 0); - check("-2147483649", 2147483647); - check("2147483648", (-2147483647 - 1)); - check("true", 1); - check("false", 0); + check("-2147483649", 0); // overflow + check("2147483648", 0); // overflow } TEST_CASE("parseInteger()") { @@ -59,10 +51,8 @@ TEST_CASE("parseInteger()") { check("+255", 255); check("3.14", 3); check("x42", 0); - check("-1", 255); + check("-1", 0); check("256", 0); - check("true", 1); - check("false", 0); } TEST_CASE("parseInteger()") { @@ -72,8 +62,6 @@ TEST_CASE("parseInteger()") { check("3.14", 3); // check(" 42", 0); check("x42", 0); - check("-1", 65535); + check("-1", 0); check("65536", 0); - check("true", 1); - check("false", 0); } diff --git a/test/Numbers/parseNumber.cpp b/test/Numbers/parseNumber.cpp new file mode 100644 index 00000000..19638fed --- /dev/null +++ b/test/Numbers/parseNumber.cpp @@ -0,0 +1,18 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#include +#include + +using namespace ARDUINOJSON_NAMESPACE; + +TEST_CASE("Test uint32_t overflow") { + ParsedNumber first = + parseNumber("4294967295"); + ParsedNumber second = + parseNumber("4294967296"); + + REQUIRE(first.type() == uint8_t(VALUE_IS_POSITIVE_INTEGER)); + REQUIRE(second.type() == uint8_t(VALUE_IS_FLOAT)); +}