forked from bblanchon/ArduinoJson
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