From 04484fdc16dba272ae36d82cbb2e50b8b5c3b018 Mon Sep 17 00:00:00 2001 From: ComplexPlane <46565903+ComplexPlane@users.noreply.github.com> Date: Sat, 21 Jan 2023 09:44:54 -0500 Subject: [PATCH 01/32] Transplant from fukkatsu that at least compiles --- .gitignore | 2 + Makefile | 7 +- dep/ArduinoJson/ArduinoJson.h | 7226 ++++++++++++++++++++++++ rel/config.cpp | 582 +- rel/include/config.h | 8 +- rel/include/log.h | 28 +- rel/include/{vecutil.h => mathutils.h} | 2 + rel/include/mkb2_ghidra.h | 3 +- rel/log.cpp | 32 + rel/main.cpp | 2 +- script/gen_compile_commands.py | 36 + 11 files changed, 7671 insertions(+), 257 deletions(-) create mode 100644 dep/ArduinoJson/ArduinoJson.h rename rel/include/{vecutil.h => mathutils.h} (91%) create mode 100644 rel/log.cpp create mode 100755 script/gen_compile_commands.py diff --git a/.gitignore b/.gitignore index 3167ea35..ee13d5a5 100644 --- a/.gitignore +++ b/.gitignore @@ -288,3 +288,5 @@ __pycache__/ /compile_commands.json /ApeSphere.* /.qtc_clangd + +.cache/ diff --git a/Makefile b/Makefile index fdaa6621..0ed9aa98 100644 --- a/Makefile +++ b/Makefile @@ -69,11 +69,11 @@ MACHDEP = -mno-sdata -mgcn -DGEKKO -mcpu=750 -meabi -mhard-float # -Wno-write-strings because some GC SDK functions take non-const char *, # and Ghidra can't represent const char * anyhow -CFLAGS = -nostdlib -ffreestanding -ffunction-sections -fdata-sections -g -Os -Wall -Wno-write-strings $(MACHDEP) $(INCLUDE) +CFLAGS = -nostdlib -lgcc -ffreestanding -ffunction-sections -fdata-sections -g -Os -Wall -Wno-write-strings $(MACHDEP) $(INCLUDE) CXXFLAGS = -fno-exceptions -fno-rtti -std=gnu++20 $(CFLAGS) ASFLAGS = -mregnames # Don't require % in front of register names -LDFLAGS = -r -e _prolog -u _prolog -u _epilog -u _unresolved -Wl,--gc-sections -nostdlib -g $(MACHDEP) -Wl,-Map,$(notdir $@).map +LDFLAGS = -r -e _prolog -u _prolog -u _epilog -u _unresolved -Wl,--gc-sections -nostdlib -lgcc -g $(MACHDEP) -Wl,-Map,$(notdir $@).map #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project @@ -146,7 +146,8 @@ endif export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ -I$(CURDIR)/$(BUILD) \ - -I$(LIBOGC_INC) + -I$(LIBOGC_INC) \ + -I$(CURDIR)/dep #--------------------------------------------------------------------------------- # build a list of library paths diff --git a/dep/ArduinoJson/ArduinoJson.h b/dep/ArduinoJson/ArduinoJson.h new file mode 100644 index 00000000..6c9a65cd --- /dev/null +++ b/dep/ArduinoJson/ArduinoJson.h @@ -0,0 +1,7226 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2022, Benoit BLANCHON +// MIT License + +#pragma once + +#ifdef __cplusplus + +#if __cplusplus >= 201103L +# define ARDUINOJSON_HAS_LONG_LONG 1 +# define ARDUINOJSON_HAS_RVALUE_REFERENCES 1 +#else +# define ARDUINOJSON_HAS_LONG_LONG 0 +# define ARDUINOJSON_HAS_RVALUE_REFERENCES 0 +#endif +#ifndef ARDUINOJSON_HAS_NULLPTR +# if __cplusplus >= 201103L +# define ARDUINOJSON_HAS_NULLPTR 1 +# else +# define ARDUINOJSON_HAS_NULLPTR 0 +# endif +#endif +#if defined(_MSC_VER) && !ARDUINOJSON_HAS_LONG_LONG +# define ARDUINOJSON_HAS_INT64 1 +#else +# define ARDUINOJSON_HAS_INT64 0 +#endif +#ifndef ARDUINOJSON_ENABLE_STD_STREAM +# ifdef __has_include +# if __has_include() && \ + __has_include() && \ + !defined(min) && \ + !defined(max) +# define ARDUINOJSON_ENABLE_STD_STREAM 1 +# else +# define ARDUINOJSON_ENABLE_STD_STREAM 0 +# endif +# else +# ifdef ARDUINO +# define ARDUINOJSON_ENABLE_STD_STREAM 0 +# else +# define ARDUINOJSON_ENABLE_STD_STREAM 1 +# endif +# endif +#endif +#ifndef ARDUINOJSON_ENABLE_STD_STRING +# ifdef __has_include +# if __has_include() && !defined(min) && !defined(max) +# define ARDUINOJSON_ENABLE_STD_STRING 1 +# else +# define ARDUINOJSON_ENABLE_STD_STRING 0 +# endif +# else +# ifdef ARDUINO +# define ARDUINOJSON_ENABLE_STD_STRING 0 +# else +# define ARDUINOJSON_ENABLE_STD_STRING 1 +# endif +# endif +#endif +#ifndef ARDUINOJSON_ENABLE_STRING_VIEW +# ifdef __has_include +# if __has_include() && __cplusplus >= 201703L +# define ARDUINOJSON_ENABLE_STRING_VIEW 1 +# else +# define ARDUINOJSON_ENABLE_STRING_VIEW 0 +# endif +# else +# define ARDUINOJSON_ENABLE_STRING_VIEW 0 +# endif +#endif +#ifndef ARDUINOJSON_USE_DOUBLE +# define ARDUINOJSON_USE_DOUBLE 1 +#endif +#ifndef ARDUINOJSON_USE_LONG_LONG +# if ARDUINOJSON_HAS_LONG_LONG && defined(__SIZEOF_POINTER__) && \ + __SIZEOF_POINTER__ >= 4 || \ + defined(_MSC_VER) +# define ARDUINOJSON_USE_LONG_LONG 1 +# endif +#endif +#ifndef ARDUINOJSON_USE_LONG_LONG +# define ARDUINOJSON_USE_LONG_LONG 0 +#endif +#ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT +# define ARDUINOJSON_DEFAULT_NESTING_LIMIT 10 +#endif +#ifndef ARDUINOJSON_SLOT_OFFSET_SIZE +# if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ <= 2 +# define ARDUINOJSON_SLOT_OFFSET_SIZE 1 +# elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ >= 8 || \ + defined(_WIN64) && _WIN64 +# define ARDUINOJSON_SLOT_OFFSET_SIZE 4 +# else +# define ARDUINOJSON_SLOT_OFFSET_SIZE 2 +# endif +#endif +#ifdef ARDUINO +# ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING +# define ARDUINOJSON_ENABLE_ARDUINO_STRING 1 +# endif +# ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM +# define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1 +# endif +# ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT +# define ARDUINOJSON_ENABLE_ARDUINO_PRINT 1 +# endif +# ifndef ARDUINOJSON_ENABLE_PROGMEM +# define ARDUINOJSON_ENABLE_PROGMEM 1 +# endif +#else // ARDUINO +# ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING +# define ARDUINOJSON_ENABLE_ARDUINO_STRING 0 +# endif +# ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM +# define ARDUINOJSON_ENABLE_ARDUINO_STREAM 0 +# endif +# ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT +# define ARDUINOJSON_ENABLE_ARDUINO_PRINT 0 +# endif +# ifndef ARDUINOJSON_ENABLE_PROGMEM +# define ARDUINOJSON_ENABLE_PROGMEM 0 +# endif +#endif // ARDUINO +#ifndef ARDUINOJSON_DECODE_UNICODE +# define ARDUINOJSON_DECODE_UNICODE 1 +#endif +#ifndef ARDUINOJSON_ENABLE_COMMENTS +# define ARDUINOJSON_ENABLE_COMMENTS 0 +#endif +#ifndef ARDUINOJSON_ENABLE_NAN +# define ARDUINOJSON_ENABLE_NAN 0 +#endif +#ifndef ARDUINOJSON_ENABLE_INFINITY +# define ARDUINOJSON_ENABLE_INFINITY 0 +#endif +#ifndef ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD +# define ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD 1e7 +#endif +#ifndef ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD +# define ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD 1e-5 +#endif +#ifndef ARDUINOJSON_LITTLE_ENDIAN +# if defined(_MSC_VER) || \ + (defined(__BYTE_ORDER__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + defined(__LITTLE_ENDIAN__) || defined(__i386) || defined(__x86_64) +# define ARDUINOJSON_LITTLE_ENDIAN 1 +# else +# define ARDUINOJSON_LITTLE_ENDIAN 0 +# endif +#endif +#ifndef ARDUINOJSON_ENABLE_ALIGNMENT +# if defined(__AVR) +# define ARDUINOJSON_ENABLE_ALIGNMENT 0 +# else +# define ARDUINOJSON_ENABLE_ALIGNMENT 1 +# endif +#endif +#ifndef ARDUINOJSON_TAB +# define ARDUINOJSON_TAB " " +#endif +#ifndef ARDUINOJSON_ENABLE_STRING_DEDUPLICATION +# define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 1 +#endif +#ifndef ARDUINOJSON_STRING_BUFFER_SIZE +# define ARDUINOJSON_STRING_BUFFER_SIZE 32 +#endif +#ifndef ARDUINOJSON_DEBUG +# ifdef __PLATFORMIO_BUILD_DEBUG__ +# define ARDUINOJSON_DEBUG 1 +# else +# define ARDUINOJSON_DEBUG 0 +# endif +#endif +#if ARDUINOJSON_HAS_NULLPTR && defined(nullptr) +# error nullptr is defined as a macro. Remove the faulty #define or #undef nullptr +#endif +#if ARDUINOJSON_ENABLE_ARDUINO_STRING || ARDUINOJSON_ENABLE_ARDUINO_STREAM || \ + ARDUINOJSON_ENABLE_ARDUINO_PRINT || ARDUINOJSON_ENABLE_PROGMEM +#include +#endif +#if !ARDUINOJSON_DEBUG +# ifdef __clang__ +# pragma clang system_header +# elif defined __GNUC__ +# pragma GCC system_header +# endif +#endif +#define ARDUINOJSON_EXPAND6(a, b, c, d, e, f) a, b, c, d, e, f +#define ARDUINOJSON_EXPAND9(a, b, c, d, e, f, g, h, i) a, b, c, d, e, f, g, h, i +#define ARDUINOJSON_EXPAND18(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, \ + q, r) \ + a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r +#define ARDUINOJSON_CONCAT_(A, B) A##B +#define ARDUINOJSON_CONCAT2(A, B) ARDUINOJSON_CONCAT_(A, B) +#define ARDUINOJSON_CONCAT4(A, B, C, D) \ + ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT2(A, B), ARDUINOJSON_CONCAT2(C, D)) +#define ARDUINOJSON_HEX_DIGIT_0000() 0 +#define ARDUINOJSON_HEX_DIGIT_0001() 1 +#define ARDUINOJSON_HEX_DIGIT_0010() 2 +#define ARDUINOJSON_HEX_DIGIT_0011() 3 +#define ARDUINOJSON_HEX_DIGIT_0100() 4 +#define ARDUINOJSON_HEX_DIGIT_0101() 5 +#define ARDUINOJSON_HEX_DIGIT_0110() 6 +#define ARDUINOJSON_HEX_DIGIT_0111() 7 +#define ARDUINOJSON_HEX_DIGIT_1000() 8 +#define ARDUINOJSON_HEX_DIGIT_1001() 9 +#define ARDUINOJSON_HEX_DIGIT_1010() A +#define ARDUINOJSON_HEX_DIGIT_1011() B +#define ARDUINOJSON_HEX_DIGIT_1100() C +#define ARDUINOJSON_HEX_DIGIT_1101() D +#define ARDUINOJSON_HEX_DIGIT_1110() E +#define ARDUINOJSON_HEX_DIGIT_1111() F +#define ARDUINOJSON_HEX_DIGIT_(A, B, C, D) ARDUINOJSON_HEX_DIGIT_##A##B##C##D() +#define ARDUINOJSON_HEX_DIGIT(A, B, C, D) ARDUINOJSON_HEX_DIGIT_(A, B, C, D) +#define ARDUINOJSON_VERSION "6.20.0" +#define ARDUINOJSON_VERSION_MAJOR 6 +#define ARDUINOJSON_VERSION_MINOR 20 +#define ARDUINOJSON_VERSION_REVISION 0 +#ifndef ARDUINOJSON_NAMESPACE +# define ARDUINOJSON_NAMESPACE \ + ARDUINOJSON_CONCAT4( \ + ARDUINOJSON_CONCAT4(ArduinoJson, ARDUINOJSON_VERSION_MAJOR, \ + ARDUINOJSON_VERSION_MINOR, \ + ARDUINOJSON_VERSION_REVISION), \ + _, \ + ARDUINOJSON_HEX_DIGIT( \ + ARDUINOJSON_ENABLE_PROGMEM, ARDUINOJSON_USE_LONG_LONG, \ + ARDUINOJSON_USE_DOUBLE, ARDUINOJSON_ENABLE_STRING_DEDUPLICATION), \ + ARDUINOJSON_HEX_DIGIT( \ + ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \ + ARDUINOJSON_ENABLE_COMMENTS, ARDUINOJSON_DECODE_UNICODE)) +#endif +namespace ARDUINOJSON_NAMESPACE { +template +struct Converter; +template +class InvalidConversion; // Error here? See https://arduinojson.org/v6/invalid-conversion/ +template +struct ConverterNeedsWriteableRef; +} // namespace ARDUINOJSON_NAMESPACE +#include +#include +namespace ARDUINOJSON_NAMESPACE { +#if ARDUINOJSON_ENABLE_ALIGNMENT +inline bool isAligned(size_t value) { + const size_t mask = sizeof(void*) - 1; + size_t addr = value; + return (addr & mask) == 0; +} +inline size_t addPadding(size_t bytes) { + const size_t mask = sizeof(void*) - 1; + return (bytes + mask) & ~mask; +} +template +struct AddPadding { + static const size_t mask = sizeof(void*) - 1; + static const size_t value = (bytes + mask) & ~mask; +}; +#else +inline bool isAligned(size_t) { + return true; +} +inline size_t addPadding(size_t bytes) { + return bytes; +} +template +struct AddPadding { + static const size_t value = bytes; +}; +#endif +template +inline bool isAligned(T* ptr) { + return isAligned(reinterpret_cast(ptr)); +} +template +inline T* addPadding(T* p) { + size_t address = addPadding(reinterpret_cast(p)); + return reinterpret_cast(address); +} +} // namespace ARDUINOJSON_NAMESPACE +#if ARDUINOJSON_DEBUG +#include +# define ARDUINOJSON_ASSERT(X) assert(X) +#else +# define ARDUINOJSON_ASSERT(X) ((void)0) +#endif +namespace ARDUINOJSON_NAMESPACE { +template Y)> +struct Max {}; +template +struct Max { + static const size_t value = X; +}; +template +struct Max { + static const size_t value = Y; +}; +template +struct conditional { + typedef TrueType type; +}; +template +struct conditional { + typedef FalseType type; +}; +template +struct enable_if {}; +template +struct enable_if { + typedef T type; +}; +template +struct integral_constant { + static const T value = v; +}; +typedef integral_constant true_type; +typedef integral_constant false_type; +template +struct is_array : false_type {}; +template +struct is_array : true_type {}; +template +struct is_array : true_type {}; +template +class is_base_of { + protected: // <- to avoid GCC's "all member functions in class are private" + static int probe(const TBase*); + static char probe(...); + public: + static const bool value = + sizeof(probe(reinterpret_cast(0))) == sizeof(int); +}; +template +T declval(); +template +struct is_class { + protected: // <- to avoid GCC's "all member functions in class are private" + template + static int probe(void (U::*)(void)); + template + static char probe(...); + public: + static const bool value = sizeof(probe(0)) == sizeof(int); +}; +template +struct is_const : false_type {}; +template +struct is_const : true_type {}; +} // namespace ARDUINOJSON_NAMESPACE +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4244) +#endif +#ifdef __ICCARM__ +#pragma diag_suppress=Pa093 +#endif +namespace ARDUINOJSON_NAMESPACE { +template +struct is_convertible { + protected: // <- to avoid GCC's "all member functions in class are private" + static int probe(To); + static char probe(...); + static From& _from; + public: + static const bool value = sizeof(probe(_from)) == sizeof(int); +}; +} // namespace ARDUINOJSON_NAMESPACE +#ifdef _MSC_VER +# pragma warning(pop) +#endif +#ifdef __ICCARM__ +#pragma diag_default=Pa093 +#endif +namespace ARDUINOJSON_NAMESPACE { +template +struct is_same : false_type {}; +template +struct is_same : true_type {}; +template +struct remove_cv { + typedef T type; +}; +template +struct remove_cv { + typedef T type; +}; +template +struct remove_cv { + typedef T type; +}; +template +struct remove_cv { + typedef T type; +}; +template +struct is_floating_point + : integral_constant< + bool, // + is_same::type>::value || + is_same::type>::value> {}; +template +struct is_integral : integral_constant::type, signed char>::value || + is_same::type, unsigned char>::value || + is_same::type, signed short>::value || + is_same::type, unsigned short>::value || + is_same::type, signed int>::value || + is_same::type, unsigned int>::value || + is_same::type, signed long>::value || + is_same::type, unsigned long>::value || +#if ARDUINOJSON_HAS_LONG_LONG + is_same::type, signed long long>::value || + is_same::type, unsigned long long>::value || +#endif +#if ARDUINOJSON_HAS_INT64 + is_same::type, signed __int64>::value || + is_same::type, unsigned __int64>::value || +#endif + is_same::type, char>::value || + is_same::type, bool>::value> {}; +template +struct is_enum { + static const bool value = is_convertible::value && + !is_class::value && !is_integral::value && + !is_floating_point::value; +}; +template +struct is_pointer : false_type {}; +template +struct is_pointer : true_type {}; +template +struct is_signed : integral_constant::type, char>::value || + is_same::type, signed char>::value || + is_same::type, signed short>::value || + is_same::type, signed int>::value || + is_same::type, signed long>::value || +#if ARDUINOJSON_HAS_LONG_LONG + is_same::type, signed long long>::value || +#endif +#if ARDUINOJSON_HAS_INT64 + is_same::type, signed __int64>::value || +#endif + is_same::type, float>::value || + is_same::type, double>::value> {}; +template +struct is_unsigned : integral_constant::type, unsigned char>::value || + is_same::type, unsigned short>::value || + is_same::type, unsigned int>::value || + is_same::type, unsigned long>::value || +#if ARDUINOJSON_HAS_INT64 + is_same::type, unsigned __int64>::value || +#endif +#if ARDUINOJSON_HAS_LONG_LONG + is_same::type, unsigned long long>::value || +#endif + is_same::type, bool>::value> {}; +template +struct type_identity { + typedef T type; +}; +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 +template +struct make_void { + typedef void type; +}; +template +struct remove_const { + typedef T type; +}; +template +struct remove_const { + typedef T type; +}; +template +struct remove_reference { + typedef T type; +}; +template +struct remove_reference { + typedef T type; +}; +} // namespace ARDUINOJSON_NAMESPACE +#include +namespace ARDUINOJSON_NAMESPACE { +namespace StringStoragePolicy { +struct Link {}; +struct Copy {}; +struct LinkOrCopy { + bool link; +}; +} // namespace StringStoragePolicy +template +struct StringAdapter; +template +struct SizedStringAdapter; +template +typename StringAdapter::AdaptedString adaptString(const TString& s) { + return StringAdapter::adapt(s); +} +template +typename StringAdapter::AdaptedString adaptString(TChar* p) { + return StringAdapter::adapt(p); +} +template +typename SizedStringAdapter::AdaptedString adaptString(TChar* p, + size_t n) { + return SizedStringAdapter::adapt(p, n); +} +template +struct IsChar + : integral_constant::value && sizeof(T) == 1> {}; +class ZeroTerminatedRamString { + public: + static const size_t typeSortKey = 3; + ZeroTerminatedRamString(const char* str) : _str(str) {} + bool isNull() const { + return !_str; + } + size_t size() const { + return _str ? ::strlen(_str) : 0; + } + char operator[](size_t i) const { + ARDUINOJSON_ASSERT(_str != 0); + ARDUINOJSON_ASSERT(i <= size()); + return _str[i]; + } + const char* data() const { + return _str; + } + friend int stringCompare(ZeroTerminatedRamString a, + ZeroTerminatedRamString b) { + ARDUINOJSON_ASSERT(!a.isNull()); + ARDUINOJSON_ASSERT(!b.isNull()); + return ::strcmp(a._str, b._str); + } + friend bool stringEquals(ZeroTerminatedRamString a, + ZeroTerminatedRamString b) { + return stringCompare(a, b) == 0; + } + StringStoragePolicy::Copy storagePolicy() const { + return StringStoragePolicy::Copy(); + } + protected: + const char* _str; +}; +template +struct StringAdapter::value>::type> { + typedef ZeroTerminatedRamString AdaptedString; + static AdaptedString adapt(const TChar* p) { + return AdaptedString(reinterpret_cast(p)); + } +}; +template +struct StringAdapter::value>::type> { + typedef ZeroTerminatedRamString AdaptedString; + static AdaptedString adapt(const TChar* p) { + return AdaptedString(reinterpret_cast(p)); + } +}; +class StaticStringAdapter : public ZeroTerminatedRamString { + public: + StaticStringAdapter(const char* str) : ZeroTerminatedRamString(str) {} + StringStoragePolicy::Link storagePolicy() const { + return StringStoragePolicy::Link(); + } +}; +template <> +struct StringAdapter { + typedef StaticStringAdapter AdaptedString; + static AdaptedString adapt(const char* p) { + return AdaptedString(p); + } +}; +class SizedRamString { + public: + static const size_t typeSortKey = 2; + SizedRamString(const char* str, size_t sz) : _str(str), _size(sz) {} + bool isNull() const { + return !_str; + } + size_t size() const { + return _size; + } + char operator[](size_t i) const { + ARDUINOJSON_ASSERT(_str != 0); + ARDUINOJSON_ASSERT(i <= size()); + return _str[i]; + } + const char* data() const { + return _str; + } + StringStoragePolicy::Copy storagePolicy() const { + return StringStoragePolicy::Copy(); + } + protected: + const char* _str; + size_t _size; +}; +template +struct SizedStringAdapter::value>::type> { + typedef SizedRamString AdaptedString; + static AdaptedString adapt(const TChar* p, size_t n) { + return AdaptedString(reinterpret_cast(p), n); + } +}; +template +class SafeBoolIdom { + protected: + typedef void (T::*bool_type)() const; + void safeBoolHelper() const {} + static bool_type safe_true() { + return &SafeBoolIdom::safeBoolHelper; + } + static bool_type safe_false() { + return 0; + } +}; +} // namespace ARDUINOJSON_NAMESPACE +#if ARDUINOJSON_ENABLE_STD_STREAM +#include +#endif +namespace ARDUINOJSON_NAMESPACE { +class JsonString : public SafeBoolIdom { + public: + enum Ownership { Copied, Linked }; + JsonString() : _data(0), _size(0), _ownership(Linked) {} + JsonString(const char* data, Ownership ownership = Linked) + : _data(data), _size(data ? ::strlen(data) : 0), _ownership(ownership) {} + JsonString(const char* data, size_t sz, Ownership ownership = Linked) + : _data(data), _size(sz), _ownership(ownership) {} + const char* c_str() const { + return _data; + } + bool isNull() const { + return !_data; + } + bool isLinked() const { + return _ownership == Linked; + } + size_t size() const { + return _size; + } + operator bool_type() const { + return _data ? safe_true() : safe_false(); + } + friend bool operator==(JsonString lhs, JsonString rhs) { + if (lhs._size != rhs._size) + return false; + if (lhs._data == rhs._data) + return true; + if (!lhs._data) + return false; + if (!rhs._data) + return false; + return memcmp(lhs._data, rhs._data, lhs._size) == 0; + } + friend bool operator!=(JsonString lhs, JsonString rhs) { + return !(lhs == rhs); + } +#if ARDUINOJSON_ENABLE_STD_STREAM + friend std::ostream& operator<<(std::ostream& lhs, const JsonString& rhs) { + lhs.write(rhs.c_str(), static_cast(rhs.size())); + return lhs; + } +#endif + private: + const char* _data; + size_t _size; + Ownership _ownership; +}; +class JsonStringAdapter : public SizedRamString { + public: + JsonStringAdapter(const JsonString& s) + : SizedRamString(s.c_str(), s.size()), _linked(s.isLinked()) {} + StringStoragePolicy::LinkOrCopy storagePolicy() const { + StringStoragePolicy::LinkOrCopy policy = {_linked}; + return policy; + } + private: + bool _linked; +}; +template <> +struct StringAdapter { + typedef JsonStringAdapter AdaptedString; + static AdaptedString adapt(const JsonString& s) { + return AdaptedString(s); + } +}; +} // namespace ARDUINOJSON_NAMESPACE +#if ARDUINOJSON_ENABLE_STD_STRING +#include +namespace ARDUINOJSON_NAMESPACE { +template +struct StringAdapter, void> { + typedef SizedRamString AdaptedString; + static AdaptedString adapt( + const std::basic_string& s) { + return AdaptedString(s.c_str(), s.size()); + } +}; +} // namespace ARDUINOJSON_NAMESPACE +#endif +#if ARDUINOJSON_ENABLE_STRING_VIEW +#include +namespace ARDUINOJSON_NAMESPACE { +template <> +struct StringAdapter { + typedef SizedRamString AdaptedString; + static AdaptedString adapt(const std::string_view& s) { + return AdaptedString(s.data(), s.size()); + } +}; +} // namespace ARDUINOJSON_NAMESPACE +#endif +#if ARDUINOJSON_ENABLE_ARDUINO_STRING +namespace ARDUINOJSON_NAMESPACE { +template +struct StringAdapter< + T, typename enable_if::value || + is_same::value>::type> { + typedef SizedRamString AdaptedString; + static AdaptedString adapt(const ::String& s) { + return AdaptedString(s.c_str(), s.length()); + } +}; +} // namespace ARDUINOJSON_NAMESPACE +#endif +#if ARDUINOJSON_ENABLE_PROGMEM +namespace ARDUINOJSON_NAMESPACE { +struct pgm_p { + pgm_p(const void* p) : address(reinterpret_cast(p)) {} + const char* address; +}; +} // namespace ARDUINOJSON_NAMESPACE +#ifndef strlen_P +inline size_t strlen_P(ARDUINOJSON_NAMESPACE::pgm_p s) { + const char* p = s.address; + ARDUINOJSON_ASSERT(p != NULL); + while (pgm_read_byte(p)) + p++; + return size_t(p - s.address); +} +#endif +#ifndef strncmp_P +inline int strncmp_P(const char* a, ARDUINOJSON_NAMESPACE::pgm_p b, size_t n) { + const char* s1 = a; + const char* s2 = b.address; + ARDUINOJSON_ASSERT(s1 != NULL); + ARDUINOJSON_ASSERT(s2 != NULL); + while (n-- > 0) { + char c1 = *s1++; + char c2 = static_cast(pgm_read_byte(s2++)); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (c1 == 0 /* and c2 as well */) + return 0; + } + return 0; +} +#endif +#ifndef strcmp_P +inline int strcmp_P(const char* a, ARDUINOJSON_NAMESPACE::pgm_p b) { + const char* s1 = a; + const char* s2 = b.address; + ARDUINOJSON_ASSERT(s1 != NULL); + ARDUINOJSON_ASSERT(s2 != NULL); + for (;;) { + char c1 = *s1++; + char c2 = static_cast(pgm_read_byte(s2++)); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (c1 == 0 /* and c2 as well */) + return 0; + } +} +#endif +#ifndef memcmp_P +inline int memcmp_P(const void* a, ARDUINOJSON_NAMESPACE::pgm_p b, size_t n) { + const uint8_t* p1 = reinterpret_cast(a); + const char* p2 = b.address; + ARDUINOJSON_ASSERT(p1 != NULL); + ARDUINOJSON_ASSERT(p2 != NULL); + while (n-- > 0) { + uint8_t v1 = *p1++; + uint8_t v2 = pgm_read_byte(p2++); + if (v1 != v2) + return v1 - v2; + } + return 0; +} +#endif +#ifndef memcpy_P +inline void* memcpy_P(void* dst, ARDUINOJSON_NAMESPACE::pgm_p src, size_t n) { + uint8_t* d = reinterpret_cast(dst); + const char* s = src.address; + ARDUINOJSON_ASSERT(d != NULL); + ARDUINOJSON_ASSERT(s != NULL); + while (n-- > 0) { + *d++ = pgm_read_byte(s++); + } + return dst; +} +#endif +#ifndef pgm_read_dword +inline uint32_t pgm_read_dword(ARDUINOJSON_NAMESPACE::pgm_p p) { + uint32_t result; + memcpy_P(&result, p.address, 4); + return result; +} +#endif +#ifndef pgm_read_ptr +inline void* pgm_read_ptr(ARDUINOJSON_NAMESPACE::pgm_p p) { + void* result; + memcpy_P(&result, p.address, sizeof(result)); + return result; +} +#endif +namespace ARDUINOJSON_NAMESPACE { +class FlashString { + public: + static const size_t typeSortKey = 1; + FlashString(const __FlashStringHelper* str, size_t sz) + : _str(reinterpret_cast(str)), _size(sz) {} + bool isNull() const { + return !_str; + } + char operator[](size_t i) const { + ARDUINOJSON_ASSERT(_str != 0); + ARDUINOJSON_ASSERT(i <= _size); + return static_cast(pgm_read_byte(_str + i)); + } + size_t size() const { + return _size; + } + friend bool stringEquals(FlashString a, SizedRamString b) { + ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey); + ARDUINOJSON_ASSERT(!a.isNull()); + ARDUINOJSON_ASSERT(!b.isNull()); + if (a.size() != b.size()) + return false; + return ::memcmp_P(b.data(), a._str, a._size) == 0; + } + friend int stringCompare(FlashString a, SizedRamString b) { + ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey); + ARDUINOJSON_ASSERT(!a.isNull()); + ARDUINOJSON_ASSERT(!b.isNull()); + size_t minsize = a.size() < b.size() ? a.size() : b.size(); + int res = ::memcmp_P(b.data(), a._str, minsize); + if (res) + return -res; + if (a.size() < b.size()) + return -1; + if (a.size() > b.size()) + return 1; + return 0; + } + friend void stringGetChars(FlashString s, char* p, size_t n) { + ARDUINOJSON_ASSERT(s.size() <= n); + ::memcpy_P(p, s._str, n); + } + StringStoragePolicy::Copy storagePolicy() const { + return StringStoragePolicy::Copy(); + } + private: + const char* _str; + size_t _size; +}; +template <> +struct StringAdapter { + typedef FlashString AdaptedString; + static AdaptedString adapt(const __FlashStringHelper* s) { + return AdaptedString(s, s ? strlen_P(reinterpret_cast(s)) : 0); + } +}; +template <> +struct SizedStringAdapter { + typedef FlashString AdaptedString; + static AdaptedString adapt(const __FlashStringHelper* s, size_t n) { + return AdaptedString(s, n); + } +}; +} // namespace ARDUINOJSON_NAMESPACE +#endif +namespace ARDUINOJSON_NAMESPACE { +template +typename enable_if::type +stringCompare(TAdaptedString1 s1, TAdaptedString2 s2) { + ARDUINOJSON_ASSERT(!s1.isNull()); + ARDUINOJSON_ASSERT(!s2.isNull()); + size_t size1 = s1.size(); + size_t size2 = s2.size(); + size_t n = size1 < size2 ? size1 : size2; + for (size_t i = 0; i < n; i++) { + if (s1[i] != s2[i]) + return s1[i] - s2[i]; + } + if (size1 < size2) + return -1; + if (size1 > size2) + return 1; + return 0; +} +template +typename enable_if< + (TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), int>::type +stringCompare(TAdaptedString1 s1, TAdaptedString2 s2) { + return -stringCompare(s2, s1); +} +template +typename enable_if::type +stringEquals(TAdaptedString1 s1, TAdaptedString2 s2) { + ARDUINOJSON_ASSERT(!s1.isNull()); + ARDUINOJSON_ASSERT(!s2.isNull()); + size_t size1 = s1.size(); + size_t size2 = s2.size(); + if (size1 != size2) + return false; + for (size_t i = 0; i < size1; i++) { + if (s1[i] != s2[i]) + return false; + } + return true; +} +template +typename enable_if< + (TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), bool>::type +stringEquals(TAdaptedString1 s1, TAdaptedString2 s2) { + return stringEquals(s2, s1); +} +template +static void stringGetChars(TAdaptedString s, char* p, size_t n) { + ARDUINOJSON_ASSERT(s.size() <= n); + for (size_t i = 0; i < n; i++) { + p[i] = s[i]; + } +} +template +struct int_t; +template <> +struct int_t<8> { + typedef int8_t type; +}; +template <> +struct int_t<16> { + typedef int16_t type; +}; +template <> +struct int_t<32> { + typedef int32_t type; +}; +} // namespace ARDUINOJSON_NAMESPACE +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4310) +#endif +namespace ARDUINOJSON_NAMESPACE { +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 +namespace ARDUINOJSON_NAMESPACE { +class MemoryPool; +class VariantData; +class VariantSlot; +class CollectionData { + VariantSlot* _head; + VariantSlot* _tail; + public: + VariantData* addElement(MemoryPool* pool); + VariantData* getElement(size_t index) const; + VariantData* getOrAddElement(size_t index, MemoryPool* pool); + void removeElement(size_t index); + template + VariantData* addMember(TAdaptedString key, MemoryPool* pool); + template + VariantData* getMember(TAdaptedString key) const; + template + VariantData* getOrAddMember(TAdaptedString key, MemoryPool* pool); + template + void removeMember(TAdaptedString key) { + removeSlot(getSlot(key)); + } + template + bool containsKey(const TAdaptedString& key) const; + void clear(); + size_t memoryUsage() const; + size_t size() const; + VariantSlot* addSlot(MemoryPool*); + void removeSlot(VariantSlot* slot); + bool copyFrom(const CollectionData& src, MemoryPool* pool); + VariantSlot* head() const { + return _head; + } + void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance); + private: + VariantSlot* getSlot(size_t index) const; + template + VariantSlot* getSlot(TAdaptedString key) const; + VariantSlot* getPreviousSlot(VariantSlot*) const; +}; +inline const VariantData* collectionToVariant( + const CollectionData* collection) { + const void* data = collection; // prevent warning cast-align + return reinterpret_cast(data); +} +inline VariantData* collectionToVariant(CollectionData* collection) { + void* data = collection; // prevent warning cast-align + return reinterpret_cast(data); +} +#if ARDUINOJSON_USE_DOUBLE +typedef double JsonFloat; +#else +typedef float JsonFloat; +#endif +#if ARDUINOJSON_USE_LONG_LONG +typedef int64_t JsonInteger; +typedef uint64_t JsonUInt; +#else +typedef long JsonInteger; +typedef unsigned long JsonUInt; +#endif +} // namespace ARDUINOJSON_NAMESPACE +#if ARDUINOJSON_HAS_LONG_LONG && !ARDUINOJSON_USE_LONG_LONG +# define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T) \ + static_assert(sizeof(T) <= sizeof(ARDUINOJSON_NAMESPACE::JsonInteger), \ + "To use 64-bit integers with ArduinoJson, you must set " \ + "ARDUINOJSON_USE_LONG_LONG to 1. See " \ + "https://arduinojson.org/v6/api/config/use_long_long/"); +#else +# define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T) +#endif +namespace ARDUINOJSON_NAMESPACE { +enum { + VALUE_MASK = 0x7F, + OWNED_VALUE_BIT = 0x01, + VALUE_IS_NULL = 0, + VALUE_IS_LINKED_RAW = 0x02, + VALUE_IS_OWNED_RAW = 0x03, + VALUE_IS_LINKED_STRING = 0x04, + VALUE_IS_OWNED_STRING = 0x05, + VALUE_IS_BOOLEAN = 0x06, + NUMBER_BIT = 0x08, + VALUE_IS_UNSIGNED_INTEGER = 0x08, + VALUE_IS_SIGNED_INTEGER = 0x0A, + VALUE_IS_FLOAT = 0x0C, + COLLECTION_MASK = 0x60, + VALUE_IS_OBJECT = 0x20, + VALUE_IS_ARRAY = 0x40, + OWNED_KEY_BIT = 0x80 +}; +struct RawData { + const char* data; + size_t size; +}; +union VariantContent { + JsonFloat asFloat; + bool asBoolean; + JsonUInt asUnsignedInteger; + JsonInteger asSignedInteger; + CollectionData asCollection; + struct { + const char* data; + size_t size; + } asString; +}; +typedef int_t::type VariantSlotDiff; +class VariantSlot { + VariantContent _content; + uint8_t _flags; + VariantSlotDiff _next; + const char* _key; + public: + VariantData* data() { + return reinterpret_cast(&_content); + } + const VariantData* data() const { + return reinterpret_cast(&_content); + } + VariantSlot* next() { + return _next ? this + _next : 0; + } + const VariantSlot* next() const { + return const_cast(this)->next(); + } + VariantSlot* next(size_t distance) { + VariantSlot* slot = this; + while (distance--) { + if (!slot->_next) + return 0; + slot += slot->_next; + } + return slot; + } + const VariantSlot* next(size_t distance) const { + return const_cast(this)->next(distance); + } + void setNext(VariantSlot* slot) { + ARDUINOJSON_ASSERT(!slot || slot - this >= + numeric_limits::lowest()); + ARDUINOJSON_ASSERT(!slot || slot - this <= + numeric_limits::highest()); + _next = VariantSlotDiff(slot ? slot - this : 0); + } + void setNextNotNull(VariantSlot* slot) { + ARDUINOJSON_ASSERT(slot != 0); + ARDUINOJSON_ASSERT(slot - this >= + numeric_limits::lowest()); + ARDUINOJSON_ASSERT(slot - this <= + numeric_limits::highest()); + _next = VariantSlotDiff(slot - this); + } + void setKey(JsonString k) { + ARDUINOJSON_ASSERT(k); + if (k.isLinked()) + _flags &= VALUE_MASK; + else + _flags |= OWNED_KEY_BIT; + _key = k.c_str(); + } + const char* key() const { + return _key; + } + bool ownsKey() const { + return (_flags & OWNED_KEY_BIT) != 0; + } + void clear() { + _next = 0; + _flags = 0; + _key = 0; + } + void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) { + if (_flags & OWNED_KEY_BIT) + _key += stringDistance; + if (_flags & OWNED_VALUE_BIT) + _content.asString.data += stringDistance; + if (_flags & COLLECTION_MASK) + _content.asCollection.movePointers(stringDistance, variantDistance); + } +}; +} // namespace ARDUINOJSON_NAMESPACE +#define JSON_STRING_SIZE(SIZE) (SIZE + 1) +#define JSON_ARRAY_SIZE(NUMBER_OF_ELEMENTS) \ + ((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::VariantSlot)) +#define JSON_OBJECT_SIZE(NUMBER_OF_ELEMENTS) \ + ((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::VariantSlot)) +namespace ARDUINOJSON_NAMESPACE { +class MemoryPool { + public: + MemoryPool(char* buf, size_t capa) + : _begin(buf), + _left(buf), + _right(buf ? buf + capa : 0), + _end(buf ? buf + capa : 0), + _overflowed(false) { + ARDUINOJSON_ASSERT(isAligned(_begin)); + ARDUINOJSON_ASSERT(isAligned(_right)); + ARDUINOJSON_ASSERT(isAligned(_end)); + } + void* buffer() { + return _begin; // NOLINT(clang-analyzer-unix.Malloc) + } + size_t capacity() const { + return size_t(_end - _begin); + } + size_t size() const { + return size_t(_left - _begin + _end - _right); + } + bool overflowed() const { + return _overflowed; + } + VariantSlot* allocVariant() { + return allocRight(); + } + template + const char* saveString(TAdaptedString str) { + if (str.isNull()) + return 0; +#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION + const char* existingCopy = findString(str); + if (existingCopy) + return existingCopy; +#endif + size_t n = str.size(); + char* newCopy = allocString(n + 1); + if (newCopy) { + stringGetChars(str, newCopy, n); + newCopy[n] = 0; // force null-terminator + } + return newCopy; + } + void getFreeZone(char** zoneStart, size_t* zoneSize) const { + *zoneStart = _left; + *zoneSize = size_t(_right - _left); + } + const char* saveStringFromFreeZone(size_t len) { +#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION + const char* dup = findString(adaptString(_left, len)); + if (dup) + return dup; +#endif + const char* str = _left; + _left += len; + *_left++ = 0; + checkInvariants(); + return str; + } + void markAsOverflowed() { + _overflowed = true; + } + void clear() { + _left = _begin; + _right = _end; + _overflowed = false; + } + bool canAlloc(size_t bytes) const { + return _left + bytes <= _right; + } + bool owns(void* p) const { + return _begin <= p && p < _end; + } + void* operator new(size_t, void* p) { + return p; + } + ptrdiff_t squash() { + char* new_right = addPadding(_left); + if (new_right >= _right) + return 0; + size_t right_size = static_cast(_end - _right); + memmove(new_right, _right, right_size); + ptrdiff_t bytes_reclaimed = _right - new_right; + _right = new_right; + _end = new_right + right_size; + return bytes_reclaimed; + } + void movePointers(ptrdiff_t offset) { + _begin += offset; + _left += offset; + _right += offset; + _end += offset; + } + private: + void checkInvariants() { + ARDUINOJSON_ASSERT(_begin <= _left); + ARDUINOJSON_ASSERT(_left <= _right); + ARDUINOJSON_ASSERT(_right <= _end); + ARDUINOJSON_ASSERT(isAligned(_right)); + } +#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION + template + const char* findString(const TAdaptedString& str) const { + size_t n = str.size(); + for (char* next = _begin; next + n < _left; ++next) { + if (next[n] == '\0' && stringEquals(str, adaptString(next, n))) + return next; + while (*next) + ++next; + } + return 0; + } +#endif + char* allocString(size_t n) { + if (!canAlloc(n)) { + _overflowed = true; + return 0; + } + char* s = _left; + _left += n; + checkInvariants(); + return s; + } + template + T* allocRight() { + return reinterpret_cast(allocRight(sizeof(T))); + } + void* allocRight(size_t bytes) { + if (!canAlloc(bytes)) { + _overflowed = true; + return 0; + } + _right -= bytes; + return _right; + } + char *_begin, *_left, *_right, *_end; + bool _overflowed; +}; +template +bool storeString(MemoryPool* pool, TAdaptedString str, + StringStoragePolicy::Copy, TCallback callback) { + const char* copy = pool->saveString(str); + JsonString storedString(copy, str.size(), JsonString::Copied); + callback(storedString); + return copy != 0; +} +template +bool storeString(MemoryPool*, TAdaptedString str, StringStoragePolicy::Link, + TCallback callback) { + JsonString storedString(str.data(), str.size(), JsonString::Linked); + callback(storedString); + return !str.isNull(); +} +template +bool storeString(MemoryPool* pool, TAdaptedString str, + StringStoragePolicy::LinkOrCopy policy, TCallback callback) { + if (policy.link) + return storeString(pool, str, StringStoragePolicy::Link(), callback); + else + return storeString(pool, str, StringStoragePolicy::Copy(), callback); +} +template +bool storeString(MemoryPool* pool, TAdaptedString str, TCallback callback) { + return storeString(pool, str, str.storagePolicy(), callback); +} +template +struct IsString : false_type {}; +template +struct IsString< + T, typename make_void::AdaptedString>::type> + : true_type {}; +} // namespace ARDUINOJSON_NAMESPACE +#ifdef _MSC_VER // Visual Studio +# define FORCE_INLINE // __forceinline causes C4714 when returning std::string +# define NO_INLINE __declspec(noinline) +#elif defined(__GNUC__) // GCC or Clang +# define FORCE_INLINE __attribute__((always_inline)) +# define NO_INLINE __attribute__((noinline)) +#else // Other compilers +# define FORCE_INLINE +# define NO_INLINE +#endif +#if __cplusplus >= 201103L +# define NOEXCEPT noexcept +#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 +namespace ARDUINOJSON_NAMESPACE { +class JsonArray; +class JsonObject; +class JsonVariant; +template +struct VariantTo {}; +template <> +struct VariantTo { + typedef JsonArray type; +}; +template <> +struct VariantTo { + typedef JsonObject type; +}; +template <> +struct VariantTo { + typedef JsonVariant type; +}; +class VariantAttorney { + template + struct ResultOfGetData { + protected: // <- to avoid GCC's "all member functions in class are private" + static int probe(const VariantData*); + static char probe(VariantData*); + static TClient& client; + public: + typedef typename conditional::type type; + }; + public: + template + FORCE_INLINE static MemoryPool* getPool(TClient& client) { + return client.getPool(); + } + template + FORCE_INLINE static typename ResultOfGetData::type getData( + TClient& client) { + return client.getData(); + } + template + FORCE_INLINE static VariantData* getOrCreateData(TClient& client) { + return client.getOrCreateData(); + } +}; +template +class SerializedValue { + public: + explicit SerializedValue(T str) : _str(str) {} + operator T() const { + return _str; + } + const char* data() const { + return _str.c_str(); + } + size_t size() const { + return _str.length(); + } + private: + T _str; +}; +template +class SerializedValue { + public: + explicit SerializedValue(TChar* p, size_t n) : _data(p), _size(n) {} + operator TChar*() const { + return _data; + } + TChar* data() const { + return _data; + } + size_t size() const { + return _size; + } + private: + TChar* _data; + size_t _size; +}; +template +inline SerializedValue serialized(T str) { + return SerializedValue(str); +} +template +inline SerializedValue serialized(TChar* p) { + return SerializedValue(p, adaptString(p).size()); +} +template +inline SerializedValue serialized(TChar* p, size_t n) { + return SerializedValue(p, n); +} +} // namespace ARDUINOJSON_NAMESPACE +#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 +namespace ARDUINOJSON_NAMESPACE { +#ifndef isnan +template +bool isnan(T x) { + return x != x; +} +#endif +#ifndef isinf +template +bool isinf(T x) { + return x != 0.0 && x * 2 == x; +} +#endif +template +struct alias_cast_t { + union { + F raw; + T data; + }; +}; +template +T alias_cast(F raw_data) { + alias_cast_t ac; + ac.raw = raw_data; + return ac.data; +} +} // namespace ARDUINOJSON_NAMESPACE +#if ARDUINOJSON_ENABLE_PROGMEM +namespace ARDUINOJSON_NAMESPACE { +template +typename enable_if::value, T>::type pgm_read(const void* p) { + return reinterpret_cast(pgm_read_ptr(p)); +} +template +typename enable_if::value, T>::type pgm_read( + const void* p) { + return pgm_read_dword(p); +} +} // namespace ARDUINOJSON_NAMESPACE +# ifndef ARDUINOJSON_DEFINE_PROGMEM_ARRAY +# define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, value) \ + static type const name[] PROGMEM = value; +# endif +# define ARDUINOJSON_DEFINE_STATIC_ARRAY ARDUINOJSON_DEFINE_PROGMEM_ARRAY +# define ARDUINOJSON_READ_STATIC_ARRAY(type, name, index) \ + pgm_read(name + index) +#else // i.e. ARDUINOJSON_ENABLE_PROGMEM == 0 +# define ARDUINOJSON_DEFINE_STATIC_ARRAY(type, name, value) \ + static type const name[] = value; +# define ARDUINOJSON_READ_STATIC_ARRAY(type, name, index) name[index] +#endif +namespace ARDUINOJSON_NAMESPACE { +template +struct FloatTraits {}; +template +struct FloatTraits { + typedef uint64_t mantissa_type; + static const short mantissa_bits = 52; + static const mantissa_type mantissa_max = + (mantissa_type(1) << mantissa_bits) - 1; + typedef int16_t exponent_type; + static const exponent_type exponent_max = 308; + template + static T make_float(T m, TExponent e) { + if (e > 0) { + for (uint8_t index = 0; e != 0; index++) { + if (e & 1) + m *= positiveBinaryPowerOfTen(index); + e >>= 1; + } + } else { + e = TExponent(-e); + for (uint8_t index = 0; e != 0; index++) { + if (e & 1) + m *= negativeBinaryPowerOfTen(index); + e >>= 1; + } + } + return m; + } + static T positiveBinaryPowerOfTen(int index) { + ARDUINOJSON_DEFINE_STATIC_ARRAY( // + uint32_t, factors, + ARDUINOJSON_EXPAND18({ + 0x40240000, 0x00000000, // 1e1 + 0x40590000, 0x00000000, // 1e2 + 0x40C38800, 0x00000000, // 1e4 + 0x4197D784, 0x00000000, // 1e8 + 0x4341C379, 0x37E08000, // 1e16 + 0x4693B8B5, 0xB5056E17, // 1e32 + 0x4D384F03, 0xE93FF9F5, // 1e64 + 0x5A827748, 0xF9301D32, // 1e128 + 0x75154FDD, 0x7F73BF3C // 1e256 + })); + return forge( + ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index), + ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index + 1)); + } + static T negativeBinaryPowerOfTen(int index) { + ARDUINOJSON_DEFINE_STATIC_ARRAY( // + uint32_t, factors, + ARDUINOJSON_EXPAND18({ + 0x3FB99999, 0x9999999A, // 1e-1 + 0x3F847AE1, 0x47AE147B, // 1e-2 + 0x3F1A36E2, 0xEB1C432D, // 1e-4 + 0x3E45798E, 0xE2308C3A, // 1e-8 + 0x3C9CD2B2, 0x97D889BC, // 1e-16 + 0x3949F623, 0xD5A8A733, // 1e-32 + 0x32A50FFD, 0x44F4A73D, // 1e-64 + 0x255BBA08, 0xCF8C979D, // 1e-128 + 0x0AC80628, 0x64AC6F43 // 1e-256 + })); + return forge( + ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index), + ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index + 1)); + } + static T negativeBinaryPowerOfTenPlusOne(int index) { + ARDUINOJSON_DEFINE_STATIC_ARRAY( // + uint32_t, factors, + ARDUINOJSON_EXPAND18({ + 0x3FF00000, 0x00000000, // 1e0 + 0x3FB99999, 0x9999999A, // 1e-1 + 0x3F50624D, 0xD2F1A9FC, // 1e-3 + 0x3E7AD7F2, 0x9ABCAF48, // 1e-7 + 0x3CD203AF, 0x9EE75616, // 1e-15 + 0x398039D6, 0x65896880, // 1e-31 + 0x32DA53FC, 0x9631D10D, // 1e-63 + 0x25915445, 0x81B7DEC2, // 1e-127 + 0x0AFE07B2, 0x7DD78B14 // 1e-255 + })); + return forge( + ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index), + ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index + 1)); + } + static T nan() { + return forge(0x7ff80000, 0x00000000); + } + static T inf() { + return forge(0x7ff00000, 0x00000000); + } + static T highest() { + return forge(0x7FEFFFFF, 0xFFFFFFFF); + } + template // int64_t + static T highest_for( + typename enable_if::value && is_signed::value && + sizeof(TOut) == 8, + signed>::type* = 0) { + return forge(0x43DFFFFF, 0xFFFFFFFF); // 9.2233720368547748e+18 + } + template // uint64_t + static T highest_for( + typename enable_if::value && is_unsigned::value && + sizeof(TOut) == 8, + unsigned>::type* = 0) { + return forge(0x43EFFFFF, 0xFFFFFFFF); // 1.8446744073709549568e+19 + } + static T lowest() { + return forge(0xFFEFFFFF, 0xFFFFFFFF); + } + static T forge(uint32_t msb, uint32_t lsb) { + return alias_cast((uint64_t(msb) << 32) | lsb); + } +}; +template +struct FloatTraits { + typedef uint32_t mantissa_type; + static const short mantissa_bits = 23; + static const mantissa_type mantissa_max = + (mantissa_type(1) << mantissa_bits) - 1; + typedef int8_t exponent_type; + static const exponent_type exponent_max = 38; + template + static T make_float(T m, TExponent e) { + if (e > 0) { + for (uint8_t index = 0; e != 0; index++) { + if (e & 1) + m *= positiveBinaryPowerOfTen(index); + e >>= 1; + } + } else { + e = -e; + for (uint8_t index = 0; e != 0; index++) { + if (e & 1) + m *= negativeBinaryPowerOfTen(index); + e >>= 1; + } + } + return m; + } + static T positiveBinaryPowerOfTen(int index) { + ARDUINOJSON_DEFINE_STATIC_ARRAY(uint32_t, factors, + ARDUINOJSON_EXPAND6({ + 0x41200000, // 1e1f + 0x42c80000, // 1e2f + 0x461c4000, // 1e4f + 0x4cbebc20, // 1e8f + 0x5a0e1bca, // 1e16f + 0x749dc5ae // 1e32f + })); + return forge(ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, index)); + } + static T negativeBinaryPowerOfTen(int index) { + ARDUINOJSON_DEFINE_STATIC_ARRAY(uint32_t, factors, + ARDUINOJSON_EXPAND6({ + 0x3dcccccd, // 1e-1f + 0x3c23d70a, // 1e-2f + 0x38d1b717, // 1e-4f + 0x322bcc77, // 1e-8f + 0x24e69595, // 1e-16f + 0x0a4fb11f // 1e-32f + })); + return forge(ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, index)); + } + static T negativeBinaryPowerOfTenPlusOne(int index) { + ARDUINOJSON_DEFINE_STATIC_ARRAY(uint32_t, factors, + ARDUINOJSON_EXPAND6({ + 0x3f800000, // 1e0f + 0x3dcccccd, // 1e-1f + 0x3a83126f, // 1e-3f + 0x33d6bf95, // 1e-7f + 0x26901d7d, // 1e-15f + 0x0c01ceb3 // 1e-31f + })); + return forge(ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, index)); + } + static T forge(uint32_t bits) { + return alias_cast(bits); + } + static T nan() { + return forge(0x7fc00000); + } + static T inf() { + return forge(0x7f800000); + } + static T highest() { + return forge(0x7f7fffff); + } + template // int32_t + static T highest_for( + typename enable_if::value && is_signed::value && + sizeof(TOut) == 4, + signed>::type* = 0) { + return forge(0x4EFFFFFF); // 2.14748352E9 + } + template // uint32_t + static T highest_for( + typename enable_if::value && is_unsigned::value && + sizeof(TOut) == 4, + unsigned>::type* = 0) { + return forge(0x4F7FFFFF); // 4.29496704E9 + } + template // int64_t + static T highest_for( + typename enable_if::value && is_signed::value && + sizeof(TOut) == 8, + signed>::type* = 0) { + return forge(0x5EFFFFFF); // 9.22337148709896192E18 + } + template // uint64_t + static T highest_for( + typename enable_if::value && is_unsigned::value && + sizeof(TOut) == 8, + unsigned>::type* = 0) { + return forge(0x5F7FFFFF); // 1.844674297419792384E19 + } + static T lowest() { + return forge(0xFf7fffff); + } +}; +template +typename enable_if::value && is_unsigned::value && + is_integral::value && sizeof(TOut) <= sizeof(TIn), + bool>::type +canConvertNumber(TIn value) { + return value <= TIn(numeric_limits::highest()); +} +template +typename enable_if::value && is_unsigned::value && + is_integral::value && sizeof(TIn) < sizeof(TOut), + bool>::type +canConvertNumber(TIn) { + return true; +} +template +typename enable_if::value && is_floating_point::value, + bool>::type +canConvertNumber(TIn) { + return true; +} +template +typename enable_if::value && is_signed::value && + is_integral::value && is_signed::value && + sizeof(TOut) < sizeof(TIn), + bool>::type +canConvertNumber(TIn value) { + return value >= TIn(numeric_limits::lowest()) && + value <= TIn(numeric_limits::highest()); +} +template +typename enable_if::value && is_signed::value && + is_integral::value && is_signed::value && + sizeof(TIn) <= sizeof(TOut), + bool>::type +canConvertNumber(TIn) { + return true; +} +template +typename enable_if::value && is_signed::value && + is_integral::value && is_unsigned::value && + sizeof(TOut) >= sizeof(TIn), + bool>::type +canConvertNumber(TIn value) { + if (value < 0) + return false; + return TOut(value) <= numeric_limits::highest(); +} +template +typename enable_if::value && is_signed::value && + is_integral::value && is_unsigned::value && + sizeof(TOut) < sizeof(TIn), + bool>::type +canConvertNumber(TIn value) { + if (value < 0) + return false; + return value <= TIn(numeric_limits::highest()); +} +template +typename enable_if::value && is_integral::value && + sizeof(TOut) < sizeof(TIn), + bool>::type +canConvertNumber(TIn value) { + return value >= numeric_limits::lowest() && + value <= numeric_limits::highest(); +} +template +typename enable_if::value && is_integral::value && + sizeof(TOut) >= sizeof(TIn), + bool>::type +canConvertNumber(TIn value) { + return value >= numeric_limits::lowest() && + value <= FloatTraits::template highest_for(); +} +template +TOut convertNumber(TIn value) { + return canConvertNumber(value) ? 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 +#if defined(__GNUC__) +# if __GNUC__ >= 7 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +# pragma GCC diagnostic ignored "-Wuninitialized" +# endif +#endif +namespace ARDUINOJSON_NAMESPACE { +class VariantData { + VariantContent _content; // must be first to allow cast from array to variant + uint8_t _flags; + public: + void init() { + _flags = VALUE_IS_NULL; + } + void operator=(const VariantData& src) { + _content = src._content; + _flags = uint8_t((_flags & OWNED_KEY_BIT) | (src._flags & ~OWNED_KEY_BIT)); + } + template + typename TVisitor::result_type accept(TVisitor& visitor) const { + switch (type()) { + case VALUE_IS_FLOAT: + return visitor.visitFloat(_content.asFloat); + case VALUE_IS_ARRAY: + return visitor.visitArray(_content.asCollection); + case VALUE_IS_OBJECT: + return visitor.visitObject(_content.asCollection); + case VALUE_IS_LINKED_STRING: + case VALUE_IS_OWNED_STRING: + return visitor.visitString(_content.asString.data, + _content.asString.size); + case VALUE_IS_OWNED_RAW: + case VALUE_IS_LINKED_RAW: + return visitor.visitRawJson(_content.asString.data, + _content.asString.size); + case VALUE_IS_SIGNED_INTEGER: + return visitor.visitSignedInteger(_content.asSignedInteger); + case VALUE_IS_UNSIGNED_INTEGER: + return visitor.visitUnsignedInteger(_content.asUnsignedInteger); + case VALUE_IS_BOOLEAN: + return visitor.visitBoolean(_content.asBoolean != 0); + default: + return visitor.visitNull(); + } + } + template + T asIntegral() const; + template + T asFloat() const; + JsonString asString() const; + bool asBoolean() const; + CollectionData* asArray() { + return isArray() ? &_content.asCollection : 0; + } + const CollectionData* asArray() const { + return const_cast(this)->asArray(); + } + const CollectionData* asCollection() const { + return isCollection() ? &_content.asCollection : 0; + } + CollectionData* asObject() { + return isObject() ? &_content.asCollection : 0; + } + const CollectionData* asObject() const { + return const_cast(this)->asObject(); + } + bool copyFrom(const VariantData& src, MemoryPool* pool); + bool isArray() const { + return (_flags & VALUE_IS_ARRAY) != 0; + } + bool isBoolean() const { + return type() == VALUE_IS_BOOLEAN; + } + bool isCollection() const { + return (_flags & COLLECTION_MASK) != 0; + } + template + bool isInteger() const { + switch (type()) { + case VALUE_IS_UNSIGNED_INTEGER: + return canConvertNumber(_content.asUnsignedInteger); + case VALUE_IS_SIGNED_INTEGER: + return canConvertNumber(_content.asSignedInteger); + default: + return false; + } + } + bool isFloat() const { + return (_flags & NUMBER_BIT) != 0; + } + bool isString() const { + return type() == VALUE_IS_LINKED_STRING || type() == VALUE_IS_OWNED_STRING; + } + bool isObject() const { + return (_flags & VALUE_IS_OBJECT) != 0; + } + bool isNull() const { + return type() == VALUE_IS_NULL; + } + bool isEnclosed() const { + return !isFloat(); + } + void remove(size_t index) { + if (isArray()) + _content.asCollection.removeElement(index); + } + template + void remove(TAdaptedString key) { + if (isObject()) + _content.asCollection.removeMember(key); + } + void setBoolean(bool value) { + setType(VALUE_IS_BOOLEAN); + _content.asBoolean = value; + } + void setFloat(JsonFloat value) { + setType(VALUE_IS_FLOAT); + _content.asFloat = value; + } + void setLinkedRaw(SerializedValue value) { + if (value.data()) { + setType(VALUE_IS_LINKED_RAW); + _content.asString.data = value.data(); + _content.asString.size = value.size(); + } else { + setType(VALUE_IS_NULL); + } + } + template + bool storeOwnedRaw(SerializedValue value, MemoryPool* pool) { + const char* dup = pool->saveString(adaptString(value.data(), value.size())); + if (dup) { + setType(VALUE_IS_OWNED_RAW); + _content.asString.data = dup; + _content.asString.size = value.size(); + return true; + } else { + setType(VALUE_IS_NULL); + return false; + } + } + template + typename enable_if::value>::type setInteger(T value) { + setType(VALUE_IS_UNSIGNED_INTEGER); + _content.asUnsignedInteger = static_cast(value); + } + template + typename enable_if::value>::type setInteger(T value) { + setType(VALUE_IS_SIGNED_INTEGER); + _content.asSignedInteger = value; + } + void setNull() { + setType(VALUE_IS_NULL); + } + void setString(JsonString s) { + ARDUINOJSON_ASSERT(s); + if (s.isLinked()) + setType(VALUE_IS_LINKED_STRING); + else + setType(VALUE_IS_OWNED_STRING); + _content.asString.data = s.c_str(); + _content.asString.size = s.size(); + } + CollectionData& toArray() { + setType(VALUE_IS_ARRAY); + _content.asCollection.clear(); + return _content.asCollection; + } + CollectionData& toObject() { + setType(VALUE_IS_OBJECT); + _content.asCollection.clear(); + return _content.asCollection; + } + size_t memoryUsage() const { + switch (type()) { + case VALUE_IS_OWNED_STRING: + case VALUE_IS_OWNED_RAW: + return _content.asString.size + 1; + case VALUE_IS_OBJECT: + case VALUE_IS_ARRAY: + return _content.asCollection.memoryUsage(); + default: + return 0; + } + } + size_t size() const { + return isCollection() ? _content.asCollection.size() : 0; + } + VariantData* addElement(MemoryPool* pool) { + if (isNull()) + toArray(); + if (!isArray()) + return 0; + return _content.asCollection.addElement(pool); + } + VariantData* getElement(size_t index) const { + const CollectionData* col = asArray(); + return col ? col->getElement(index) : 0; + } + VariantData* getOrAddElement(size_t index, MemoryPool* pool) { + if (isNull()) + toArray(); + if (!isArray()) + return 0; + return _content.asCollection.getOrAddElement(index, pool); + } + template + VariantData* getMember(TAdaptedString key) const { + const CollectionData* col = asObject(); + return col ? col->getMember(key) : 0; + } + template + VariantData* getOrAddMember(TAdaptedString key, MemoryPool* pool) { + if (isNull()) + toObject(); + if (!isObject()) + return 0; + return _content.asCollection.getOrAddMember(key, pool); + } + void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) { + if (_flags & OWNED_VALUE_BIT) + _content.asString.data += stringDistance; + if (_flags & COLLECTION_MASK) + _content.asCollection.movePointers(stringDistance, variantDistance); + } + uint8_t type() const { + return _flags & VALUE_MASK; + } + template + inline bool setString(TAdaptedString value, MemoryPool* pool) { + if (value.isNull()) { + setNull(); + return true; + } + return storeString(pool, value, VariantStringSetter(this)); + } + private: + void setType(uint8_t t) { + _flags &= OWNED_KEY_BIT; + _flags |= t; + } + struct VariantStringSetter { + VariantStringSetter(VariantData* instance) : _instance(instance) {} + template + void operator()(TStoredString s) { + if (s) + _instance->setString(s); + else + _instance->setNull(); + } + VariantData* _instance; + }; +}; +} // namespace ARDUINOJSON_NAMESPACE +#if defined(__GNUC__) +# if __GNUC__ >= 8 +# pragma GCC diagnostic pop +# endif +#endif +namespace ARDUINOJSON_NAMESPACE { +template +struct Visitor { + typedef TResult result_type; + TResult visitArray(const CollectionData&) { + return TResult(); + } + TResult visitBoolean(bool) { + return TResult(); + } + TResult visitFloat(JsonFloat) { + return TResult(); + } + TResult visitSignedInteger(JsonInteger) { + return TResult(); + } + TResult visitNull() { + return TResult(); + } + TResult visitObject(const CollectionData&) { + return TResult(); + } + TResult visitUnsignedInteger(JsonUInt) { + return TResult(); + } + TResult visitRawJson(const char*, size_t) { + return TResult(); + } + TResult visitString(const char*, size_t) { + return TResult(); + } +}; +template +inline typename TVisitor::result_type variantAccept(const VariantData* var, + TVisitor& visitor) { + if (var != 0) + return var->accept(visitor); + else + return visitor.visitNull(); +} +inline bool variantCopyFrom(VariantData* dst, const VariantData* src, + MemoryPool* pool) { + if (!dst) + return false; + if (!src) { + dst->setNull(); + return true; + } + return dst->copyFrom(*src, pool); +} +inline void variantSetNull(VariantData* var) { + if (!var) + return; + var->setNull(); +} +template +inline bool variantSetString(VariantData* var, TAdaptedString value, + MemoryPool* pool) { + return var != 0 ? var->setString(value, pool) : 0; +} +inline size_t variantSize(const VariantData* var) { + return var != 0 ? var->size() : 0; +} +inline CollectionData* variantToArray(VariantData* var) { + if (!var) + return 0; + return &var->toArray(); +} +inline CollectionData* variantToObject(VariantData* var) { + if (!var) + return 0; + return &var->toObject(); +} +inline VariantData* variantGetElement(const VariantData* var, size_t index) { + return var != 0 ? var->getElement(index) : 0; +} +inline NO_INLINE VariantData* variantAddElement(VariantData* var, + MemoryPool* pool) { + return var != 0 ? var->addElement(pool) : 0; +} +inline NO_INLINE VariantData* variantGetOrAddElement(VariantData* var, + size_t index, + MemoryPool* pool) { + return var != 0 ? var->getOrAddElement(index, pool) : 0; +} +template +VariantData* variantGetMember(const VariantData* var, TAdaptedString key) { + if (!var) + return 0; + return var->getMember(key); +} +template +VariantData* variantGetOrAddMember(VariantData* var, TAdaptedString key, + MemoryPool* pool) { + if (!var) + return 0; + return var->getOrAddMember(key, pool); +} +inline bool variantIsNull(const VariantData* var) { + return var == 0 || var->isNull(); +} +inline size_t variantNesting(const VariantData* var) { + if (!var) + return 0; + const CollectionData* collection = var->asCollection(); + if (!collection) + return 0; + size_t maxChildNesting = 0; + for (const VariantSlot* s = collection->head(); s; s = s->next()) { + size_t childNesting = variantNesting(s->data()); + if (childNesting > maxChildNesting) + maxChildNesting = childNesting; + } + return maxChildNesting + 1; +} +enum CompareResult { + COMPARE_RESULT_DIFFER = 0, + COMPARE_RESULT_EQUAL = 1, + COMPARE_RESULT_GREATER = 2, + COMPARE_RESULT_LESS = 4, + COMPARE_RESULT_GREATER_OR_EQUAL = 3, + COMPARE_RESULT_LESS_OR_EQUAL = 5 +}; +template +CompareResult arithmeticCompare(const T& lhs, const T& rhs) { + if (lhs < rhs) + return COMPARE_RESULT_LESS; + else if (lhs > rhs) + return COMPARE_RESULT_GREATER; + else + return COMPARE_RESULT_EQUAL; +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value && is_integral::value && + sizeof(T1) < sizeof(T2), + int // Using int instead of void to avoid C2572 on + >::type* = 0) { + return arithmeticCompare(static_cast(lhs), rhs); +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value && is_integral::value && + sizeof(T2) < sizeof(T1)>::type* = 0) { + return arithmeticCompare(lhs, static_cast(rhs)); +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value && is_integral::value && + is_signed::value == is_signed::value && + sizeof(T2) == sizeof(T1)>::type* = 0) { + return arithmeticCompare(lhs, static_cast(rhs)); +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value && is_integral::value && + is_unsigned::value && is_signed::value && + sizeof(T2) == sizeof(T1)>::type* = 0) { + if (rhs < 0) + return COMPARE_RESULT_GREATER; + return arithmeticCompare(lhs, static_cast(rhs)); +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value && is_integral::value && + is_signed::value && is_unsigned::value && + sizeof(T2) == sizeof(T1)>::type* = 0) { + if (lhs < 0) + return COMPARE_RESULT_LESS; + return arithmeticCompare(static_cast(lhs), rhs); +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value || + is_floating_point::value>::type* = 0) { + return arithmeticCompare(static_cast(lhs), + static_cast(rhs)); +} +template +CompareResult arithmeticCompareNegateLeft( + JsonUInt, const T2&, + typename enable_if::value>::type* = 0) { + return COMPARE_RESULT_LESS; +} +template +CompareResult arithmeticCompareNegateLeft( + JsonUInt lhs, const T2& rhs, + typename enable_if::value>::type* = 0) { + if (rhs > 0) + return COMPARE_RESULT_LESS; + return arithmeticCompare(-rhs, static_cast(lhs)); +} +template +CompareResult arithmeticCompareNegateRight( + const T1&, JsonUInt, + typename enable_if::value>::type* = 0) { + return COMPARE_RESULT_GREATER; +} +template +CompareResult arithmeticCompareNegateRight( + const T1& lhs, JsonUInt rhs, + typename enable_if::value>::type* = 0) { + if (lhs > 0) + return COMPARE_RESULT_GREATER; + return arithmeticCompare(static_cast(rhs), -lhs); +} +struct VariantTag {}; +template +struct IsVariant : is_base_of {}; +class JsonVariantConst; +template +CompareResult compare(JsonVariantConst lhs, + const T& rhs); // VariantCompare.cpp +struct VariantOperatorTag {}; +template +struct VariantOperators : VariantOperatorTag { + template + friend + typename enable_if::value && !is_array::value, T>::type + operator|(const TVariant& variant, const T& defaultValue) { + if (variant.template is()) + return variant.template as(); + else + return defaultValue; + } + friend const char* operator|(const TVariant& variant, + const char* defaultValue) { + if (variant.template is()) + return variant.template as(); + else + return defaultValue; + } + template + friend typename enable_if::value, JsonVariantConst>::type + operator|(const TVariant& variant, T defaultValue) { + if (variant) + return variant; + else + return defaultValue; + } + template + friend bool operator==(T* lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; + } + template + friend bool operator==(const T& lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; + } + template + friend bool operator==(TVariant lhs, T* rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; + } + template + friend + typename enable_if::value, bool>::type + operator==(TVariant lhs, const T& rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; + } + template + friend bool operator!=(T* lhs, TVariant rhs) { + return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; + } + template + friend bool operator!=(const T& lhs, TVariant rhs) { + return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; + } + template + friend bool operator!=(TVariant lhs, T* rhs) { + return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; + } + template + friend + typename enable_if::value, bool>::type + operator!=(TVariant lhs, const T& rhs) { + return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; + } + template + friend bool operator<(T* lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_GREATER; + } + template + friend bool operator<(const T& lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_GREATER; + } + template + friend bool operator<(TVariant lhs, T* rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_LESS; + } + template + friend + typename enable_if::value, bool>::type + operator<(TVariant lhs, const T& rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_LESS; + } + template + friend bool operator<=(T* lhs, TVariant rhs) { + return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } + template + friend bool operator<=(const T& lhs, TVariant rhs) { + return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } + template + friend bool operator<=(TVariant lhs, T* rhs) { + return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend + typename enable_if::value, bool>::type + operator<=(TVariant lhs, const T& rhs) { + return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend bool operator>(T* lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_LESS; + } + template + friend bool operator>(const T& lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_LESS; + } + template + friend bool operator>(TVariant lhs, T* rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_GREATER; + } + template + friend + typename enable_if::value, bool>::type + operator>(TVariant lhs, const T& rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_GREATER; + } + template + friend bool operator>=(T* lhs, TVariant rhs) { + return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend bool operator>=(const T& lhs, TVariant rhs) { + return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend bool operator>=(TVariant lhs, T* rhs) { + return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } + template + friend + typename enable_if::value, bool>::type + operator>=(TVariant lhs, const T& rhs) { + return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } +}; +class JsonArray; +class JsonObject; +class JsonVariantConst : public VariantTag, + public VariantOperators { + friend class VariantAttorney; + public: + JsonVariantConst() : _data(0) {} + explicit JsonVariantConst(const VariantData* data) : _data(data) {} + FORCE_INLINE bool isNull() const { + return variantIsNull(_data); + } + FORCE_INLINE bool isUnbound() const { + return !_data; + } + FORCE_INLINE size_t memoryUsage() const { + return _data ? _data->memoryUsage() : 0; + } + FORCE_INLINE size_t nesting() const { + return variantNesting(_data); + } + size_t size() const { + return variantSize(_data); + } + template + FORCE_INLINE + typename enable_if::value && !is_same::value, + T>::type + as() const { + return Converter::fromJson(*this); + } + template + FORCE_INLINE + typename enable_if::value && !is_same::value, + bool>::type + is() const { + return Converter::checkJson(*this); + } + template + FORCE_INLINE operator T() const { + return as(); + } + FORCE_INLINE JsonVariantConst operator[](size_t index) const { + return JsonVariantConst(variantGetElement(_data, index)); + } + template + FORCE_INLINE + typename enable_if::value, JsonVariantConst>::type + operator[](const TString& key) const { + return JsonVariantConst(variantGetMember(_data, adaptString(key))); + } + template + FORCE_INLINE + typename enable_if::value, JsonVariantConst>::type + operator[](TChar* key) const { + return JsonVariantConst(variantGetMember(_data, adaptString(key))); + } + template + FORCE_INLINE typename enable_if::value, bool>::type + containsKey(const TString& key) const { + return variantGetMember(getData(), adaptString(key)) != 0; + } + template + FORCE_INLINE typename enable_if::value, bool>::type + containsKey(TChar* key) const { + return variantGetMember(getData(), adaptString(key)) != 0; + } + protected: + const VariantData* getData() const { + return _data; + } + private: + const VariantData* _data; +}; +class JsonVariant; +template +class ElementProxy; +template +class MemberProxy; +template +class VariantRefBase : public VariantTag { + friend class VariantAttorney; + public: + FORCE_INLINE void clear() const { + variantSetNull(getData()); + } + FORCE_INLINE bool isNull() const { + return variantIsNull(getData()); + } + FORCE_INLINE bool isUnbound() const { + return !getData(); + } + template + FORCE_INLINE typename enable_if::value && + !is_same::value && + !ConverterNeedsWriteableRef::value, + T>::type + as() const { + return Converter::fromJson(getVariantConst()); + } + template + FORCE_INLINE typename enable_if::value, T>::type + as() const { + return Converter::fromJson(getVariant()); + } + template + FORCE_INLINE operator T() const { + return as(); + } + template + typename enable_if::value, JsonArray>::type to() const; + template + typename enable_if::value, JsonObject>::type to() + const; + template + typename enable_if::value, JsonVariant>::type to() + const; + template + FORCE_INLINE + typename enable_if::value, bool>::type + is() const { + return Converter::checkJson(getVariant()); + } + template + FORCE_INLINE typename enable_if::value && + !is_same::value && + !is_same::value, + bool>::type + is() const { + return Converter::checkJson(getVariantConst()); + } + FORCE_INLINE void shallowCopy(JsonVariantConst target) { + VariantData* data = getOrCreateData(); + if (!data) + return; + const VariantData* targetData = VariantAttorney::getData(target); + if (targetData) + *data = *targetData; + else + data->setNull(); + } + template + FORCE_INLINE bool set(const T& value) const { + Converter::toJson(value, getOrCreateVariant()); + MemoryPool* pool = getPool(); + return pool && !pool->overflowed(); + } + template + FORCE_INLINE bool set(T* value) const { + Converter::toJson(value, getOrCreateVariant()); + MemoryPool* pool = getPool(); + return pool && !pool->overflowed(); + } + FORCE_INLINE size_t size() const { + return variantSize(getData()); + } + FORCE_INLINE size_t memoryUsage() const { + VariantData* data = getData(); + return data ? data->memoryUsage() : 0; + } + FORCE_INLINE size_t nesting() const { + return variantNesting(getData()); + } + FORCE_INLINE JsonVariant add() const; + template + FORCE_INLINE bool add(const T& value) const { + return add().set(value); + } + template + FORCE_INLINE bool add(T* value) const { + return add().set(value); + } + FORCE_INLINE void remove(size_t index) const { + VariantData* data = getData(); + if (data) + data->remove(index); + } + template + FORCE_INLINE typename enable_if::value>::type remove( + TChar* key) const { + VariantData* data = getData(); + if (data) + data->remove(adaptString(key)); + } + template + FORCE_INLINE typename enable_if::value>::type remove( + const TString& key) const { + VariantData* data = getData(); + if (data) + data->remove(adaptString(key)); + } + FORCE_INLINE JsonArray createNestedArray() const; + FORCE_INLINE JsonObject createNestedObject() const; + FORCE_INLINE ElementProxy operator[](size_t index) const; + template + FORCE_INLINE typename enable_if::value, bool>::type + containsKey(const TString& key) const; + template + FORCE_INLINE typename enable_if::value, bool>::type + containsKey(TChar* key) const; + template + FORCE_INLINE typename enable_if::value, + MemberProxy >::type + operator[](const TString& key) const; + template + FORCE_INLINE typename enable_if::value, + MemberProxy >::type + operator[](TChar* key) const; + template + FORCE_INLINE JsonArray createNestedArray(const TString& key) const; + template + FORCE_INLINE JsonArray createNestedArray(TChar* key) const; + template + JsonObject createNestedObject(const TString& key) const; + template + JsonObject createNestedObject(TChar* key) const; + private: + TDerived& derived() { + return static_cast(*this); + } + const TDerived& derived() const { + return static_cast(*this); + } + FORCE_INLINE MemoryPool* getPool() const { + return VariantAttorney::getPool(derived()); + } + FORCE_INLINE VariantData* getData() const { + return VariantAttorney::getData(derived()); + } + FORCE_INLINE VariantData* getOrCreateData() const { + return VariantAttorney::getOrCreateData(derived()); + } + private: + FORCE_INLINE JsonVariant getVariant() const; + FORCE_INLINE JsonVariantConst getVariantConst() const { + return JsonVariantConst(getData()); + } + FORCE_INLINE JsonVariant getOrCreateVariant() const; +}; +template +class ElementProxy : public VariantRefBase >, + public VariantOperators > { + friend class VariantAttorney; + public: + ElementProxy(TUpstream upstream, size_t index) + : _upstream(upstream), _index(index) {} + ElementProxy(const ElementProxy& src) + : _upstream(src._upstream), _index(src._index) {} + FORCE_INLINE ElementProxy& operator=(const ElementProxy& src) { + this->set(src); + return *this; + } + template + FORCE_INLINE ElementProxy& operator=(const T& src) { + this->set(src); + return *this; + } + template + FORCE_INLINE ElementProxy& operator=(T* src) { + this->set(src); + return *this; + } + private: + FORCE_INLINE MemoryPool* getPool() const { + return VariantAttorney::getPool(_upstream); + } + FORCE_INLINE VariantData* getData() const { + return variantGetElement(VariantAttorney::getData(_upstream), _index); + } + FORCE_INLINE VariantData* getOrCreateData() const { + return variantGetOrAddElement(VariantAttorney::getOrCreateData(_upstream), + _index, VariantAttorney::getPool(_upstream)); + } + TUpstream _upstream; + size_t _index; +}; +class JsonVariant : public VariantRefBase, + public VariantOperators { + friend class VariantAttorney; + public: + JsonVariant() : _data(0), _pool(0) {} + JsonVariant(MemoryPool* pool, VariantData* data) : _data(data), _pool(pool) {} + private: + FORCE_INLINE MemoryPool* getPool() const { + return _pool; + } + FORCE_INLINE VariantData* getData() const { + return _data; + } + FORCE_INLINE VariantData* getOrCreateData() const { + return _data; + } + VariantData* _data; + MemoryPool* _pool; +}; +template <> +struct Converter : private VariantAttorney { + static void toJson(JsonVariant src, JsonVariant dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); + } + static JsonVariant fromJson(JsonVariant src) { + return src; + } + static InvalidConversion fromJson( + JsonVariantConst); + static bool checkJson(JsonVariant src) { + VariantData* data = getData(src); + return !!data; + } + static bool checkJson(JsonVariantConst) { + return false; + } +}; +template <> +struct Converter : private VariantAttorney { + static void toJson(JsonVariantConst src, JsonVariant dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); + } + static JsonVariantConst fromJson(JsonVariantConst src) { + return JsonVariantConst(getData(src)); + } + static bool checkJson(JsonVariantConst src) { + const VariantData* data = getData(src); + return !!data; + } +}; +struct SlotKeySetter { + SlotKeySetter(VariantSlot* instance) : _instance(instance) {} + template + void operator()(TStoredString s) { + if (!s) + return; + ARDUINOJSON_ASSERT(_instance != 0); + _instance->setKey(s); + } + VariantSlot* _instance; +}; +template +inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool* pool) { + if (!var) + return false; + return storeString(pool, key, SlotKeySetter(var)); +} +inline size_t slotSize(const VariantSlot* var) { + size_t n = 0; + while (var) { + n++; + var = var->next(); + } + return n; +} +inline VariantData* slotData(VariantSlot* slot) { + return reinterpret_cast(slot); +} +class VariantPtr { + public: + VariantPtr(MemoryPool* pool, VariantData* data) : _variant(pool, data) {} + JsonVariant* operator->() { + return &_variant; + } + JsonVariant& operator*() { + return _variant; + } + private: + JsonVariant _variant; +}; +class JsonArrayIterator { + friend class JsonArray; + public: + JsonArrayIterator() : _slot(0) {} + explicit JsonArrayIterator(MemoryPool* pool, VariantSlot* slot) + : _pool(pool), _slot(slot) {} + JsonVariant operator*() const { + return JsonVariant(_pool, _slot->data()); + } + VariantPtr operator->() { + return VariantPtr(_pool, _slot->data()); + } + bool operator==(const JsonArrayIterator& other) const { + return _slot == other._slot; + } + bool operator!=(const JsonArrayIterator& other) const { + return _slot != other._slot; + } + JsonArrayIterator& operator++() { + _slot = _slot->next(); + return *this; + } + JsonArrayIterator& operator+=(size_t distance) { + _slot = _slot->next(distance); + return *this; + } + private: + MemoryPool* _pool; + VariantSlot* _slot; +}; +class VariantConstPtr { + public: + VariantConstPtr(const VariantData* data) : _variant(data) {} + JsonVariantConst* operator->() { + return &_variant; + } + JsonVariantConst& operator*() { + return _variant; + } + private: + JsonVariantConst _variant; +}; +class JsonArrayConstIterator { + friend class JsonArray; + public: + JsonArrayConstIterator() : _slot(0) {} + explicit JsonArrayConstIterator(const VariantSlot* slot) : _slot(slot) {} + JsonVariantConst operator*() const { + return JsonVariantConst(_slot->data()); + } + VariantConstPtr operator->() { + return VariantConstPtr(_slot->data()); + } + bool operator==(const JsonArrayConstIterator& other) const { + return _slot == other._slot; + } + bool operator!=(const JsonArrayConstIterator& other) const { + return _slot != other._slot; + } + JsonArrayConstIterator& operator++() { + _slot = _slot->next(); + return *this; + } + JsonArrayConstIterator& operator+=(size_t distance) { + _slot = _slot->next(distance); + return *this; + } + private: + const VariantSlot* _slot; +}; +class JsonObject; +class JsonArrayConst : public VariantOperators { + friend class JsonArray; + friend class VariantAttorney; + public: + typedef JsonArrayConstIterator iterator; + FORCE_INLINE iterator begin() const { + if (!_data) + return iterator(); + return iterator(_data->head()); + } + FORCE_INLINE iterator end() const { + return iterator(); + } + FORCE_INLINE JsonArrayConst() : _data(0) {} + FORCE_INLINE JsonArrayConst(const CollectionData* data) : _data(data) {} + FORCE_INLINE bool operator==(JsonArrayConst rhs) const { + if (_data == rhs._data) + return true; + if (!_data || !rhs._data) + return false; + iterator it1 = begin(); + iterator it2 = rhs.begin(); + for (;;) { + bool end1 = it1 == end(); + bool end2 = it2 == rhs.end(); + if (end1 && end2) + return true; + if (end1 || end2) + return false; + if (*it1 != *it2) + return false; + ++it1; + ++it2; + } + } + FORCE_INLINE JsonVariantConst operator[](size_t index) const { + return JsonVariantConst(_data ? _data->getElement(index) : 0); + } + operator JsonVariantConst() const { + return JsonVariantConst(collectionToVariant(_data)); + } + FORCE_INLINE bool isNull() const { + return _data == 0; + } + FORCE_INLINE operator bool() const { + return _data != 0; + } + FORCE_INLINE size_t memoryUsage() const { + return _data ? _data->memoryUsage() : 0; + } + FORCE_INLINE size_t nesting() const { + return variantNesting(collectionToVariant(_data)); + } + FORCE_INLINE size_t size() const { + return _data ? _data->size() : 0; + } + private: + const VariantData* getData() const { + return collectionToVariant(_data); + } + const CollectionData* _data; +}; +template <> +struct Converter : private VariantAttorney { + static void toJson(JsonVariantConst src, JsonVariant dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); + } + static JsonArrayConst fromJson(JsonVariantConst src) { + const VariantData* data = getData(src); + return data ? data->asArray() : 0; + } + static bool checkJson(JsonVariantConst src) { + const VariantData* data = getData(src); + return data && data->isArray(); + } +}; +class JsonObject; +class JsonArray : public VariantOperators { + friend class VariantAttorney; + public: + typedef JsonArrayIterator iterator; + FORCE_INLINE JsonArray() : _data(0), _pool(0) {} + FORCE_INLINE JsonArray(MemoryPool* pool, CollectionData* data) + : _data(data), _pool(pool) {} + operator JsonVariant() { + void* data = _data; // prevent warning cast-align + return JsonVariant(_pool, reinterpret_cast(data)); + } + operator JsonArrayConst() const { + return JsonArrayConst(_data); + } + JsonVariant add() const { + if (!_data) + return JsonVariant(); + return JsonVariant(_pool, _data->addElement(_pool)); + } + template + FORCE_INLINE bool add(const T& value) const { + return add().set(value); + } + template + FORCE_INLINE bool add(T* value) const { + return add().set(value); + } + FORCE_INLINE iterator begin() const { + if (!_data) + return iterator(); + return iterator(_pool, _data->head()); + } + FORCE_INLINE iterator end() const { + return iterator(); + } + FORCE_INLINE bool set(JsonArrayConst src) const { + if (!_data || !src._data) + return false; + return _data->copyFrom(*src._data, _pool); + } + FORCE_INLINE bool operator==(JsonArray rhs) const { + return JsonArrayConst(_data) == JsonArrayConst(rhs._data); + } + FORCE_INLINE void remove(iterator it) const { + if (!_data) + return; + _data->removeSlot(it._slot); + } + FORCE_INLINE void remove(size_t index) const { + if (!_data) + return; + _data->removeElement(index); + } + void clear() const { + if (!_data) + return; + _data->clear(); + } + FORCE_INLINE ElementProxy operator[](size_t index) const { + return ElementProxy(*this, index); + } + FORCE_INLINE JsonObject createNestedObject() const; + FORCE_INLINE JsonArray createNestedArray() const { + return add().to(); + } + operator JsonVariantConst() const { + return JsonVariantConst(collectionToVariant(_data)); + } + FORCE_INLINE bool isNull() const { + return _data == 0; + } + FORCE_INLINE operator bool() const { + return _data != 0; + } + FORCE_INLINE size_t memoryUsage() const { + return _data ? _data->memoryUsage() : 0; + } + FORCE_INLINE size_t nesting() const { + return variantNesting(collectionToVariant(_data)); + } + FORCE_INLINE size_t size() const { + return _data ? _data->size() : 0; + } + private: + MemoryPool* getPool() const { + return _pool; + } + VariantData* getData() const { + return collectionToVariant(_data); + } + VariantData* getOrCreateData() const { + return collectionToVariant(_data); + } + CollectionData* _data; + MemoryPool* _pool; +}; +template <> +struct Converter : private VariantAttorney { + static void toJson(JsonVariantConst src, JsonVariant dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); + } + static JsonArray fromJson(JsonVariant src) { + VariantData* data = getData(src); + MemoryPool* pool = getPool(src); + return JsonArray(pool, data != 0 ? data->asArray() : 0); + } + static InvalidConversion fromJson( + JsonVariantConst); + static bool checkJson(JsonVariantConst) { + return false; + } + static bool checkJson(JsonVariant src) { + VariantData* data = getData(src); + return data && data->isArray(); + } +}; +class JsonPair { + public: + JsonPair(MemoryPool* pool, VariantSlot* slot) { + if (slot) { + _key = JsonString(slot->key(), slot->ownsKey() ? JsonString::Copied + : JsonString::Linked); + _value = JsonVariant(pool, slot->data()); + } + } + JsonString key() const { + return _key; + } + JsonVariant value() const { + return _value; + } + private: + JsonString _key; + JsonVariant _value; +}; +class JsonPairConst { + public: + JsonPairConst(const VariantSlot* slot) { + if (slot) { + _key = JsonString(slot->key(), slot->ownsKey() ? JsonString::Copied + : JsonString::Linked); + _value = JsonVariantConst(slot->data()); + } + } + JsonString key() const { + return _key; + } + JsonVariantConst value() const { + return _value; + } + private: + JsonString _key; + JsonVariantConst _value; +}; +class JsonPairPtr { + public: + JsonPairPtr(MemoryPool* pool, VariantSlot* slot) : _pair(pool, slot) {} + const JsonPair* operator->() const { + return &_pair; + } + const JsonPair& operator*() const { + return _pair; + } + private: + JsonPair _pair; +}; +class JsonObjectIterator { + friend class JsonObject; + public: + JsonObjectIterator() : _slot(0) {} + explicit JsonObjectIterator(MemoryPool* pool, VariantSlot* slot) + : _pool(pool), _slot(slot) {} + JsonPair operator*() const { + return JsonPair(_pool, _slot); + } + JsonPairPtr operator->() { + return JsonPairPtr(_pool, _slot); + } + bool operator==(const JsonObjectIterator& other) const { + return _slot == other._slot; + } + bool operator!=(const JsonObjectIterator& other) const { + return _slot != other._slot; + } + JsonObjectIterator& operator++() { + _slot = _slot->next(); + return *this; + } + JsonObjectIterator& operator+=(size_t distance) { + _slot = _slot->next(distance); + return *this; + } + private: + MemoryPool* _pool; + VariantSlot* _slot; +}; +class JsonPairConstPtr { + public: + JsonPairConstPtr(const VariantSlot* slot) : _pair(slot) {} + const JsonPairConst* operator->() const { + return &_pair; + } + const JsonPairConst& operator*() const { + return _pair; + } + private: + JsonPairConst _pair; +}; +class JsonObjectConstIterator { + friend class JsonObject; + public: + JsonObjectConstIterator() : _slot(0) {} + explicit JsonObjectConstIterator(const VariantSlot* slot) : _slot(slot) {} + JsonPairConst operator*() const { + return JsonPairConst(_slot); + } + JsonPairConstPtr operator->() { + return JsonPairConstPtr(_slot); + } + bool operator==(const JsonObjectConstIterator& other) const { + return _slot == other._slot; + } + bool operator!=(const JsonObjectConstIterator& other) const { + return _slot != other._slot; + } + JsonObjectConstIterator& operator++() { + _slot = _slot->next(); + return *this; + } + JsonObjectConstIterator& operator+=(size_t distance) { + _slot = _slot->next(distance); + return *this; + } + private: + const VariantSlot* _slot; +}; +class JsonObjectConst : public VariantOperators { + friend class JsonObject; + friend class VariantAttorney; + public: + typedef JsonObjectConstIterator iterator; + JsonObjectConst() : _data(0) {} + JsonObjectConst(const CollectionData* data) : _data(data) {} + operator JsonVariantConst() const { + return JsonVariantConst(collectionToVariant(_data)); + } + FORCE_INLINE bool isNull() const { + return _data == 0; + } + FORCE_INLINE operator bool() const { + return _data != 0; + } + FORCE_INLINE size_t memoryUsage() const { + return _data ? _data->memoryUsage() : 0; + } + FORCE_INLINE size_t nesting() const { + return variantNesting(collectionToVariant(_data)); + } + FORCE_INLINE size_t size() const { + return _data ? _data->size() : 0; + } + FORCE_INLINE iterator begin() const { + if (!_data) + return iterator(); + return iterator(_data->head()); + } + FORCE_INLINE iterator end() const { + return iterator(); + } + template + FORCE_INLINE bool containsKey(const TString& key) const { + return getMember(adaptString(key)) != 0; + } + template + FORCE_INLINE bool containsKey(TChar* key) const { + return getMember(adaptString(key)) != 0; + } + template + FORCE_INLINE + typename enable_if::value, JsonVariantConst>::type + operator[](const TString& key) const { + return JsonVariantConst(getMember(adaptString(key))); + } + template + FORCE_INLINE + typename enable_if::value, JsonVariantConst>::type + operator[](TChar* key) const { + return JsonVariantConst(getMember(adaptString(key))); + } + FORCE_INLINE bool operator==(JsonObjectConst rhs) const { + if (_data == rhs._data) + return true; + if (!_data || !rhs._data) + return false; + size_t count = 0; + for (iterator it = begin(); it != end(); ++it) { + if (it->value() != rhs[it->key()]) + return false; + count++; + } + return count == rhs.size(); + } + private: + const VariantData* getData() const { + return collectionToVariant(_data); + } + template + const VariantData* getMember(TAdaptedString key) const { + if (!_data) + return 0; + return _data->getMember(key); + } + const CollectionData* _data; +}; +template <> +struct Converter : private VariantAttorney { + static void toJson(JsonVariantConst src, JsonVariant dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); + } + static JsonObjectConst fromJson(JsonVariantConst src) { + const VariantData* data = getData(src); + return data != 0 ? data->asObject() : 0; + } + static bool checkJson(JsonVariantConst src) { + const VariantData* data = getData(src); + return data && data->isObject(); + } +}; +template +class MemberProxy + : public VariantRefBase >, + public VariantOperators > { + friend class VariantAttorney; + public: + FORCE_INLINE MemberProxy(TUpstream upstream, TStringRef key) + : _upstream(upstream), _key(key) {} + MemberProxy(const MemberProxy& src) + : _upstream(src._upstream), _key(src._key) {} + FORCE_INLINE MemberProxy& operator=(const MemberProxy& src) { + this->set(src); + return *this; + } + template + FORCE_INLINE MemberProxy& operator=(const T& src) { + this->set(src); + return *this; + } + template + FORCE_INLINE MemberProxy& operator=(T* src) { + this->set(src); + return *this; + } + private: + FORCE_INLINE MemoryPool* getPool() const { + return VariantAttorney::getPool(_upstream); + } + FORCE_INLINE VariantData* getData() const { + return variantGetMember(VariantAttorney::getData(_upstream), + adaptString(_key)); + } + FORCE_INLINE VariantData* getOrCreateData() const { + return variantGetOrAddMember(VariantAttorney::getOrCreateData(_upstream), + adaptString(_key), + VariantAttorney::getPool(_upstream)); + } + private: + TUpstream _upstream; + TStringRef _key; +}; +class JsonArray; +class JsonObject : public VariantOperators { + friend class VariantAttorney; + public: + typedef JsonObjectIterator iterator; + FORCE_INLINE JsonObject() : _data(0), _pool(0) {} + FORCE_INLINE JsonObject(MemoryPool* buf, CollectionData* data) + : _data(data), _pool(buf) {} + operator JsonVariant() const { + void* data = _data; // prevent warning cast-align + return JsonVariant(_pool, reinterpret_cast(data)); + } + operator JsonObjectConst() const { + return JsonObjectConst(_data); + } + operator JsonVariantConst() const { + return JsonVariantConst(collectionToVariant(_data)); + } + FORCE_INLINE bool isNull() const { + return _data == 0; + } + FORCE_INLINE operator bool() const { + return _data != 0; + } + FORCE_INLINE size_t memoryUsage() const { + return _data ? _data->memoryUsage() : 0; + } + FORCE_INLINE size_t nesting() const { + return variantNesting(collectionToVariant(_data)); + } + FORCE_INLINE size_t size() const { + return _data ? _data->size() : 0; + } + FORCE_INLINE iterator begin() const { + if (!_data) + return iterator(); + return iterator(_pool, _data->head()); + } + FORCE_INLINE iterator end() const { + return iterator(); + } + void clear() const { + if (!_data) + return; + _data->clear(); + } + FORCE_INLINE bool set(JsonObjectConst src) { + if (!_data || !src._data) + return false; + return _data->copyFrom(*src._data, _pool); + } + FORCE_INLINE bool operator==(JsonObject rhs) const { + return JsonObjectConst(_data) == JsonObjectConst(rhs._data); + } + template + FORCE_INLINE typename enable_if::value, + MemberProxy >::type + operator[](const TString& key) const { + return MemberProxy(*this, key); + } + template + FORCE_INLINE typename enable_if::value, + MemberProxy >::type + operator[](TChar* key) const { + return MemberProxy(*this, key); + } + FORCE_INLINE void remove(iterator it) const { + if (!_data) + return; + _data->removeSlot(it._slot); + } + template + FORCE_INLINE void remove(const TString& key) const { + removeMember(adaptString(key)); + } + template + FORCE_INLINE void remove(TChar* key) const { + removeMember(adaptString(key)); + } + template + FORCE_INLINE typename enable_if::value, bool>::type + containsKey(const TString& key) const { + return getMember(adaptString(key)) != 0; + } + template + FORCE_INLINE typename enable_if::value, bool>::type + containsKey(TChar* key) const { + return getMember(adaptString(key)) != 0; + } + template + FORCE_INLINE JsonArray createNestedArray(const TString& key) const; + template + FORCE_INLINE JsonArray createNestedArray(TChar* key) const; + template + JsonObject createNestedObject(const TString& key) const { + return operator[](key).template to(); + } + template + JsonObject createNestedObject(TChar* key) const { + return operator[](key).template to(); + } + private: + MemoryPool* getPool() const { + return _pool; + } + VariantData* getData() const { + return collectionToVariant(_data); + } + VariantData* getOrCreateData() const { + return collectionToVariant(_data); + } + template + inline VariantData* getMember(TAdaptedString key) const { + if (!_data) + return 0; + return _data->getMember(key); + } + template + void removeMember(TAdaptedString key) const { + if (!_data) + return; + _data->removeMember(key); + } + CollectionData* _data; + MemoryPool* _pool; +}; +template <> +struct Converter : private VariantAttorney { + static void toJson(JsonVariantConst src, JsonVariant dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); + } + static JsonObject fromJson(JsonVariant src) { + VariantData* data = getData(src); + MemoryPool* pool = getPool(src); + return JsonObject(pool, data != 0 ? data->asObject() : 0); + } + static InvalidConversion fromJson( + JsonVariantConst); + static bool checkJson(JsonVariantConst) { + return false; + } + static bool checkJson(JsonVariant src) { + VariantData* data = getData(src); + return data && data->isObject(); + } +}; +class JsonDocument : public VariantOperators { + friend class VariantAttorney; + public: + template + T as() { + return getVariant().template as(); + } + template + T as() const { + return getVariant().template as(); + } + void clear() { + _pool.clear(); + _data.init(); + } + template + bool is() { + return getVariant().template is(); + } + template + bool is() const { + return getVariant().template is(); + } + bool isNull() const { + return getVariant().isNull(); + } + size_t memoryUsage() const { + return _pool.size(); + } + bool overflowed() const { + return _pool.overflowed(); + } + size_t nesting() const { + return variantNesting(&_data); + } + size_t capacity() const { + return _pool.capacity(); + } + size_t size() const { + return _data.size(); + } + bool set(const JsonDocument& src) { + return to().set(src.as()); + } + template + typename enable_if::value, bool>::type set( + const T& src) { + return to().set(src); + } + template + typename VariantTo::type to() { + clear(); + return getVariant().template to(); + } + JsonArray createNestedArray() { + return add().to(); + } + template + JsonArray createNestedArray(TChar* key) { + return operator[](key).template to(); + } + template + JsonArray createNestedArray(const TString& key) { + return operator[](key).template to(); + } + JsonObject createNestedObject() { + return add().to(); + } + template + JsonObject createNestedObject(TChar* key) { + return operator[](key).template to(); + } + template + JsonObject createNestedObject(const TString& key) { + return operator[](key).template to(); + } + template + bool containsKey(TChar* key) const { + return _data.getMember(adaptString(key)) != 0; + } + template + bool containsKey(const TString& key) const { + return _data.getMember(adaptString(key)) != 0; + } + template + FORCE_INLINE typename enable_if::value, + MemberProxy >::type + operator[](const TString& key) { + return MemberProxy(*this, key); + } + template + FORCE_INLINE typename enable_if::value, + MemberProxy >::type + operator[](TChar* key) { + return MemberProxy(*this, key); + } + template + FORCE_INLINE + typename enable_if::value, JsonVariantConst>::type + operator[](const TString& key) const { + return JsonVariantConst(_data.getMember(adaptString(key))); + } + template + FORCE_INLINE + typename enable_if::value, JsonVariantConst>::type + operator[](TChar* key) const { + return JsonVariantConst(_data.getMember(adaptString(key))); + } + FORCE_INLINE ElementProxy operator[](size_t index) { + return ElementProxy(*this, index); + } + FORCE_INLINE JsonVariantConst operator[](size_t index) const { + return JsonVariantConst(_data.getElement(index)); + } + FORCE_INLINE JsonVariant add() { + return JsonVariant(&_pool, _data.addElement(&_pool)); + } + template + FORCE_INLINE bool add(const TValue& value) { + return add().set(value); + } + template + FORCE_INLINE bool add(TChar* value) { + return add().set(value); + } + FORCE_INLINE void remove(size_t index) { + _data.remove(index); + } + template + FORCE_INLINE typename enable_if::value>::type remove( + TChar* key) { + _data.remove(adaptString(key)); + } + template + FORCE_INLINE typename enable_if::value>::type remove( + const TString& key) { + _data.remove(adaptString(key)); + } + FORCE_INLINE operator JsonVariant() { + return getVariant(); + } + FORCE_INLINE operator JsonVariantConst() const { + return getVariant(); + } + protected: + JsonDocument() : _pool(0, 0) { + _data.init(); + } + JsonDocument(MemoryPool pool) : _pool(pool) { + _data.init(); + } + JsonDocument(char* buf, size_t capa) : _pool(buf, capa) { + _data.init(); + } + ~JsonDocument() {} + void replacePool(MemoryPool pool) { + _pool = pool; + } + JsonVariant getVariant() { + return JsonVariant(&_pool, &_data); + } + JsonVariantConst getVariant() const { + return JsonVariantConst(&_data); + } + MemoryPool _pool; + VariantData _data; + private: + JsonDocument(const JsonDocument&); + JsonDocument& operator=(const JsonDocument&); + protected: + MemoryPool* getPool() { + return &_pool; + } + VariantData* getData() { + return &_data; + } + const VariantData* getData() const { + return &_data; + } + VariantData* getOrCreateData() { + return &_data; + } +}; +inline void convertToJson(const JsonDocument& src, JsonVariant dst) { + dst.set(src.as()); +} +template +class AllocatorOwner { + public: + AllocatorOwner() {} + AllocatorOwner(TAllocator a) : _allocator(a) {} + void* allocate(size_t size) { + return _allocator.allocate(size); + } + void deallocate(void* ptr) { + if (ptr) + _allocator.deallocate(ptr); + } + void* reallocate(void* ptr, size_t new_size) { + return _allocator.reallocate(ptr, new_size); + } + TAllocator& allocator() { + return _allocator; + } + private: + TAllocator _allocator; +}; +template +class BasicJsonDocument : AllocatorOwner, public JsonDocument { + public: + explicit BasicJsonDocument(size_t capa, TAllocator alloc = TAllocator()) + : AllocatorOwner(alloc), JsonDocument(allocPool(capa)) {} + BasicJsonDocument(const BasicJsonDocument& src) + : AllocatorOwner(src), JsonDocument() { + copyAssignFrom(src); + } +#if ARDUINOJSON_HAS_RVALUE_REFERENCES + BasicJsonDocument(BasicJsonDocument&& src) : AllocatorOwner(src) { + moveAssignFrom(src); + } +#endif + BasicJsonDocument(const JsonDocument& src) { + copyAssignFrom(src); + } + template + BasicJsonDocument( + const T& src, + typename enable_if< + is_same::value || + is_same::value || is_same::value || + is_same::value || is_same::value || + is_same::value>::type* = 0) + : JsonDocument(allocPool(src.memoryUsage())) { + set(src); + } + BasicJsonDocument(JsonVariant src) + : JsonDocument(allocPool(src.memoryUsage())) { + set(src); + } + ~BasicJsonDocument() { + freePool(); + } + BasicJsonDocument& operator=(const BasicJsonDocument& src) { + copyAssignFrom(src); + return *this; + } +#if ARDUINOJSON_HAS_RVALUE_REFERENCES + BasicJsonDocument& operator=(BasicJsonDocument&& src) { + moveAssignFrom(src); + return *this; + } +#endif + template + BasicJsonDocument& operator=(const T& src) { + size_t requiredSize = src.memoryUsage(); + if (requiredSize > capacity()) + reallocPool(requiredSize); + set(src); + return *this; + } + void shrinkToFit() { + ptrdiff_t bytes_reclaimed = _pool.squash(); + if (bytes_reclaimed == 0) + return; + void* old_ptr = _pool.buffer(); + void* new_ptr = this->reallocate(old_ptr, _pool.capacity()); + ptrdiff_t ptr_offset = + static_cast(new_ptr) - static_cast(old_ptr); + _pool.movePointers(ptr_offset); + _data.movePointers(ptr_offset, ptr_offset - bytes_reclaimed); + } + bool garbageCollect() { + BasicJsonDocument tmp(*this); + if (!tmp.capacity()) + return false; + tmp.set(*this); + moveAssignFrom(tmp); + return true; + } + using AllocatorOwner::allocator; + private: + MemoryPool allocPool(size_t requiredSize) { + size_t capa = addPadding(requiredSize); + return MemoryPool(reinterpret_cast(this->allocate(capa)), capa); + } + void reallocPool(size_t requiredSize) { + size_t capa = addPadding(requiredSize); + if (capa == _pool.capacity()) + return; + freePool(); + replacePool(allocPool(addPadding(requiredSize))); + } + void freePool() { + this->deallocate(getPool()->buffer()); + } + void copyAssignFrom(const JsonDocument& src) { + reallocPool(src.capacity()); + set(src); + } + void moveAssignFrom(BasicJsonDocument& src) { + freePool(); + _data = src._data; + _pool = src._pool; + src._data.setNull(); + src._pool = MemoryPool(0, 0); + } +}; +struct DefaultAllocator { + void* allocate(size_t size) { + return malloc(size); + } + void deallocate(void* ptr) { + free(ptr); + } + void* reallocate(void* ptr, size_t new_size) { + return realloc(ptr, new_size); + } +}; +typedef BasicJsonDocument DynamicJsonDocument; +template +class StaticJsonDocument : public JsonDocument { + static const size_t _capacity = + AddPadding::value>::value; + public: + StaticJsonDocument() : JsonDocument(_buffer, _capacity) {} + StaticJsonDocument(const StaticJsonDocument& src) + : JsonDocument(_buffer, _capacity) { + set(src); + } + template + StaticJsonDocument( + const T& src, + typename enable_if::value>::type* = 0) + : JsonDocument(_buffer, _capacity) { + set(src); + } + StaticJsonDocument(JsonVariant src) : JsonDocument(_buffer, _capacity) { + set(src); + } + StaticJsonDocument& operator=(const StaticJsonDocument& src) { + set(src); + return *this; + } + template + StaticJsonDocument& operator=(const T& src) { + set(src); + return *this; + } + void garbageCollect() { + StaticJsonDocument tmp(*this); + set(tmp); + } + private: + char _buffer[_capacity]; +}; +inline JsonObject JsonArray::createNestedObject() const { + return add().to(); +} +template +inline JsonArray VariantRefBase::createNestedArray() const { + return add().template to(); +} +template +inline JsonObject VariantRefBase::createNestedObject() const { + return add().template to(); +} +template +inline ElementProxy VariantRefBase::operator[]( + size_t index) const { + return ElementProxy(derived(), index); +} +template +inline typename enable_if::value, bool>::type copyArray( + const T& src, JsonVariant dst) { + return dst.set(src); +} +template +inline typename enable_if::value, + bool>::type +copyArray(T (&src)[N], const TDestination& dst) { + return copyArray(src, N, dst); +} +template +inline typename enable_if::value, + bool>::type +copyArray(const T* src, size_t len, const TDestination& dst) { + bool ok = true; + for (size_t i = 0; i < len; i++) { + ok &= copyArray(src[i], dst.add()); + } + return ok; +} +template +inline bool copyArray(const char* src, size_t, const TDestination& dst) { + return dst.set(src); +} +template +inline bool copyArray(const T& src, JsonDocument& dst) { + return copyArray(src, dst.to()); +} +template +inline bool copyArray(const T* src, size_t len, JsonDocument& dst) { + return copyArray(src, len, dst.to()); +} +template +inline typename enable_if::value, size_t>::type copyArray( + JsonVariantConst src, T& dst) { + dst = src.as(); + return 1; +} +template +inline size_t copyArray(JsonArrayConst src, T (&dst)[N]) { + return copyArray(src, dst, N); +} +template +inline size_t copyArray(JsonArrayConst src, T* dst, size_t len) { + size_t i = 0; + for (JsonArrayConst::iterator it = src.begin(); it != src.end() && i < len; + ++it) + copyArray(*it, dst[i++]); + return i; +} +template +inline size_t copyArray(JsonVariantConst src, char (&dst)[N]) { + JsonString s = src; + size_t len = N - 1; + if (len > s.size()) + len = s.size(); + memcpy(dst, s.c_str(), len); + dst[len] = 0; + return 1; +} +template +inline typename enable_if::value && + is_base_of::value, + size_t>::type +copyArray(const TSource& src, T& dst) { + return copyArray(src.template as(), dst); +} +inline VariantSlot* CollectionData::addSlot(MemoryPool* pool) { + VariantSlot* slot = pool->allocVariant(); + if (!slot) + return 0; + if (_tail) { + ARDUINOJSON_ASSERT(pool->owns(_tail)); // Can't alter a linked array/object + _tail->setNextNotNull(slot); + _tail = slot; + } else { + _head = slot; + _tail = slot; + } + slot->clear(); + return slot; +} +inline VariantData* CollectionData::addElement(MemoryPool* pool) { + return slotData(addSlot(pool)); +} +template +inline VariantData* CollectionData::addMember(TAdaptedString key, + MemoryPool* pool) { + VariantSlot* slot = addSlot(pool); + if (!slotSetKey(slot, key, pool)) { + removeSlot(slot); + return 0; + } + return slot->data(); +} +inline void CollectionData::clear() { + _head = 0; + _tail = 0; +} +template +inline bool CollectionData::containsKey(const TAdaptedString& key) const { + return getSlot(key) != 0; +} +inline bool CollectionData::copyFrom(const CollectionData& src, + MemoryPool* pool) { + clear(); + for (VariantSlot* s = src._head; s; s = s->next()) { + VariantData* var; + if (s->key() != 0) { + JsonString key(s->key(), + s->ownsKey() ? JsonString::Copied : JsonString::Linked); + var = addMember(adaptString(key), pool); + } else { + var = addElement(pool); + } + if (!var) + return false; + if (!var->copyFrom(*s->data(), pool)) + return false; + } + return true; +} +template +inline VariantSlot* CollectionData::getSlot(TAdaptedString key) const { + if (key.isNull()) + return 0; + VariantSlot* slot = _head; + while (slot) { + if (stringEquals(key, adaptString(slot->key()))) + break; + slot = slot->next(); + } + return slot; +} +inline VariantSlot* CollectionData::getSlot(size_t index) const { + if (!_head) + return 0; + return _head->next(index); +} +inline VariantSlot* CollectionData::getPreviousSlot(VariantSlot* target) const { + VariantSlot* current = _head; + while (current) { + VariantSlot* next = current->next(); + if (next == target) + return current; + current = next; + } + return 0; +} +template +inline VariantData* CollectionData::getMember(TAdaptedString key) const { + VariantSlot* slot = getSlot(key); + return slot ? slot->data() : 0; +} +template +inline VariantData* CollectionData::getOrAddMember(TAdaptedString key, + MemoryPool* pool) { + if (key.isNull()) + return 0; + VariantSlot* slot = getSlot(key); + if (slot) + return slot->data(); + return addMember(key, pool); +} +inline VariantData* CollectionData::getElement(size_t index) const { + VariantSlot* slot = getSlot(index); + return slot ? slot->data() : 0; +} +inline VariantData* CollectionData::getOrAddElement(size_t index, + MemoryPool* pool) { + VariantSlot* slot = _head; + while (slot && index > 0) { + slot = slot->next(); + index--; + } + if (!slot) + index++; + while (index > 0) { + slot = addSlot(pool); + index--; + } + return slotData(slot); +} +inline void CollectionData::removeSlot(VariantSlot* slot) { + if (!slot) + return; + VariantSlot* prev = getPreviousSlot(slot); + VariantSlot* next = slot->next(); + if (prev) + prev->setNext(next); + else + _head = next; + if (!next) + _tail = prev; +} +inline void CollectionData::removeElement(size_t index) { + removeSlot(getSlot(index)); +} +inline size_t CollectionData::memoryUsage() const { + size_t total = 0; + for (VariantSlot* s = _head; s; s = s->next()) { + total += sizeof(VariantSlot) + s->data()->memoryUsage(); + if (s->ownsKey()) + total += strlen(s->key()) + 1; + } + return total; +} +inline size_t CollectionData::size() const { + return slotSize(_head); +} +template +inline void movePointer(T*& p, ptrdiff_t offset) { + if (!p) + return; + p = reinterpret_cast( + reinterpret_cast(reinterpret_cast(p) + offset)); + ARDUINOJSON_ASSERT(isAligned(p)); +} +inline void CollectionData::movePointers(ptrdiff_t stringDistance, + ptrdiff_t variantDistance) { + movePointer(_head, variantDistance); + movePointer(_tail, variantDistance); + for (VariantSlot* slot = _head; slot; slot = slot->next()) + slot->movePointers(stringDistance, variantDistance); +} +template +inline JsonArray JsonObject::createNestedArray(const TString& key) const { + return operator[](key).template to(); +} +template +inline JsonArray JsonObject::createNestedArray(TChar* key) const { + return operator[](key).template to(); +} +template +template +inline JsonArray VariantRefBase::createNestedArray( + const TString& key) const { + return operator[](key).template to(); +} +template +template +inline JsonArray VariantRefBase::createNestedArray(TChar* key) const { + return operator[](key).template to(); +} +template +template +inline JsonObject VariantRefBase::createNestedObject( + const TString& key) const { + return operator[](key).template to(); +} +template +template +inline JsonObject VariantRefBase::createNestedObject( + TChar* key) const { + return operator[](key).template to(); +} +template +template +inline typename enable_if::value, bool>::type +VariantRefBase::containsKey(const TString& key) const { + return variantGetMember(VariantAttorney::getData(derived()), + adaptString(key)) != 0; +} +template +template +inline typename enable_if::value, bool>::type +VariantRefBase::containsKey(TChar* key) const { + return variantGetMember(VariantAttorney::getData(derived()), + adaptString(key)) != 0; +} +template +template +inline typename enable_if::value, + MemberProxy >::type +VariantRefBase::operator[](TString* key) const { + return MemberProxy(derived(), key); +} +template +template +inline typename enable_if::value, + MemberProxy >::type +VariantRefBase::operator[](const TString& key) const { + return MemberProxy(derived(), key); +} +class EscapeSequence { + public: + static char escapeChar(char c) { + const char* p = escapeTable(true); + while (p[0] && p[1] != c) { + p += 2; + } + return p[0]; + } + static char unescapeChar(char c) { + const char* p = escapeTable(false); + for (;;) { + if (p[0] == '\0') + return 0; + if (p[0] == c) + return p[1]; + p += 2; + } + } + private: + static const char* escapeTable(bool excludeSolidus) { + return &"//\"\"\\\\b\bf\fn\nr\rt\t"[excludeSolidus ? 2 : 0]; + } +}; +template +struct FloatParts { + uint32_t integral; + uint32_t decimal; + int16_t exponent; + int8_t decimalPlaces; + FloatParts(TFloat value) { + uint32_t maxDecimalPart = sizeof(TFloat) >= 8 ? 1000000000 : 1000000; + decimalPlaces = sizeof(TFloat) >= 8 ? 9 : 6; + exponent = normalize(value); + integral = uint32_t(value); + for (uint32_t tmp = integral; tmp >= 10; tmp /= 10) { + maxDecimalPart /= 10; + decimalPlaces--; + } + TFloat remainder = (value - TFloat(integral)) * TFloat(maxDecimalPart); + decimal = uint32_t(remainder); + remainder = remainder - TFloat(decimal); + decimal += uint32_t(remainder * 2); + if (decimal >= maxDecimalPart) { + decimal = 0; + integral++; + if (exponent && integral >= 10) { + exponent++; + integral = 1; + } + } + while (decimal % 10 == 0 && decimalPlaces > 0) { + decimal /= 10; + decimalPlaces--; + } + } + static int16_t normalize(TFloat& value) { + typedef FloatTraits traits; + int16_t powersOf10 = 0; + int8_t index = sizeof(TFloat) == 8 ? 8 : 5; + int bit = 1 << index; + if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) { + for (; index >= 0; index--) { + if (value >= traits::positiveBinaryPowerOfTen(index)) { + value *= traits::negativeBinaryPowerOfTen(index); + powersOf10 = int16_t(powersOf10 + bit); + } + bit >>= 1; + } + } + if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) { + for (; index >= 0; index--) { + if (value < traits::negativeBinaryPowerOfTenPlusOne(index)) { + value *= traits::positiveBinaryPowerOfTen(index); + powersOf10 = int16_t(powersOf10 - bit); + } + bit >>= 1; + } + } + return powersOf10; + } +}; +template +class CountingDecorator { + public: + explicit CountingDecorator(TWriter& writer) : _writer(writer), _count(0) {} + void write(uint8_t c) { + _count += _writer.write(c); + } + void write(const uint8_t* s, size_t n) { + _count += _writer.write(s, n); + } + size_t count() const { + return _count; + } + private: + TWriter _writer; + size_t _count; +}; +template +class TextFormatter { + public: + explicit TextFormatter(TWriter writer) : _writer(writer) {} + size_t bytesWritten() const { + return _writer.count(); + } + void writeBoolean(bool value) { + if (value) + writeRaw("true"); + else + writeRaw("false"); + } + void writeString(const char* value) { + ARDUINOJSON_ASSERT(value != NULL); + writeRaw('\"'); + while (*value) + writeChar(*value++); + writeRaw('\"'); + } + void writeString(const char* value, size_t n) { + ARDUINOJSON_ASSERT(value != NULL); + writeRaw('\"'); + while (n--) + writeChar(*value++); + writeRaw('\"'); + } + void writeChar(char c) { + char specialChar = EscapeSequence::escapeChar(c); + if (specialChar) { + writeRaw('\\'); + writeRaw(specialChar); + } else if (c) { + writeRaw(c); + } else { + writeRaw("\\u0000"); + } + } + template + void writeFloat(T value) { + if (isnan(value)) + return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null"); +#if ARDUINOJSON_ENABLE_INFINITY + if (value < 0.0) { + writeRaw('-'); + value = -value; + } + if (isinf(value)) + return writeRaw("Infinity"); +#else + if (isinf(value)) + return writeRaw("null"); + if (value < 0.0) { + writeRaw('-'); + value = -value; + } +#endif + FloatParts parts(value); + writeInteger(parts.integral); + if (parts.decimalPlaces) + writeDecimals(parts.decimal, parts.decimalPlaces); + if (parts.exponent) { + writeRaw('e'); + writeInteger(parts.exponent); + } + } + template + typename enable_if::value>::type writeInteger(T value) { + typedef typename make_unsigned::type unsigned_type; + unsigned_type unsigned_value; + if (value < 0) { + writeRaw('-'); + unsigned_value = unsigned_type(unsigned_type(~value) + 1); + } else { + unsigned_value = unsigned_type(value); + } + writeInteger(unsigned_value); + } + template + typename enable_if::value>::type writeInteger(T value) { + char buffer[22]; + char* end = buffer + sizeof(buffer); + char* begin = end; + do { + *--begin = char(value % 10 + '0'); + value = T(value / 10); + } while (value); + writeRaw(begin, end); + } + void writeDecimals(uint32_t value, int8_t width) { + char buffer[16]; + char* end = buffer + sizeof(buffer); + char* begin = end; + while (width--) { + *--begin = char(value % 10 + '0'); + value /= 10; + } + *--begin = '.'; + writeRaw(begin, end); + } + void writeRaw(const char* s) { + _writer.write(reinterpret_cast(s), strlen(s)); + } + void writeRaw(const char* s, size_t n) { + _writer.write(reinterpret_cast(s), n); + } + void writeRaw(const char* begin, const char* end) { + _writer.write(reinterpret_cast(begin), + static_cast(end - begin)); + } + template + void writeRaw(const char (&s)[N]) { + _writer.write(reinterpret_cast(s), N - 1); + } + void writeRaw(char c) { + _writer.write(static_cast(c)); + } + protected: + CountingDecorator _writer; + private: + TextFormatter& operator=(const TextFormatter&); // cannot be assigned +}; +class DummyWriter { + public: + size_t write(uint8_t) { + return 1; + } + size_t write(const uint8_t*, size_t n) { + return n; + } +}; +template