// ArduinoJson - https://arduinojson.org // Copyright © 2014-2024, Benoit BLANCHON // MIT License #pragma once #ifdef __cplusplus #if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_VER < 1910) #error ArduinoJson requires C++11 or newer. Configure your compiler for C++11 or downgrade ArduinoJson to 6.20. #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_SIZEOF_POINTER #if defined(__SIZEOF_POINTER__) #define ARDUINOJSON_SIZEOF_POINTER __SIZEOF_POINTER__ #elif defined(_WIN64) && _WIN64 #define ARDUINOJSON_SIZEOF_POINTER 8 // 64 bits #else #define ARDUINOJSON_SIZEOF_POINTER 4 // assume 32 bits otherwise #endif #endif #ifndef ARDUINOJSON_USE_LONG_LONG #if ARDUINOJSON_SIZEOF_POINTER >= 4 // 32 & 64 bits systems #define ARDUINOJSON_USE_LONG_LONG 1 #else #define ARDUINOJSON_USE_LONG_LONG 0 #endif #endif #ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT #define ARDUINOJSON_DEFAULT_NESTING_LIMIT 10 #endif #ifndef ARDUINOJSON_SLOT_ID_SIZE #if ARDUINOJSON_SIZEOF_POINTER <= 2 #define ARDUINOJSON_SLOT_ID_SIZE 1 #elif ARDUINOJSON_SIZEOF_POINTER == 4 #define ARDUINOJSON_SLOT_ID_SIZE 2 #else #define ARDUINOJSON_SLOT_ID_SIZE 4 #endif #endif #ifndef ARDUINOJSON_POOL_CAPACITY #if ARDUINOJSON_SIZEOF_POINTER <= 2 #define ARDUINOJSON_POOL_CAPACITY 16 // 128 bytes #elif ARDUINOJSON_SIZEOF_POINTER == 4 #define ARDUINOJSON_POOL_CAPACITY 64 // 1024 bytes #else #define ARDUINOJSON_POOL_CAPACITY 128 // 3072 bytes #endif #endif #ifndef ARDUINOJSON_INITIAL_POOL_COUNT #define ARDUINOJSON_INITIAL_POOL_COUNT 4 #endif #ifndef ARDUINOJSON_AUTO_SHRINK #if ARDUINOJSON_SIZEOF_POINTER <= 2 #define ARDUINOJSON_AUTO_SHRINK 0 #else #define ARDUINOJSON_AUTO_SHRINK 1 #endif #endif #ifndef ARDUINOJSON_STRING_LENGTH_SIZE #if ARDUINOJSON_SIZEOF_POINTER <= 2 #define ARDUINOJSON_STRING_LENGTH_SIZE 1 // up to 255 characters #else #define ARDUINOJSON_STRING_LENGTH_SIZE 2 // up to 65535 characters #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 #ifdef __AVR__ #define ARDUINOJSON_ENABLE_PROGMEM 1 #else #define ARDUINOJSON_ENABLE_PROGMEM 0 #endif #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_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 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 && defined(ARDUINO)) #include #endif #if !ARDUINOJSON_DEBUG #ifdef __clang__ #pragma clang system_header #elif defined __GNUC__ #pragma GCC system_header #endif #endif #define ARDUINOJSON_CONCAT_(A, B) A##B #define ARDUINOJSON_CONCAT2(A, B) ARDUINOJSON_CONCAT_(A, B) #define ARDUINOJSON_CONCAT3(A, B, C) ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT2(A, B), C) #define ARDUINOJSON_CONCAT4(A, B, C, D) ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT3(A, B, C), D) #define ARDUINOJSON_CONCAT5(A, B, C, D, E) ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT4(A, B, C, D), E) #define ARDUINOJSON_BIN2ALPHA_0000() A #define ARDUINOJSON_BIN2ALPHA_0001() B #define ARDUINOJSON_BIN2ALPHA_0010() C #define ARDUINOJSON_BIN2ALPHA_0011() D #define ARDUINOJSON_BIN2ALPHA_0100() E #define ARDUINOJSON_BIN2ALPHA_0101() F #define ARDUINOJSON_BIN2ALPHA_0110() G #define ARDUINOJSON_BIN2ALPHA_0111() H #define ARDUINOJSON_BIN2ALPHA_1000() I #define ARDUINOJSON_BIN2ALPHA_1001() J #define ARDUINOJSON_BIN2ALPHA_1010() K #define ARDUINOJSON_BIN2ALPHA_1011() L #define ARDUINOJSON_BIN2ALPHA_1100() M #define ARDUINOJSON_BIN2ALPHA_1101() N #define ARDUINOJSON_BIN2ALPHA_1110() O #define ARDUINOJSON_BIN2ALPHA_1111() P #define ARDUINOJSON_BIN2ALPHA_(A, B, C, D) ARDUINOJSON_BIN2ALPHA_##A##B##C##D() #define ARDUINOJSON_BIN2ALPHA(A, B, C, D) ARDUINOJSON_BIN2ALPHA_(A, B, C, D) #define ARDUINOJSON_VERSION "7.1.0" #define ARDUINOJSON_VERSION_MAJOR 7 #define ARDUINOJSON_VERSION_MINOR 1 #define ARDUINOJSON_VERSION_REVISION 0 #define ARDUINOJSON_VERSION_MACRO V710 #ifndef ARDUINOJSON_VERSION_NAMESPACE #define ARDUINOJSON_VERSION_NAMESPACE \ ARDUINOJSON_CONCAT5(ARDUINOJSON_VERSION_MACRO, \ ARDUINOJSON_BIN2ALPHA(ARDUINOJSON_ENABLE_PROGMEM, ARDUINOJSON_USE_LONG_LONG, ARDUINOJSON_USE_DOUBLE, 1), \ ARDUINOJSON_BIN2ALPHA(ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, ARDUINOJSON_ENABLE_COMMENTS, ARDUINOJSON_DECODE_UNICODE), \ ARDUINOJSON_SLOT_ID_SIZE, \ ARDUINOJSON_STRING_LENGTH_SIZE) #endif #define ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE \ namespace ArduinoJson { \ inline namespace ARDUINOJSON_VERSION_NAMESPACE { #define ARDUINOJSON_END_PUBLIC_NAMESPACE \ } \ } #define ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE \ namespace ArduinoJson { \ inline namespace ARDUINOJSON_VERSION_NAMESPACE { \ namespace detail { #define ARDUINOJSON_END_PRIVATE_NAMESPACE \ } \ } \ } ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE template struct Converter; ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template class InvalidConversion; // Error here? See https://arduinojson.org/v7/invalid-conversion/ ARDUINOJSON_END_PRIVATE_NAMESPACE #include #include #include ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class Allocator { public: virtual void * allocate(size_t size) = 0; virtual void deallocate(void * ptr) = 0; virtual void * reallocate(void * ptr, size_t new_size) = 0; protected: ~Allocator() = default; }; namespace detail { class DefaultAllocator : public Allocator { public: void * allocate(size_t size) override { return malloc(size); } void deallocate(void * ptr) override { free(ptr); } void * reallocate(void * ptr, size_t new_size) override { return realloc(ptr, new_size); } static Allocator * instance() { static DefaultAllocator allocator; return &allocator; } private: DefaultAllocator() = default; ~DefaultAllocator() = default; }; } // namespace detail ARDUINOJSON_END_PUBLIC_NAMESPACE #if ARDUINOJSON_DEBUG #include #define ARDUINOJSON_ASSERT(X) assert(X) #else #define ARDUINOJSON_ASSERT(X) ((void)0) #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template struct uint_; template <> struct uint_<8> { typedef uint8_t type; }; template <> struct uint_<16> { typedef uint16_t type; }; template <> struct uint_<32> { typedef uint32_t type; }; template using uint_t = typename uint_::type; template struct conditional { typedef TrueType type; }; template struct conditional { typedef FalseType type; }; template using conditional_t = typename conditional::type; template struct enable_if {}; template struct enable_if { typedef T type; }; template using enable_if_t = typename enable_if::type; template struct function_traits; template struct function_traits { using return_type = ReturnType; using arg1_type = Arg1; }; template struct function_traits { using return_type = ReturnType; using arg1_type = Arg1; using arg2_type = Arg2; }; 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 struct remove_reference { typedef T type; }; template struct remove_reference { typedef T type; }; template using remove_reference_t = typename remove_reference::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 {}; ARDUINOJSON_END_PRIVATE_NAMESPACE #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4244) #endif #ifdef __ICCARM__ #pragma diag_suppress = Pa093 #endif ARDUINOJSON_BEGIN_PRIVATE_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); }; ARDUINOJSON_END_PRIVATE_NAMESPACE #ifdef _MSC_VER #pragma warning(pop) #endif #ifdef __ICCARM__ #pragma diag_default = Pa093 #endif ARDUINOJSON_BEGIN_PRIVATE_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 using remove_cv_t = typename remove_cv::type; template struct is_floating_point : integral_constant>::value || is_same>::value> {}; template struct is_integral : integral_constant< bool, is_same, signed char>::value || is_same, unsigned char>::value || is_same, signed short>::value || is_same, unsigned short>::value || is_same, signed int>::value || is_same, unsigned int>::value || is_same, signed long>::value || is_same, unsigned long>::value || is_same, signed long long>::value || is_same, unsigned long long>::value || is_same, char>::value || is_same, 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, char>::value || is_same, signed char>::value || is_same, signed short>::value || is_same, signed int>::value || is_same, signed long>::value || is_same, signed long long>::value || is_same, float>::value || is_same, double>::value> { }; template struct is_unsigned : integral_constant, unsigned char>::value || is_same, unsigned short>::value || is_same, unsigned int>::value || is_same, unsigned long>::value || is_same, unsigned long long>::value || is_same, 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 {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template using make_unsigned_t = typename make_unsigned::type; template struct remove_const { typedef T type; }; template struct remove_const { typedef T type; }; template using remove_const_t = typename remove_const::type; template struct make_void { using type = void; }; template using void_t = typename make_void::type; ARDUINOJSON_END_PRIVATE_NAMESPACE #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4310) #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template struct numeric_limits; template struct numeric_limits::value>> { static constexpr T lowest() { return 0; } static constexpr T highest() { return T(-1); } }; template struct numeric_limits::value && is_signed::value>> { static constexpr T lowest() { return T(T(1) << (sizeof(T) * 8 - 1)); } static constexpr T highest() { return T(~lowest()); } }; ARDUINOJSON_END_PRIVATE_NAMESPACE #ifdef _MSC_VER #pragma warning(pop) #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE struct StringNode { using references_type = uint_t; using length_type = uint_t; struct StringNode * next; references_type references; length_type length; char data[1]; static constexpr size_t maxLength = numeric_limits::highest(); static constexpr size_t sizeForLength(size_t n) { return n + 1 + offsetof(StringNode, data); } static StringNode * create(size_t length, Allocator * allocator) { if (length > maxLength) return nullptr; auto size = sizeForLength(length); if (size < length) // integer overflow return nullptr; // (not testable on 64-bit) auto node = reinterpret_cast(allocator->allocate(size)); if (node) { node->length = length_type(length); node->references = 1; } return node; } static StringNode * resize(StringNode * node, size_t length, Allocator * allocator) { ARDUINOJSON_ASSERT(node != nullptr); StringNode * newNode; if (length <= maxLength) newNode = reinterpret_cast(allocator->reallocate(node, sizeForLength(length))); else newNode = nullptr; if (newNode) newNode->length = length_type(length); else allocator->deallocate(node); return newNode; } static void destroy(StringNode * node, Allocator * allocator) { allocator->deallocate(node); } }; constexpr size_t sizeofString(size_t n) { return StringNode::sizeForLength(n); } using nullptr_t = decltype(nullptr); template T && forward(remove_reference_t & t) noexcept { return static_cast(t); } template remove_reference_t && move(T && t) { return static_cast &&>(t); } template void swap_(T & a, T & b) { T tmp = move(a); a = move(b); b = move(tmp); } ARDUINOJSON_END_PRIVATE_NAMESPACE #include #ifdef _MSC_VER // Visual Studio #define FORCE_INLINE // __forceinline causes C4714 when returning std::string #ifndef ARDUINOJSON_DEPRECATED #define ARDUINOJSON_DEPRECATED(msg) __declspec(deprecated(msg)) #endif #elif defined(__GNUC__) // GCC or Clang #define FORCE_INLINE __attribute__((always_inline)) #ifndef ARDUINOJSON_DEPRECATED #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) #define ARDUINOJSON_DEPRECATED(msg) __attribute__((deprecated(msg))) #else #define ARDUINOJSON_DEPRECATED(msg) __attribute__((deprecated)) #endif #endif #else // Other compilers #define FORCE_INLINE #ifndef ARDUINOJSON_DEPRECATED #define ARDUINOJSON_DEPRECATED(msg) #endif #endif #if defined(__has_attribute) #if __has_attribute(no_sanitize) #define ARDUINOJSON_NO_SANITIZE(check) __attribute__((no_sanitize(check))) #else #define ARDUINOJSON_NO_SANITIZE(check) #endif #else #define ARDUINOJSON_NO_SANITIZE(check) #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE 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_; } FORCE_INLINE 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; } bool isLinked() const { return false; } protected: const char * str_; }; template struct StringAdapter::value>> { typedef ZeroTerminatedRamString AdaptedString; static AdaptedString adapt(const TChar * p) { return AdaptedString(reinterpret_cast(p)); } }; template struct StringAdapter::value>> { 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) { } bool isLinked() const { return true; } }; 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_; } bool isLinked() const { return false; } protected: const char * str_; size_t size_; }; template struct SizedStringAdapter::value>> { typedef SizedRamString AdaptedString; static AdaptedString adapt(const TChar * p, size_t n) { return AdaptedString(reinterpret_cast(p), n); } }; ARDUINOJSON_END_PRIVATE_NAMESPACE #if ARDUINOJSON_ENABLE_STD_STREAM #include #endif ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class JsonString { 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_; } explicit operator bool() const { return data_ != 0; } 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_; }; ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE class JsonStringAdapter : public SizedRamString { public: JsonStringAdapter(const JsonString & s) : SizedRamString(s.c_str(), s.size()) , linked_(s.isLinked()) { } bool isLinked() const { return linked_; } private: bool linked_; }; template <> struct StringAdapter { typedef JsonStringAdapter AdaptedString; static AdaptedString adapt(const JsonString & s) { return AdaptedString(s); } }; namespace string_traits_impl { template struct has_cstr : false_type {}; template struct has_cstr().c_str()), const char *>::value>> : true_type {}; template struct has_data : false_type {}; template struct has_data().data()), const char *>::value>> : true_type {}; template struct has_length : false_type {}; template struct has_length().length())>::value>> : true_type {}; template struct has_size : false_type {}; template struct has_size().size()), size_t>::value>> : true_type {}; } // namespace string_traits_impl template struct string_traits { enum { has_cstr = string_traits_impl::has_cstr::value, has_length = string_traits_impl::has_length::value, has_data = string_traits_impl::has_data::value, has_size = string_traits_impl::has_size::value }; }; template struct StringAdapter::has_cstr || string_traits::has_data) && (string_traits::has_length || string_traits::has_size)>> { typedef SizedRamString AdaptedString; static AdaptedString adapt(const T & s) { return AdaptedString(get_data(s), get_size(s)); } private: template static enable_if_t::has_size, size_t> get_size(const U & s) { return s.size(); } template static enable_if_t::has_size, size_t> get_size(const U & s) { return s.length(); } template static enable_if_t::has_data, const char *> get_data(const U & s) { return s.data(); } template static enable_if_t::has_data, const char *> get_data(const U & s) { return s.c_str(); } }; ARDUINOJSON_END_PRIVATE_NAMESPACE #if ARDUINOJSON_ENABLE_PROGMEM #ifdef ARDUINO #else class __FlashStringHelper; #include #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE struct pgm_p { pgm_p(const void * p) : address(reinterpret_cast(p)) { } const char * address; }; ARDUINOJSON_END_PRIVATE_NAMESPACE #ifndef strlen_P inline size_t strlen_P(ArduinoJson::detail::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::detail::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::detail::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::detail::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::detail::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::detail::pgm_p p) { uint32_t result; memcpy_P(&result, p.address, 4); return result; } #endif #ifndef pgm_read_float inline float pgm_read_float(ArduinoJson::detail::pgm_p p) { float result; memcpy_P(&result, p.address, sizeof(float)); return result; } #endif #ifndef pgm_read_double #if defined(__SIZEOF_DOUBLE__) && defined(__SIZEOF_FLOAT__) && __SIZEOF_DOUBLE__ == __SIZEOF_FLOAT__ inline double pgm_read_double(ArduinoJson::detail::pgm_p p) { return pgm_read_float(p.address); } #else inline double pgm_read_double(ArduinoJson::detail::pgm_p p) { double result; memcpy_P(&result, p.address, sizeof(double)); return result; } #endif #endif #ifndef pgm_read_ptr inline void * pgm_read_ptr(ArduinoJson::detail::pgm_p p) { void * result; memcpy_P(&result, p.address, sizeof(result)); return result; } #endif ARDUINOJSON_BEGIN_PRIVATE_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)); } const char * data() const { return nullptr; } 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); } bool isLinked() const { return false; } 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); } }; ARDUINOJSON_END_PRIVATE_NAMESPACE #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template enable_if_t 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 enable_if_t<(TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), int> stringCompare(TAdaptedString1 s1, TAdaptedString2 s2) { return -stringCompare(s2, s1); } template enable_if_t 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 enable_if_t<(TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), bool> 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]; } } class VariantSlot; class VariantPool; class StringPool { public: StringPool() = default; StringPool(const StringPool &) = delete; void operator=(StringPool && src) = delete; ~StringPool() { ARDUINOJSON_ASSERT(strings_ == nullptr); } friend void swap(StringPool & a, StringPool & b) { swap_(a.strings_, b.strings_); } void clear(Allocator * allocator) { while (strings_) { auto node = strings_; strings_ = node->next; StringNode::destroy(node, allocator); } } size_t size() const { size_t total = 0; for (auto node = strings_; node; node = node->next) total += sizeofString(node->length); return total; } template StringNode * add(TAdaptedString str, Allocator * allocator) { ARDUINOJSON_ASSERT(str.isNull() == false); auto node = get(str); if (node) { node->references++; return node; } size_t n = str.size(); node = StringNode::create(n, allocator); if (!node) return nullptr; stringGetChars(str, node->data, n); node->data[n] = 0; // force NUL terminator add(node); return node; } void add(StringNode * node) { ARDUINOJSON_ASSERT(node != nullptr); node->next = strings_; strings_ = node; } template StringNode * get(const TAdaptedString & str) const { for (auto node = strings_; node; node = node->next) { if (stringEquals(str, adaptString(node->data, node->length))) return node; } return nullptr; } void dereference(const char * s, Allocator * allocator) { StringNode * prev = nullptr; for (auto node = strings_; node; node = node->next) { if (node->data == s) { if (--node->references == 0) { if (prev) prev->next = node->next; else strings_ = node->next; StringNode::destroy(node, allocator); } return; } prev = node; } } private: StringNode * strings_ = nullptr; }; class VariantSlot; using SlotId = uint_t; using SlotCount = SlotId; const SlotId NULL_SLOT = SlotId(-1); class SlotWithId { public: SlotWithId() : slot_(nullptr) , id_(NULL_SLOT) { } SlotWithId(VariantSlot * slot, SlotId id) : slot_(slot) , id_(id) { ARDUINOJSON_ASSERT((slot == nullptr) == (id == NULL_SLOT)); } SlotId id() const { return id_; } operator VariantSlot *() { return slot_; } VariantSlot * operator->() { ARDUINOJSON_ASSERT(slot_ != nullptr); return slot_; } private: VariantSlot * slot_; SlotId id_; }; class VariantPool { public: void create(SlotCount cap, Allocator * allocator); void destroy(Allocator * allocator); SlotWithId allocSlot(); VariantSlot * getSlot(SlotId id) const; void clear(); void shrinkToFit(Allocator *); SlotCount usage() const; static SlotCount bytesToSlots(size_t); static size_t slotsToBytes(SlotCount); private: SlotCount capacity_; SlotCount usage_; VariantSlot * slots_; }; using PoolCount = SlotId; class VariantPoolList { public: VariantPoolList() = default; ~VariantPoolList() { ARDUINOJSON_ASSERT(count_ == 0); } friend void swap(VariantPoolList & a, VariantPoolList & b) { bool aUsedPreallocated = a.pools_ == a.preallocatedPools_; bool bUsedPreallocated = b.pools_ == b.preallocatedPools_; if (aUsedPreallocated && bUsedPreallocated) { for (PoolCount i = 0; i < ARDUINOJSON_INITIAL_POOL_COUNT; i++) swap_(a.preallocatedPools_[i], b.preallocatedPools_[i]); } else if (bUsedPreallocated) { for (PoolCount i = 0; i < b.count_; i++) a.preallocatedPools_[i] = b.preallocatedPools_[i]; b.pools_ = a.pools_; a.pools_ = a.preallocatedPools_; } else if (aUsedPreallocated) { for (PoolCount i = 0; i < a.count_; i++) b.preallocatedPools_[i] = a.preallocatedPools_[i]; a.pools_ = b.pools_; b.pools_ = b.preallocatedPools_; } else { swap_(a.pools_, b.pools_); } swap_(a.count_, b.count_); swap_(a.capacity_, b.capacity_); swap_(a.freeList_, b.freeList_); } VariantPoolList & operator=(VariantPoolList && src) { ARDUINOJSON_ASSERT(count_ == 0); if (src.pools_ == src.preallocatedPools_) { memcpy(preallocatedPools_, src.preallocatedPools_, sizeof(preallocatedPools_)); pools_ = preallocatedPools_; } else { pools_ = src.pools_; src.pools_ = nullptr; } count_ = src.count_; capacity_ = src.capacity_; src.count_ = 0; src.capacity_ = 0; return *this; } SlotWithId allocSlot(Allocator * allocator) { if (freeList_ != NULL_SLOT) { return allocFromFreeList(); } if (count_) { auto slot = allocFromLastPool(); if (slot) return slot; } auto pool = addPool(allocator); if (!pool) return {}; return allocFromLastPool(); } void freeSlot(SlotWithId slot); VariantSlot * getSlot(SlotId id) const { if (id == NULL_SLOT) return nullptr; auto poolIndex = SlotId(id / ARDUINOJSON_POOL_CAPACITY); auto indexInPool = SlotId(id % ARDUINOJSON_POOL_CAPACITY); ARDUINOJSON_ASSERT(poolIndex < count_); return pools_[poolIndex].getSlot(indexInPool); } void clear(Allocator * allocator) { for (PoolCount i = 0; i < count_; i++) pools_[i].destroy(allocator); count_ = 0; freeList_ = NULL_SLOT; if (pools_ != preallocatedPools_) { allocator->deallocate(pools_); pools_ = preallocatedPools_; capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT; } } SlotCount usage() const { SlotCount total = 0; for (PoolCount i = 0; i < count_; i++) total = SlotCount(total + pools_[i].usage()); return total; } void shrinkToFit(Allocator * allocator) { if (count_ > 0) pools_[count_ - 1].shrinkToFit(allocator); if (pools_ != preallocatedPools_ && count_ != capacity_) { pools_ = static_cast(allocator->reallocate(pools_, count_ * sizeof(VariantPool))); ARDUINOJSON_ASSERT(pools_ != nullptr); // realloc to smaller can't fail capacity_ = count_; } } private: SlotWithId allocFromFreeList(); SlotWithId allocFromLastPool() { ARDUINOJSON_ASSERT(count_ > 0); auto poolIndex = SlotId(count_ - 1); auto slot = pools_[poolIndex].allocSlot(); if (!slot) return {}; return {slot, SlotId(poolIndex * ARDUINOJSON_POOL_CAPACITY + slot.id())}; } VariantPool * addPool(Allocator * allocator) { if (count_ == capacity_ && !increaseCapacity(allocator)) return nullptr; auto pool = &pools_[count_++]; SlotCount poolCapacity = ARDUINOJSON_POOL_CAPACITY; if (count_ == maxPools) // last pool is smaller because of NULL_SLOT poolCapacity--; pool->create(poolCapacity, allocator); return pool; } bool increaseCapacity(Allocator * allocator) { if (capacity_ == maxPools) return false; void * newPools; auto newCapacity = PoolCount(capacity_ * 2); if (pools_ == preallocatedPools_) { newPools = allocator->allocate(newCapacity * sizeof(VariantPool)); if (!newPools) return false; memcpy(newPools, preallocatedPools_, sizeof(preallocatedPools_)); } else { newPools = allocator->reallocate(pools_, newCapacity * sizeof(VariantPool)); if (!newPools) return false; } pools_ = static_cast(newPools); capacity_ = newCapacity; return true; } VariantPool preallocatedPools_[ARDUINOJSON_INITIAL_POOL_COUNT]; VariantPool * pools_ = preallocatedPools_; PoolCount count_ = 0; PoolCount capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT; SlotId freeList_ = NULL_SLOT; public: static const PoolCount maxPools = PoolCount(NULL_SLOT / ARDUINOJSON_POOL_CAPACITY + 1); }; class VariantSlot; class VariantPool; class ResourceManager { public: ResourceManager(Allocator * allocator = DefaultAllocator::instance()) : allocator_(allocator) , overflowed_(false) { } ~ResourceManager() { stringPool_.clear(allocator_); variantPools_.clear(allocator_); } ResourceManager(const ResourceManager &) = delete; ResourceManager & operator=(const ResourceManager & src) = delete; friend void swap(ResourceManager & a, ResourceManager & b) { swap(a.stringPool_, b.stringPool_); swap(a.variantPools_, b.variantPools_); swap_(a.allocator_, b.allocator_); swap_(a.overflowed_, b.overflowed_); } Allocator * allocator() const { return allocator_; } size_t size() const { return VariantPool::slotsToBytes(variantPools_.usage()) + stringPool_.size(); } bool overflowed() const { return overflowed_; } SlotWithId allocSlot() { auto p = variantPools_.allocSlot(allocator_); if (!p) overflowed_ = true; return p; } void freeSlot(SlotWithId slot); VariantSlot * getSlot(SlotId id) const { return variantPools_.getSlot(id); } template StringNode * saveString(TAdaptedString str) { if (str.isNull()) return 0; auto node = stringPool_.add(str, allocator_); if (!node) overflowed_ = true; return node; } void saveString(StringNode * node) { stringPool_.add(node); } template StringNode * getString(const TAdaptedString & str) const { return stringPool_.get(str); } StringNode * createString(size_t length) { auto node = StringNode::create(length, allocator_); if (!node) overflowed_ = true; return node; } StringNode * resizeString(StringNode * node, size_t length) { node = StringNode::resize(node, length, allocator_); if (!node) overflowed_ = true; return node; } void destroyString(StringNode * node) { StringNode::destroy(node, allocator_); } void dereferenceString(const char * s) { stringPool_.dereference(s, allocator_); } void clear() { variantPools_.clear(allocator_); overflowed_ = false; stringPool_.clear(allocator_); } void shrinkToFit() { variantPools_.shrinkToFit(allocator_); } private: Allocator * allocator_; bool overflowed_; StringPool stringPool_; VariantPoolList variantPools_; }; template struct IsString : false_type {}; template struct IsString::AdaptedString>> : true_type {}; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE 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_; }; using RawString = SerializedValue; template inline SerializedValue serialized(T str) { return SerializedValue(str); } template inline SerializedValue serialized(TChar * p) { return SerializedValue(p, detail::adaptString(p).size()); } template inline SerializedValue serialized(TChar * p, size_t n) { return SerializedValue(p, n); } ARDUINOJSON_END_PUBLIC_NAMESPACE #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #endif ARDUINOJSON_BEGIN_PRIVATE_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; } ARDUINOJSON_END_PRIVATE_NAMESPACE #if ARDUINOJSON_ENABLE_PROGMEM #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE #if ARDUINOJSON_ENABLE_PROGMEM #ifndef ARDUINOJSON_DEFINE_PROGMEM_ARRAY #define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, ...) static type const name[] PROGMEM = __VA_ARGS__; #endif template inline const T * pgm_read(const T * const * p) { return reinterpret_cast(pgm_read_ptr(p)); } inline uint32_t pgm_read(const uint32_t * p) { return pgm_read_dword(p); } inline double pgm_read(const double * p) { return pgm_read_double(p); } inline float pgm_read(const float * p) { return pgm_read_float(p); } #else #ifndef ARDUINOJSON_DEFINE_PROGMEM_ARRAY #define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, ...) static type const name[] = __VA_ARGS__; #endif template inline T pgm_read(const T * p) { return *p; } #endif template class pgm_ptr { public: explicit pgm_ptr(const T * ptr) : ptr_(ptr) { } T operator[](intptr_t index) const { return pgm_read(ptr_ + index); } private: const T * ptr_; }; 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; static pgm_ptr positiveBinaryPowersOfTen() { ARDUINOJSON_DEFINE_PROGMEM_ARRAY( // uint64_t, factors, { 0x4024000000000000, // 1e1 0x4059000000000000, // 1e2 0x40C3880000000000, // 1e4 0x4197D78400000000, // 1e8 0x4341C37937E08000, // 1e16 0x4693B8B5B5056E17, // 1e32 0x4D384F03E93FF9F5, // 1e64 0x5A827748F9301D32, // 1e128 0x75154FDD7F73BF3C, // 1e256 }); return pgm_ptr(reinterpret_cast(factors)); } static pgm_ptr negativeBinaryPowersOfTen() { ARDUINOJSON_DEFINE_PROGMEM_ARRAY( // uint64_t, factors, { 0x3FB999999999999A, // 1e-1 0x3F847AE147AE147B, // 1e-2 0x3F1A36E2EB1C432D, // 1e-4 0x3E45798EE2308C3A, // 1e-8 0x3C9CD2B297D889BC, // 1e-16 0x3949F623D5A8A733, // 1e-32 0x32A50FFD44F4A73D, // 1e-64 0x255BBA08CF8C979D, // 1e-128 0x0AC8062864AC6F43 // 1e-256 }); return pgm_ptr(reinterpret_cast(factors)); } static T nan() { return forge(0x7ff8000000000000); } static T inf() { return forge(0x7ff0000000000000); } static T highest() { return forge(0x7FEFFFFFFFFFFFFF); } template // int64_t static T highest_for(enable_if_t::value && is_signed::value && sizeof(TOut) == 8, signed> * = 0) { return forge(0x43DFFFFFFFFFFFFF); // 9.2233720368547748e+18 } template // uint64_t static T highest_for(enable_if_t::value && is_unsigned::value && sizeof(TOut) == 8, unsigned> * = 0) { return forge(0x43EFFFFFFFFFFFFF); // 1.8446744073709549568e+19 } static T lowest() { return forge(0xFFEFFFFFFFFFFFFF); } static T forge(uint64_t bits) { return alias_cast(bits); } }; 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; static pgm_ptr positiveBinaryPowersOfTen() { ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors, { 0x41200000, // 1e1f 0x42c80000, // 1e2f 0x461c4000, // 1e4f 0x4cbebc20, // 1e8f 0x5a0e1bca, // 1e16f 0x749dc5ae // 1e32f }); return pgm_ptr(reinterpret_cast(factors)); } static pgm_ptr negativeBinaryPowersOfTen() { ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors, { 0x3dcccccd, // 1e-1f 0x3c23d70a, // 1e-2f 0x38d1b717, // 1e-4f 0x322bcc77, // 1e-8f 0x24e69595, // 1e-16f 0x0a4fb11f // 1e-32f }); return pgm_ptr(reinterpret_cast(factors)); } 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(enable_if_t::value && is_signed::value && sizeof(TOut) == 4, signed> * = 0) { return forge(0x4EFFFFFF); // 2.14748352E9 } template // uint32_t static T highest_for(enable_if_t::value && is_unsigned::value && sizeof(TOut) == 4, unsigned> * = 0) { return forge(0x4F7FFFFF); // 4.29496704E9 } template // int64_t static T highest_for(enable_if_t::value && is_signed::value && sizeof(TOut) == 8, signed> * = 0) { return forge(0x5EFFFFFF); // 9.22337148709896192E18 } template // uint64_t static T highest_for(enable_if_t::value && is_unsigned::value && sizeof(TOut) == 8, unsigned> * = 0) { return forge(0x5F7FFFFF); // 1.844674297419792384E19 } static T lowest() { return forge(0xFf7fffff); } }; template inline TFloat make_float(TFloat m, TExponent e) { using traits = FloatTraits; auto powersOfTen = e > 0 ? traits::positiveBinaryPowersOfTen() : traits::negativeBinaryPowersOfTen(); if (e <= 0) e = TExponent(-e); for (uint8_t index = 0; e != 0; index++) { if (e & 1) m *= powersOfTen[index]; e >>= 1; } return m; } ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE #if ARDUINOJSON_USE_DOUBLE typedef double JsonFloat; #else typedef float JsonFloat; #endif ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template enable_if_t::value && is_unsigned::value && is_integral::value && sizeof(TOut) <= sizeof(TIn), bool> canConvertNumber(TIn value) { return value <= TIn(numeric_limits::highest()); } template enable_if_t::value && is_unsigned::value && is_integral::value && sizeof(TIn) < sizeof(TOut), bool> canConvertNumber(TIn) { return true; } template enable_if_t::value && is_floating_point::value, bool> canConvertNumber(TIn) { return true; } template enable_if_t::value && is_signed::value && is_integral::value && is_signed::value && sizeof(TOut) < sizeof(TIn), bool> canConvertNumber(TIn value) { return value >= TIn(numeric_limits::lowest()) && value <= TIn(numeric_limits::highest()); } template enable_if_t::value && is_signed::value && is_integral::value && is_signed::value && sizeof(TIn) <= sizeof(TOut), bool> canConvertNumber(TIn) { return true; } template enable_if_t::value && is_signed::value && is_integral::value && is_unsigned::value && sizeof(TOut) >= sizeof(TIn), bool> canConvertNumber(TIn value) { if (value < 0) return false; return TOut(value) <= numeric_limits::highest(); } template enable_if_t::value && is_signed::value && is_integral::value && is_unsigned::value && sizeof(TOut) < sizeof(TIn), bool> canConvertNumber(TIn value) { if (value < 0) return false; return value <= TIn(numeric_limits::highest()); } template enable_if_t::value && is_integral::value && sizeof(TOut) < sizeof(TIn), bool> canConvertNumber(TIn value) { return value >= numeric_limits::lowest() && value <= numeric_limits::highest(); } template enable_if_t::value && is_integral::value && sizeof(TOut) >= sizeof(TIn), bool> canConvertNumber(TIn value) { return value >= numeric_limits::lowest() && value <= FloatTraits::template highest_for(); } template TOut convertNumber(TIn value) { return canConvertNumber(value) ? TOut(value) : 0; } ARDUINOJSON_END_PRIVATE_NAMESPACE #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) #pragma GCC diagnostic pop #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE class VariantData; class VariantSlot; class CollectionIterator { friend class CollectionData; public: CollectionIterator() : slot_(nullptr) , currentId_(NULL_SLOT) { } void next(const ResourceManager * resources); bool done() const { return slot_ == nullptr; } bool operator==(const CollectionIterator & other) const { return slot_ == other.slot_; } bool operator!=(const CollectionIterator & other) const { return slot_ != other.slot_; } VariantData * operator->() { ARDUINOJSON_ASSERT(slot_ != nullptr); return data(); } VariantData & operator*() { ARDUINOJSON_ASSERT(slot_ != nullptr); return *data(); } const VariantData & operator*() const { ARDUINOJSON_ASSERT(slot_ != nullptr); return *data(); } const char * key() const; bool ownsKey() const; void setKey(StringNode *); void setKey(const char *); VariantData * data() { return reinterpret_cast(slot_); } const VariantData * data() const { return reinterpret_cast(slot_); } private: CollectionIterator(VariantSlot * slot, SlotId slotId); VariantSlot * slot_; SlotId currentId_, nextId_; }; class CollectionData { SlotId head_ = NULL_SLOT; SlotId tail_ = NULL_SLOT; public: static void * operator new(size_t, void * p) noexcept { return p; } static void operator delete(void *, void *) noexcept { } using iterator = CollectionIterator; iterator createIterator(const ResourceManager * resources) const { return iterator(resources->getSlot(head_), head_); } size_t size(const ResourceManager *) const; size_t nesting(const ResourceManager *) const; void clear(ResourceManager * resources); static void clear(CollectionData * collection, ResourceManager * resources) { if (!collection) return; collection->clear(resources); } void remove(iterator it, ResourceManager * resources); static void remove(CollectionData * collection, iterator it, ResourceManager * resources) { if (collection) return collection->remove(it, resources); } SlotId head() const { return head_; } void addSlot(SlotWithId slot, ResourceManager * resources); protected: iterator addSlot(ResourceManager *); private: SlotWithId getPreviousSlot(VariantSlot *, const ResourceManager *) 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); } class ArrayData : public CollectionData { public: VariantData * addElement(ResourceManager * resources) { return addSlot(resources).data(); } static VariantData * addElement(ArrayData * array, ResourceManager * resources) { if (!array) return nullptr; return array->addElement(resources); } template bool addValue(T && value, ResourceManager * resources); template static bool addValue(ArrayData * array, T && value, ResourceManager * resources) { if (!array) return false; return array->addValue(value, resources); } VariantData * getOrAddElement(size_t index, ResourceManager * resources); VariantData * getElement(size_t index, const ResourceManager * resources) const; static VariantData * getElement(const ArrayData * array, size_t index, const ResourceManager * resources) { if (!array) return nullptr; return array->getElement(index, resources); } void removeElement(size_t index, ResourceManager * resources); static void removeElement(ArrayData * array, size_t index, ResourceManager * resources) { if (!array) return; array->removeElement(index, resources); } bool copyFrom(const ArrayData & src, ResourceManager * resources); static bool copy(ArrayData * dst, const ArrayData * src, ResourceManager * resources) { if (!dst || !src) return false; return dst->copyFrom(*src, resources); } private: iterator at(size_t index, const ResourceManager * resources) const; }; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE #if ARDUINOJSON_USE_LONG_LONG typedef int64_t JsonInteger; typedef uint64_t JsonUInt; #else typedef long JsonInteger; typedef unsigned long JsonUInt; #endif ARDUINOJSON_END_PUBLIC_NAMESPACE #define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T) \ static_assert(sizeof(T) <= sizeof(ArduinoJson::JsonInteger), \ "To use 64-bit integers with ArduinoJson, you must set " \ "ARDUINOJSON_USE_LONG_LONG to 1. See " \ "https://arduinojson.org/v7/api/config/use_long_long/"); ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE class ObjectData : public CollectionData { public: VariantData * addMember(StringNode * key, ResourceManager * resources) { ARDUINOJSON_ASSERT(key != nullptr); auto it = addSlot(resources); if (it.done()) return nullptr; it.setKey(key); return it.data(); } template VariantData * addMember(TAdaptedString key, ResourceManager * resources) { ARDUINOJSON_ASSERT(!key.isNull()); if (key.isLinked()) { auto it = addSlot(resources); if (!it.done()) it.setKey(key.data()); return it.data(); } else { auto storedKey = resources->saveString(key); if (!storedKey) return nullptr; auto it = addSlot(resources); if (!it.done()) it.setKey(storedKey); return it.data(); } } template VariantData * getOrAddMember(TAdaptedString key, ResourceManager * resources); template VariantData * getMember(TAdaptedString key, const ResourceManager * resources) const; template static VariantData * getMember(const ObjectData * object, TAdaptedString key, const ResourceManager * resources) { if (!object) return nullptr; return object->getMember(key, resources); } template void removeMember(TAdaptedString key, ResourceManager * resources); template static void removeMember(ObjectData * obj, TAdaptedString key, ResourceManager * resources) { if (!obj) return; obj->removeMember(key, resources); } private: template iterator findKey(TAdaptedString key, const ResourceManager * resources) const; }; enum { VALUE_MASK = 0x7F, OWNED_VALUE_BIT = 0x01, VALUE_IS_NULL = 0, VALUE_IS_RAW_STRING = 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 }; union VariantContent { VariantContent() { } JsonFloat asFloat; bool asBoolean; JsonUInt asUnsignedInteger; JsonInteger asSignedInteger; ArrayData asArray; ObjectData asObject; CollectionData asCollection; const char * asLinkedString; struct StringNode * asOwnedString; }; struct StringNode; class VariantSlot { VariantContent content_; uint8_t flags_; SlotId next_; const char * key_; public: static void * operator new(size_t, void * p) noexcept { return p; } static void operator delete(void *, void *) noexcept { } VariantSlot() : flags_(0) , next_(NULL_SLOT) , key_(0) { } VariantData * data() { return reinterpret_cast(&content_); } const VariantData * data() const { return reinterpret_cast(&content_); } SlotId next() const { return next_; } void setNext(SlotId slot) { next_ = slot; } void setKey(const char * k) { ARDUINOJSON_ASSERT(k); flags_ &= VALUE_MASK; key_ = k; } void setKey(StringNode * k) { ARDUINOJSON_ASSERT(k); flags_ |= OWNED_KEY_BIT; key_ = k->data; } const char * key() const { return key_; } bool ownsKey() const { return (flags_ & OWNED_KEY_BIT) != 0; } }; inline VariantData * slotData(VariantSlot * slot) { return reinterpret_cast(slot); } constexpr size_t sizeofArray(size_t n) { return n * sizeof(VariantSlot); } constexpr size_t sizeofObject(size_t n) { return n * sizeof(VariantSlot); } template T parseNumber(const char * s); class VariantData { VariantContent content_; // must be first to allow cast from array to variant uint8_t flags_; public: VariantData() : flags_(VALUE_IS_NULL) { } template typename TVisitor::result_type accept(TVisitor & visit) const { switch (type()) { case VALUE_IS_FLOAT: return visit.visit(content_.asFloat); case VALUE_IS_ARRAY: return visit.visit(content_.asArray); case VALUE_IS_OBJECT: return visit.visit(content_.asObject); case VALUE_IS_LINKED_STRING: return visit.visit(JsonString(content_.asLinkedString)); case VALUE_IS_OWNED_STRING: return visit.visit(JsonString(content_.asOwnedString->data, content_.asOwnedString->length, JsonString::Copied)); case VALUE_IS_RAW_STRING: return visit.visit(RawString(content_.asOwnedString->data, content_.asOwnedString->length)); case VALUE_IS_SIGNED_INTEGER: return visit.visit(content_.asSignedInteger); case VALUE_IS_UNSIGNED_INTEGER: return visit.visit(content_.asUnsignedInteger); case VALUE_IS_BOOLEAN: return visit.visit(content_.asBoolean != 0); default: return visit.visit(nullptr); } } template static typename TVisitor::result_type accept(const VariantData * var, TVisitor & visit) { if (var != 0) return var->accept(visit); else return visit.visit(nullptr); } VariantData * addElement(ResourceManager * resources) { auto array = isNull() ? &toArray() : asArray(); return detail::ArrayData::addElement(array, resources); } static VariantData * addElement(VariantData * var, ResourceManager * resources) { if (!var) return nullptr; return var->addElement(resources); } template bool addValue(T && value, ResourceManager * resources) { auto array = isNull() ? &toArray() : asArray(); return detail::ArrayData::addValue(array, detail::forward(value), resources); } template static bool addValue(VariantData * var, T && value, ResourceManager * resources) { if (!var) return false; return var->addValue(value, resources); } bool asBoolean() const { switch (type()) { case VALUE_IS_BOOLEAN: return content_.asBoolean; case VALUE_IS_SIGNED_INTEGER: case VALUE_IS_UNSIGNED_INTEGER: return content_.asUnsignedInteger != 0; case VALUE_IS_FLOAT: return content_.asFloat != 0; case VALUE_IS_NULL: return false; default: return true; } } ArrayData * asArray() { return isArray() ? &content_.asArray : 0; } const ArrayData * asArray() const { return const_cast(this)->asArray(); } CollectionData * asCollection() { return isCollection() ? &content_.asCollection : 0; } const CollectionData * asCollection() const { return const_cast(this)->asCollection(); } template T asFloat() const { static_assert(is_floating_point::value, "T must be a floating point"); switch (type()) { case VALUE_IS_BOOLEAN: return static_cast(content_.asBoolean); case VALUE_IS_UNSIGNED_INTEGER: return static_cast(content_.asUnsignedInteger); case VALUE_IS_SIGNED_INTEGER: return static_cast(content_.asSignedInteger); case VALUE_IS_LINKED_STRING: case VALUE_IS_OWNED_STRING: return parseNumber(content_.asOwnedString->data); case VALUE_IS_FLOAT: return static_cast(content_.asFloat); default: return 0; } } template T asIntegral() const { static_assert(is_integral::value, "T must be an integral type"); switch (type()) { case VALUE_IS_BOOLEAN: return content_.asBoolean; case VALUE_IS_UNSIGNED_INTEGER: return convertNumber(content_.asUnsignedInteger); case VALUE_IS_SIGNED_INTEGER: return convertNumber(content_.asSignedInteger); case VALUE_IS_LINKED_STRING: return parseNumber(content_.asLinkedString); case VALUE_IS_OWNED_STRING: return parseNumber(content_.asOwnedString->data); case VALUE_IS_FLOAT: return convertNumber(content_.asFloat); default: return 0; } } ObjectData * asObject() { return isObject() ? &content_.asObject : 0; } const ObjectData * asObject() const { return const_cast(this)->asObject(); } JsonString asRawString() const { switch (type()) { case VALUE_IS_RAW_STRING: return JsonString(content_.asOwnedString->data, content_.asOwnedString->length, JsonString::Copied); default: return JsonString(); } } JsonString asString() const { switch (type()) { case VALUE_IS_LINKED_STRING: return JsonString(content_.asLinkedString, JsonString::Linked); case VALUE_IS_OWNED_STRING: return JsonString(content_.asOwnedString->data, content_.asOwnedString->length, JsonString::Copied); default: return JsonString(); } } VariantData * getElement(size_t index, const ResourceManager * resources) const { return ArrayData::getElement(asArray(), index, resources); } static VariantData * getElement(const VariantData * var, size_t index, const ResourceManager * resources) { return var != 0 ? var->getElement(index, resources) : 0; } template VariantData * getMember(TAdaptedString key, const ResourceManager * resources) const { return ObjectData::getMember(asObject(), key, resources); } template static VariantData * getMember(const VariantData * var, TAdaptedString key, const ResourceManager * resources) { if (!var) return 0; return var->getMember(key, resources); } VariantData * getOrAddElement(size_t index, ResourceManager * resources) { auto array = isNull() ? &toArray() : asArray(); if (!array) return nullptr; return array->getOrAddElement(index, resources); } template VariantData * getOrAddMember(TAdaptedString key, ResourceManager * resources) { if (key.isNull()) return nullptr; auto obj = isNull() ? &toObject() : asObject(); if (!obj) return nullptr; return obj->getOrAddMember(key, resources); } 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; } bool isFloat() const { return (flags_ & NUMBER_BIT) != 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 isNull() const { return type() == VALUE_IS_NULL; } static bool isNull(const VariantData * var) { if (!var) return true; return var->isNull(); } bool isObject() const { return (flags_ & VALUE_IS_OBJECT) != 0; } bool isString() const { return type() == VALUE_IS_LINKED_STRING || type() == VALUE_IS_OWNED_STRING; } size_t nesting(const ResourceManager * resources) const { auto collection = asCollection(); if (collection) return collection->nesting(resources); else return 0; } static size_t nesting(const VariantData * var, const ResourceManager * resources) { if (!var) return 0; return var->nesting(resources); } void removeElement(size_t index, ResourceManager * resources) { ArrayData::removeElement(asArray(), index, resources); } static void removeElement(VariantData * var, size_t index, ResourceManager * resources) { if (!var) return; var->removeElement(index, resources); } template void removeMember(TAdaptedString key, ResourceManager * resources) { ObjectData::removeMember(asObject(), key, resources); } template static void removeMember(VariantData * var, TAdaptedString key, ResourceManager * resources) { if (!var) return; var->removeMember(key, resources); } void reset() { flags_ = VALUE_IS_NULL; } void setBoolean(bool value) { setType(VALUE_IS_BOOLEAN); content_.asBoolean = value; } void setBoolean(bool value, ResourceManager * resources) { release(resources); setBoolean(value); } void setFloat(JsonFloat value) { setType(VALUE_IS_FLOAT); content_.asFloat = value; } void setFloat(JsonFloat value, ResourceManager * resources) { release(resources); setFloat(value); } template enable_if_t::value> setInteger(T value) { setType(VALUE_IS_SIGNED_INTEGER); content_.asSignedInteger = value; } template enable_if_t::value> setInteger(T value) { setType(VALUE_IS_UNSIGNED_INTEGER); content_.asUnsignedInteger = static_cast(value); } template void setInteger(T value, ResourceManager * resources) { release(resources); setInteger(value); } void setNull() { setType(VALUE_IS_NULL); } void setNull(ResourceManager * resources) { release(resources); setNull(); } static void setNull(VariantData * var, ResourceManager * resources) { if (!var) return; var->setNull(resources); } void setRawString(StringNode * s) { ARDUINOJSON_ASSERT(s); setType(VALUE_IS_RAW_STRING); content_.asOwnedString = s; } template void setRawString(SerializedValue value, ResourceManager * resources) { release(resources); auto dup = resources->saveString(adaptString(value.data(), value.size())); if (dup) setRawString(dup); else setNull(); } template static void setRawString(VariantData * var, SerializedValue value, ResourceManager * resources) { if (!var) return; var->setRawString(value, resources); } template void setString(TAdaptedString value, ResourceManager * resources) { setNull(resources); if (value.isNull()) return; if (value.isLinked()) { setLinkedString(value.data()); return; } auto dup = resources->saveString(value); if (dup) setOwnedString(dup); } template static void setString(VariantData * var, TAdaptedString value, ResourceManager * resources) { if (!var) return; var->setString(value, resources); } void setLinkedString(const char * s) { ARDUINOJSON_ASSERT(s); setType(VALUE_IS_LINKED_STRING); content_.asLinkedString = s; } void setOwnedString(StringNode * s) { ARDUINOJSON_ASSERT(s); setType(VALUE_IS_OWNED_STRING); content_.asOwnedString = s; } size_t size(const ResourceManager * resources) const { return isCollection() ? content_.asCollection.size(resources) : 0; } static size_t size(const VariantData * var, const ResourceManager * resources) { return var != 0 ? var->size(resources) : 0; } ArrayData & toArray() { setType(VALUE_IS_ARRAY); new (&content_.asArray) ArrayData(); return content_.asArray; } ArrayData & toArray(ResourceManager * resources) { release(resources); return toArray(); } static ArrayData * toArray(VariantData * var, ResourceManager * resources) { if (!var) return 0; return &var->toArray(resources); } ObjectData & toObject() { setType(VALUE_IS_OBJECT); new (&content_.asObject) ObjectData(); return content_.asObject; } ObjectData & toObject(ResourceManager * resources) { release(resources); return toObject(); } static ObjectData * toObject(VariantData * var, ResourceManager * resources) { if (!var) return 0; return &var->toObject(resources); } uint8_t type() const { return flags_ & VALUE_MASK; } private: void release(ResourceManager * resources) { if (flags_ & OWNED_VALUE_BIT) resources->dereferenceString(content_.asOwnedString->data); auto collection = asCollection(); if (collection) collection->clear(resources); } void setType(uint8_t t) { flags_ &= OWNED_KEY_BIT; flags_ |= t; } }; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class JsonArray; class JsonObject; class JsonVariant; ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template struct VariantTo {}; template <> struct VariantTo { typedef JsonArray type; }; template <> struct VariantTo { typedef JsonObject type; }; template <> struct VariantTo { typedef JsonVariant type; }; class VariantAttorney { public: template static auto getResourceManager(TClient & client) -> decltype(client.getResourceManager()) { return client.getResourceManager(); } template static auto getData(TClient & client) -> decltype(client.getData()) { return client.getData(); } template static VariantData * getOrCreateData(TClient & client) { return client.getOrCreateData(); } }; 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, enable_if_t::value && is_integral::value && sizeof(T1) < sizeof(T2)> * = 0) { return arithmeticCompare(static_cast(lhs), rhs); } template CompareResult arithmeticCompare(const T1 & lhs, const T2 & rhs, enable_if_t::value && is_integral::value && sizeof(T2) < sizeof(T1)> * = 0) { return arithmeticCompare(lhs, static_cast(rhs)); } template CompareResult arithmeticCompare(const T1 & lhs, const T2 & rhs, enable_if_t::value && is_integral::value && is_signed::value == is_signed::value && sizeof(T2) == sizeof(T1)> * = 0) { return arithmeticCompare(lhs, static_cast(rhs)); } template CompareResult arithmeticCompare( const T1 & lhs, const T2 & rhs, enable_if_t::value && is_integral::value && is_unsigned::value && is_signed::value && sizeof(T2) == sizeof(T1)> * = 0) { if (rhs < 0) return COMPARE_RESULT_GREATER; return arithmeticCompare(lhs, static_cast(rhs)); } template CompareResult arithmeticCompare( const T1 & lhs, const T2 & rhs, enable_if_t::value && is_integral::value && is_signed::value && is_unsigned::value && sizeof(T2) == sizeof(T1)> * = 0) { if (lhs < 0) return COMPARE_RESULT_LESS; return arithmeticCompare(static_cast(lhs), rhs); } template CompareResult arithmeticCompare(const T1 & lhs, const T2 & rhs, enable_if_t::value || is_floating_point::value> * = 0) { return arithmeticCompare(static_cast(lhs), static_cast(rhs)); } template CompareResult arithmeticCompareNegateLeft(JsonUInt, const T2 &, enable_if_t::value> * = 0) { return COMPARE_RESULT_LESS; } template CompareResult arithmeticCompareNegateLeft(JsonUInt lhs, const T2 & rhs, enable_if_t::value> * = 0) { if (rhs > 0) return COMPARE_RESULT_LESS; return arithmeticCompare(-rhs, static_cast(lhs)); } template CompareResult arithmeticCompareNegateRight(const T1 &, JsonUInt, enable_if_t::value> * = 0) { return COMPARE_RESULT_GREATER; } template CompareResult arithmeticCompareNegateRight(const T1 & lhs, JsonUInt rhs, enable_if_t::value> * = 0) { if (lhs > 0) return COMPARE_RESULT_GREATER; return arithmeticCompare(static_cast(rhs), -lhs); } struct VariantTag {}; template struct IsVariant : is_base_of {}; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class JsonVariantConst; ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template CompareResult compare(JsonVariantConst lhs, const T & rhs); // VariantCompare.cpp struct VariantOperatorTag {}; template struct VariantOperators : VariantOperatorTag { template friend enable_if_t::value && !is_array::value, T> 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 enable_if_t::value, JsonVariantConst> 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 enable_if_t::value, bool> 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 enable_if_t::value, bool> 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 enable_if_t::value, bool> 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 enable_if_t::value, bool> 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 enable_if_t::value, bool> 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 enable_if_t::value, bool> operator>=(TVariant lhs, const T & rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } }; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class JsonArray; class JsonObject; class JsonVariantConst : public detail::VariantTag, public detail::VariantOperators { friend class detail::VariantAttorney; template using ConversionSupported = detail::is_same::fromJson)>::arg1_type, JsonVariantConst>; public: JsonVariantConst() : data_(nullptr) , resources_(nullptr) { } explicit JsonVariantConst(const detail::VariantData * data, const detail::ResourceManager * resources) : data_(data) , resources_(resources) { } bool isNull() const { return detail::VariantData::isNull(data_); } bool isUnbound() const { return !data_; } size_t nesting() const { return detail::VariantData::nesting(data_, resources_); } size_t size() const { return detail::VariantData::size(data_, resources_); } template ::value, bool> = true> T as() const { return Converter::fromJson(*this); } template ::value, bool> = true> detail::InvalidConversion as() const; template detail::enable_if_t::value, bool> is() const { return Converter::checkJson(*this); } template detail::enable_if_t::value, bool> is() const { return false; } template operator T() const { return as(); } template detail::enable_if_t::value, JsonVariantConst> operator[](T index) const { return JsonVariantConst(detail::VariantData::getElement(data_, size_t(index), resources_), resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](const TString & key) const { return JsonVariantConst(detail::VariantData::getMember(data_, detail::adaptString(key), resources_), resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](TChar * key) const { return JsonVariantConst(detail::VariantData::getMember(data_, detail::adaptString(key), resources_), resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](const TVariant & key) const { if (key.template is()) return operator[](key.template as()); else return operator[](key.template as()); } template detail::enable_if_t::value, bool> containsKey(const TString & key) const { return detail::VariantData::getMember(getData(), detail::adaptString(key), resources_) != 0; } template detail::enable_if_t::value, bool> containsKey(TChar * key) const { return detail::VariantData::getMember(getData(), detail::adaptString(key), resources_) != 0; } template detail::enable_if_t::value, bool> containsKey(const TVariant & key) const { return containsKey(key.template as()); } ARDUINOJSON_DEPRECATED("always returns zero") size_t memoryUsage() const { return 0; } protected: const detail::VariantData * getData() const { return data_; } const detail::ResourceManager * getResourceManager() const { return resources_; } private: const detail::VariantData * data_; const detail::ResourceManager * resources_; }; class JsonVariant; ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template class ElementProxy; template class MemberProxy; template class VariantRefBase : public VariantTag { friend class VariantAttorney; public: void clear() const { VariantData::setNull(getOrCreateData(), getResourceManager()); } bool isNull() const { return VariantData::isNull(getData()); } bool isUnbound() const { return !getData(); } template T as() const; template ::value>> operator T() const { return as(); } template enable_if_t::value, JsonArray> to() const; template enable_if_t::value, JsonObject> to() const; template enable_if_t::value, JsonVariant> to() const; template FORCE_INLINE bool is() const; template bool set(const T & value) const { return doSet>>(value); } template bool set(T * value) const { return doSet>(value); } size_t size() const { return VariantData::size(getData(), getResourceManager()); } size_t nesting() const { return VariantData::nesting(getData(), getResourceManager()); } template enable_if_t::value, T> add() const { return add().template to(); } template enable_if_t::value, T> add() const; template bool add(const T & value) const { return detail::VariantData::addValue(getOrCreateData(), value, getResourceManager()); } template bool add(T * value) const { return detail::VariantData::addValue(getOrCreateData(), value, getResourceManager()); } void remove(size_t index) const { VariantData::removeElement(getData(), index, getResourceManager()); } template enable_if_t::value> remove(TChar * key) const { VariantData::removeMember(getData(), adaptString(key), getResourceManager()); } template enable_if_t::value> remove(const TString & key) const { VariantData::removeMember(getData(), adaptString(key), getResourceManager()); } template enable_if_t::value> remove(const TVariant & key) const { if (key.template is()) remove(key.template as()); else remove(key.template as()); } ElementProxy operator[](size_t index) const; template enable_if_t::value, bool> containsKey(const TString & key) const; template enable_if_t::value, bool> containsKey(TChar * key) const; template enable_if_t::value, bool> containsKey(const TVariant & key) const; template FORCE_INLINE enable_if_t::value, MemberProxy> operator[](const TString & key) const; template FORCE_INLINE enable_if_t::value, MemberProxy> operator[](TChar * key) const; template enable_if_t::value, JsonVariantConst> operator[](const TVariant & key) const { if (key.template is()) return operator[](key.template as()); else return operator[](key.template as()); } ARDUINOJSON_DEPRECATED("use add() instead") JsonVariant add() const; ARDUINOJSON_DEPRECATED("use add() instead") JsonArray createNestedArray() const; template ARDUINOJSON_DEPRECATED("use var[key].to() instead") JsonArray createNestedArray(TChar * key) const; template ARDUINOJSON_DEPRECATED("use var[key].to() instead") JsonArray createNestedArray(const TString & key) const; ARDUINOJSON_DEPRECATED("use add() instead") JsonObject createNestedObject() const; template ARDUINOJSON_DEPRECATED("use var[key].to() instead") JsonObject createNestedObject(TChar * key) const; template ARDUINOJSON_DEPRECATED("use var[key].to() instead") JsonObject createNestedObject(const TString & key) const; ARDUINOJSON_DEPRECATED("always returns zero") size_t memoryUsage() const { return 0; } ARDUINOJSON_DEPRECATED("performs a deep copy") void shallowCopy(JsonVariantConst src) const { set(src); } private: TDerived & derived() { return static_cast(*this); } const TDerived & derived() const { return static_cast(*this); } ResourceManager * getResourceManager() const { return VariantAttorney::getResourceManager(derived()); } VariantData * getData() const { return VariantAttorney::getData(derived()); } VariantData * getOrCreateData() const { return VariantAttorney::getOrCreateData(derived()); } FORCE_INLINE ArduinoJson::JsonVariant getVariant() const; FORCE_INLINE ArduinoJson::JsonVariantConst getVariantConst() const { return ArduinoJson::JsonVariantConst(getData(), getResourceManager()); } template FORCE_INLINE enable_if_t::value, T> getVariant() const { return getVariantConst(); } template FORCE_INLINE enable_if_t::value, T> getVariant() const { return getVariant(); } template bool doSet(T && value) const { return doSet(detail::forward(value), is_same::return_type, bool>{}); } template bool doSet(T && value, false_type) const; template bool doSet(T && value, true_type) const; ArduinoJson::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_) { } ElementProxy & operator=(const ElementProxy & src) { this->set(src); return *this; } template ElementProxy & operator=(const T & src) { this->set(src); return *this; } template ElementProxy & operator=(T * src) { this->set(src); return *this; } private: ResourceManager * getResourceManager() const { return VariantAttorney::getResourceManager(upstream_); } FORCE_INLINE VariantData * getData() const { return VariantData::getElement(VariantAttorney::getData(upstream_), index_, VariantAttorney::getResourceManager(upstream_)); } VariantData * getOrCreateData() const { auto data = VariantAttorney::getOrCreateData(upstream_); if (!data) return nullptr; return data->getOrAddElement(index_, VariantAttorney::getResourceManager(upstream_)); } TUpstream upstream_; size_t index_; }; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class JsonVariant : public detail::VariantRefBase, public detail::VariantOperators { friend class detail::VariantAttorney; public: JsonVariant() : data_(0) , resources_(0) { } JsonVariant(detail::VariantData * data, detail::ResourceManager * resources) : data_(data) , resources_(resources) { } private: detail::ResourceManager * getResourceManager() const { return resources_; } detail::VariantData * getData() const { return data_; } detail::VariantData * getOrCreateData() const { return data_; } detail::VariantData * data_; detail::ResourceManager * resources_; }; namespace detail { bool copyVariant(JsonVariant dst, JsonVariantConst src); } template <> struct Converter : private detail::VariantAttorney { static void toJson(JsonVariantConst src, JsonVariant dst) { copyVariant(dst, src); } static JsonVariant fromJson(JsonVariant src) { return src; } static bool checkJson(JsonVariant src) { auto data = getData(src); return !!data; } }; template <> struct Converter : private detail::VariantAttorney { static void toJson(JsonVariantConst src, JsonVariant dst) { copyVariant(dst, src); } static JsonVariantConst fromJson(JsonVariantConst src) { return JsonVariantConst(getData(src), getResourceManager(src)); } static bool checkJson(JsonVariantConst src) { auto data = getData(src); return !!data; } }; template class Ptr { public: Ptr(T value) : value_(value) { } T * operator->() { return &value_; } T & operator*() { return value_; } private: T value_; }; class JsonArrayIterator { friend class JsonArray; public: JsonArrayIterator() { } explicit JsonArrayIterator(detail::ArrayData::iterator iterator, detail::ResourceManager * resources) : iterator_(iterator) , resources_(resources) { } JsonVariant operator*() { return JsonVariant(iterator_.data(), resources_); } Ptr operator->() { return operator*(); } bool operator==(const JsonArrayIterator & other) const { return iterator_ == other.iterator_; } bool operator!=(const JsonArrayIterator & other) const { return iterator_ != other.iterator_; } JsonArrayIterator & operator++() { iterator_.next(resources_); return *this; } private: detail::ArrayData::iterator iterator_; detail::ResourceManager * resources_; }; class JsonArrayConstIterator { friend class JsonArray; public: JsonArrayConstIterator() { } explicit JsonArrayConstIterator(detail::ArrayData::iterator iterator, const detail::ResourceManager * resources) : iterator_(iterator) , resources_(resources) { } JsonVariantConst operator*() const { return JsonVariantConst(iterator_.data(), resources_); } Ptr operator->() { return operator*(); } bool operator==(const JsonArrayConstIterator & other) const { return iterator_ == other.iterator_; } bool operator!=(const JsonArrayConstIterator & other) const { return iterator_ != other.iterator_; } JsonArrayConstIterator & operator++() { iterator_.next(resources_); return *this; } private: detail::ArrayData::iterator iterator_; const detail::ResourceManager * resources_; }; class JsonObject; class JsonArrayConst : public detail::VariantOperators { friend class JsonArray; friend class detail::VariantAttorney; public: typedef JsonArrayConstIterator iterator; iterator begin() const { if (!data_) return iterator(); return iterator(data_->createIterator(resources_), resources_); } iterator end() const { return iterator(); } JsonArrayConst() : data_(0) , resources_(0) { } JsonArrayConst(const detail::ArrayData * data, const detail::ResourceManager * resources) : data_(data) , resources_(resources) { } template detail::enable_if_t::value, JsonVariantConst> operator[](T index) const { return JsonVariantConst(detail::ArrayData::getElement(data_, size_t(index), resources_), resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](const TVariant & variant) const { if (variant.template is()) return operator[](variant.template as()); else return JsonVariantConst(); } operator JsonVariantConst() const { return JsonVariantConst(getData(), resources_); } bool isNull() const { return data_ == 0; } operator bool() const { return data_ != 0; } size_t nesting() const { return detail::VariantData::nesting(getData(), resources_); } size_t size() const { return data_ ? data_->size(resources_) : 0; } ARDUINOJSON_DEPRECATED("always returns zero") size_t memoryUsage() const { return 0; } private: const detail::VariantData * getData() const { return collectionToVariant(data_); } const detail::ArrayData * data_; const detail::ResourceManager * resources_; }; inline bool operator==(JsonArrayConst lhs, JsonArrayConst rhs) { if (!lhs && !rhs) return true; if (!lhs || !rhs) return false; auto a = lhs.begin(); auto b = rhs.begin(); for (;;) { if (a == b) // same pointer or both null return true; if (a == lhs.end() || b == rhs.end()) return false; if (*a != *b) return false; ++a; ++b; } } class JsonObject; class JsonArray : public detail::VariantOperators { friend class detail::VariantAttorney; public: typedef JsonArrayIterator iterator; JsonArray() : data_(0) , resources_(0) { } JsonArray(detail::ArrayData * data, detail::ResourceManager * resources) : data_(data) , resources_(resources) { } operator JsonVariant() { void * data = data_; // prevent warning cast-align return JsonVariant(reinterpret_cast(data), resources_); } operator JsonArrayConst() const { return JsonArrayConst(data_, resources_); } template detail::enable_if_t::value, T> add() const { return add().to(); } template detail::enable_if_t::value, T> add() const { return JsonVariant(detail::ArrayData::addElement(data_, resources_), resources_); } template bool add(const T & value) const { return detail::ArrayData::addValue(data_, value, resources_); } template bool add(T * value) const { return detail::ArrayData::addValue(data_, value, resources_); } iterator begin() const { if (!data_) return iterator(); return iterator(data_->createIterator(resources_), resources_); } iterator end() const { return iterator(); } bool set(JsonArrayConst src) const { if (!data_) return false; clear(); for (auto element : src) { if (!add(element)) return false; } return true; } void remove(iterator it) const { detail::ArrayData::remove(data_, it.iterator_, resources_); } void remove(size_t index) const { detail::ArrayData::removeElement(data_, index, resources_); } template detail::enable_if_t::value> remove(TVariant variant) const { if (variant.template is()) remove(variant.template as()); } void clear() const { detail::ArrayData::clear(data_, resources_); } template detail::enable_if_t::value, detail::ElementProxy> operator[](T index) const { return {*this, size_t(index)}; } template detail::enable_if_t::value, detail::ElementProxy> operator[](const TVariant & variant) const { if (variant.template is()) return operator[](variant.template as()); else return {*this, size_t(-1)}; } operator JsonVariantConst() const { return JsonVariantConst(collectionToVariant(data_), resources_); } bool isNull() const { return data_ == 0; } operator bool() const { return data_ != 0; } size_t nesting() const { return detail::VariantData::nesting(collectionToVariant(data_), resources_); } size_t size() const { return data_ ? data_->size(resources_) : 0; } ARDUINOJSON_DEPRECATED("use add() instead") JsonVariant add() const { return add(); } ARDUINOJSON_DEPRECATED("use add() instead") JsonArray createNestedArray() const { return add(); } ARDUINOJSON_DEPRECATED("use add() instead") JsonObject createNestedObject() const; ARDUINOJSON_DEPRECATED("always returns zero") size_t memoryUsage() const { return 0; } private: detail::ResourceManager * getResourceManager() const { return resources_; } detail::VariantData * getData() const { return collectionToVariant(data_); } detail::VariantData * getOrCreateData() const { return collectionToVariant(data_); } detail::ArrayData * data_; detail::ResourceManager * resources_; }; class JsonPair { public: JsonPair(detail::ObjectData::iterator iterator, detail::ResourceManager * resources) : iterator_(iterator) , resources_(resources) { } JsonString key() const { if (!iterator_.done()) return JsonString(iterator_.key(), iterator_.ownsKey() ? JsonString::Copied : JsonString::Linked); else return JsonString(); } JsonVariant value() { return JsonVariant(iterator_.data(), resources_); } private: detail::ObjectData::iterator iterator_; detail::ResourceManager * resources_; }; class JsonPairConst { public: JsonPairConst(detail::ObjectData::iterator iterator, const detail::ResourceManager * resources) : iterator_(iterator) , resources_(resources) { } JsonString key() const { if (!iterator_.done()) return JsonString(iterator_.key(), iterator_.ownsKey() ? JsonString::Copied : JsonString::Linked); else return JsonString(); } JsonVariantConst value() const { return JsonVariantConst(iterator_.data(), resources_); } private: detail::ObjectData::iterator iterator_; const detail::ResourceManager * resources_; }; class JsonObjectIterator { friend class JsonObject; public: JsonObjectIterator() { } explicit JsonObjectIterator(detail::ObjectData::iterator iterator, detail::ResourceManager * resources) : iterator_(iterator) , resources_(resources) { } JsonPair operator*() const { return JsonPair(iterator_, resources_); } Ptr operator->() { return operator*(); } bool operator==(const JsonObjectIterator & other) const { return iterator_ == other.iterator_; } bool operator!=(const JsonObjectIterator & other) const { return iterator_ != other.iterator_; } JsonObjectIterator & operator++() { iterator_.next(resources_); return *this; } private: detail::ObjectData::iterator iterator_; detail::ResourceManager * resources_; }; class JsonObjectConstIterator { friend class JsonObject; public: JsonObjectConstIterator() { } explicit JsonObjectConstIterator(detail::ObjectData::iterator iterator, const detail::ResourceManager * resources) : iterator_(iterator) , resources_(resources) { } JsonPairConst operator*() const { return JsonPairConst(iterator_, resources_); } Ptr operator->() { return operator*(); } bool operator==(const JsonObjectConstIterator & other) const { return iterator_ == other.iterator_; } bool operator!=(const JsonObjectConstIterator & other) const { return iterator_ != other.iterator_; } JsonObjectConstIterator & operator++() { iterator_.next(resources_); return *this; } private: detail::ObjectData::iterator iterator_; const detail::ResourceManager * resources_; }; class JsonObjectConst : public detail::VariantOperators { friend class JsonObject; friend class detail::VariantAttorney; public: typedef JsonObjectConstIterator iterator; JsonObjectConst() : data_(0) , resources_(0) { } JsonObjectConst(const detail::ObjectData * data, const detail::ResourceManager * resources) : data_(data) , resources_(resources) { } operator JsonVariantConst() const { return JsonVariantConst(getData(), resources_); } bool isNull() const { return data_ == 0; } operator bool() const { return data_ != 0; } size_t nesting() const { return detail::VariantData::nesting(getData(), resources_); } size_t size() const { return data_ ? data_->size(resources_) : 0; } iterator begin() const { if (!data_) return iterator(); return iterator(data_->createIterator(resources_), resources_); } iterator end() const { return iterator(); } template detail::enable_if_t::value, bool> containsKey(const TString & key) const { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } template bool containsKey(TChar * key) const { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } template detail::enable_if_t::value, bool> containsKey(const TVariant & key) const { return containsKey(key.template as()); } template detail::enable_if_t::value, JsonVariantConst> operator[](const TString & key) const { return JsonVariantConst(detail::ObjectData::getMember(data_, detail::adaptString(key), resources_), resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](TChar * key) const { return JsonVariantConst(detail::ObjectData::getMember(data_, detail::adaptString(key), resources_), resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](const TVariant & key) const { if (key.template is()) return operator[](key.template as()); else return JsonVariantConst(); } ARDUINOJSON_DEPRECATED("always returns zero") size_t memoryUsage() const { return 0; } private: const detail::VariantData * getData() const { return collectionToVariant(data_); } const detail::ObjectData * data_; const detail::ResourceManager * resources_; }; inline bool operator==(JsonObjectConst lhs, JsonObjectConst rhs) { if (!lhs && !rhs) // both are null return true; if (!lhs || !rhs) // only one is null return false; size_t count = 0; for (auto kvp : lhs) { auto rhsValue = rhs[kvp.key()]; if (rhsValue.isUnbound()) return false; if (kvp.value() != rhsValue) return false; count++; } return count == rhs.size(); } ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template class MemberProxy : public VariantRefBase>, public VariantOperators> { friend class VariantAttorney; public: MemberProxy(TUpstream upstream, TStringRef key) : upstream_(upstream) , key_(key) { } MemberProxy(const MemberProxy & src) : upstream_(src.upstream_) , key_(src.key_) { } MemberProxy & operator=(const MemberProxy & src) { this->set(src); return *this; } template MemberProxy & operator=(const T & src) { this->set(src); return *this; } template MemberProxy & operator=(T * src) { this->set(src); return *this; } private: ResourceManager * getResourceManager() const { return VariantAttorney::getResourceManager(upstream_); } VariantData * getData() const { return VariantData::getMember(VariantAttorney::getData(upstream_), adaptString(key_), VariantAttorney::getResourceManager(upstream_)); } VariantData * getOrCreateData() const { auto data = VariantAttorney::getOrCreateData(upstream_); if (!data) return nullptr; return data->getOrAddMember(adaptString(key_), VariantAttorney::getResourceManager(upstream_)); } private: TUpstream upstream_; TStringRef key_; }; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class JsonArray; class JsonObject : public detail::VariantOperators { friend class detail::VariantAttorney; public: typedef JsonObjectIterator iterator; JsonObject() : data_(0) , resources_(0) { } JsonObject(detail::ObjectData * data, detail::ResourceManager * resource) : data_(data) , resources_(resource) { } operator JsonVariant() const { void * data = data_; // prevent warning cast-align return JsonVariant(reinterpret_cast(data), resources_); } operator JsonObjectConst() const { return JsonObjectConst(data_, resources_); } operator JsonVariantConst() const { return JsonVariantConst(collectionToVariant(data_), resources_); } bool isNull() const { return data_ == 0; } operator bool() const { return data_ != 0; } size_t nesting() const { return detail::VariantData::nesting(collectionToVariant(data_), resources_); } size_t size() const { return data_ ? data_->size(resources_) : 0; } iterator begin() const { if (!data_) return iterator(); return iterator(data_->createIterator(resources_), resources_); } iterator end() const { return iterator(); } void clear() const { detail::ObjectData::clear(data_, resources_); } bool set(JsonObjectConst src) { if (!data_ || !src.data_) return false; clear(); for (auto kvp : src) { if (!operator[](kvp.key()).set(kvp.value())) return false; } return true; } template detail::enable_if_t::value, detail::MemberProxy> operator[](const TString & key) const { return {*this, key}; } template detail::enable_if_t::value, detail::MemberProxy> operator[](TChar * key) const { return {*this, key}; } template detail::enable_if_t::value, detail::MemberProxy> operator[](const TVariant & key) const { if (key.template is()) return {*this, key.template as()}; else return {*this, nullptr}; } FORCE_INLINE void remove(iterator it) const { detail::ObjectData::remove(data_, it.iterator_, resources_); } template detail::enable_if_t::value> remove(const TString & key) const { detail::ObjectData::removeMember(data_, detail::adaptString(key), resources_); } template detail::enable_if_t::value> remove(const TVariant & key) const { if (key.template is()) remove(key.template as()); } template FORCE_INLINE void remove(TChar * key) const { detail::ObjectData::removeMember(data_, detail::adaptString(key), resources_); } template detail::enable_if_t::value, bool> containsKey(const TString & key) const { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } template detail::enable_if_t::value, bool> containsKey(TChar * key) const { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } template detail::enable_if_t::value, bool> containsKey(const TVariant & key) const { return containsKey(key.template as()); } template ARDUINOJSON_DEPRECATED("use obj[key].to() instead") JsonArray createNestedArray(TChar * key) const { return operator[](key).template to(); } template ARDUINOJSON_DEPRECATED("use obj[key].to() instead") JsonArray createNestedArray(const TString & key) const { return operator[](key).template to(); } template ARDUINOJSON_DEPRECATED("use obj[key].to() instead") JsonObject createNestedObject(TChar * key) { return operator[](key).template to(); } template ARDUINOJSON_DEPRECATED("use obj[key].to() instead") JsonObject createNestedObject(const TString & key) { return operator[](key).template to(); } ARDUINOJSON_DEPRECATED("always returns zero") size_t memoryUsage() const { return 0; } private: detail::ResourceManager * getResourceManager() const { return resources_; } detail::VariantData * getData() const { return detail::collectionToVariant(data_); } detail::VariantData * getOrCreateData() const { return detail::collectionToVariant(data_); } detail::ObjectData * data_; detail::ResourceManager * resources_; }; class JsonDocument : public detail::VariantOperators { friend class detail::VariantAttorney; public: explicit JsonDocument(Allocator * alloc = detail::DefaultAllocator::instance()) : resources_(alloc) { } JsonDocument(const JsonDocument & src) : JsonDocument(src.allocator()) { set(src); } JsonDocument(JsonDocument && src) : JsonDocument(detail::DefaultAllocator::instance()) { swap(*this, src); } template JsonDocument(const T & src, Allocator * alloc = detail::DefaultAllocator::instance(), detail::enable_if_t::value || detail::is_same::value || detail::is_same::value || detail::is_same::value || detail::is_same::value> * = 0) : JsonDocument(alloc) { set(src); } JsonDocument & operator=(JsonDocument src) { swap(*this, src); return *this; } template JsonDocument & operator=(const T & src) { set(src); return *this; } Allocator * allocator() const { return resources_.allocator(); } void shrinkToFit() { resources_.shrinkToFit(); } template T as() { return getVariant().template as(); } template T as() const { return getVariant().template as(); } void clear() { resources_.clear(); data_.reset(); } template bool is() { return getVariant().template is(); } template bool is() const { return getVariant().template is(); } bool isNull() const { return getVariant().isNull(); } bool overflowed() const { return resources_.overflowed(); } size_t nesting() const { return data_.nesting(&resources_); } size_t size() const { return data_.size(&resources_); } bool set(const JsonDocument & src) { return to().set(src.as()); } template detail::enable_if_t::value, bool> set(const T & src) { return to().set(src); } template typename detail::VariantTo::type to() { clear(); return getVariant().template to(); } template bool containsKey(TChar * key) const { return data_.getMember(detail::adaptString(key), &resources_) != 0; } template detail::enable_if_t::value, bool> containsKey(const TString & key) const { return data_.getMember(detail::adaptString(key), &resources_) != 0; } template detail::enable_if_t::value, bool> containsKey(const TVariant & key) const { return containsKey(key.template as()); } template detail::enable_if_t::value, detail::MemberProxy> operator[](const TString & key) { return {*this, key}; } template detail::enable_if_t::value, detail::MemberProxy> operator[](TChar * key) { return {*this, key}; } template detail::enable_if_t::value, JsonVariantConst> operator[](const TString & key) const { return JsonVariantConst(data_.getMember(detail::adaptString(key), &resources_), &resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](TChar * key) const { return JsonVariantConst(data_.getMember(detail::adaptString(key), &resources_), &resources_); } template detail::enable_if_t::value, detail::ElementProxy> operator[](T index) { return {*this, size_t(index)}; } JsonVariantConst operator[](size_t index) const { return JsonVariantConst(data_.getElement(index, &resources_), &resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](const TVariant & key) const { if (key.template is()) return operator[](key.template as()); if (key.template is()) return operator[](key.template as()); return {}; } template detail::enable_if_t::value, T> add() { return add().to(); } template detail::enable_if_t::value, T> add() { return JsonVariant(data_.addElement(&resources_), &resources_); } template bool add(const TValue & value) { return data_.addValue(value, &resources_); } template bool add(TChar * value) { return data_.addValue(value, &resources_); } template detail::enable_if_t::value> remove(T index) { detail::VariantData::removeElement(getData(), size_t(index), getResourceManager()); } template detail::enable_if_t::value> remove(TChar * key) { detail::VariantData::removeMember(getData(), detail::adaptString(key), getResourceManager()); } template detail::enable_if_t::value> remove(const TString & key) { detail::VariantData::removeMember(getData(), detail::adaptString(key), getResourceManager()); } template detail::enable_if_t::value> remove(const TVariant & key) { if (key.template is()) remove(key.template as()); if (key.template is()) remove(key.template as()); } operator JsonVariant() { return getVariant(); } operator JsonVariantConst() const { return getVariant(); } friend void swap(JsonDocument & a, JsonDocument & b) { swap(a.resources_, b.resources_); swap_(a.data_, b.data_); } ARDUINOJSON_DEPRECATED("use add() instead") JsonVariant add() { return add(); } ARDUINOJSON_DEPRECATED("use add() instead") JsonArray createNestedArray() { return add(); } template ARDUINOJSON_DEPRECATED("use doc[key].to() instead") JsonArray createNestedArray(TChar * key) { return operator[](key).template to(); } template ARDUINOJSON_DEPRECATED("use doc[key].to() instead") JsonArray createNestedArray(const TString & key) { return operator[](key).template to(); } ARDUINOJSON_DEPRECATED("use add() instead") JsonObject createNestedObject() { return add(); } template ARDUINOJSON_DEPRECATED("use doc[key].to() instead") JsonObject createNestedObject(TChar * key) { return operator[](key).template to(); } template ARDUINOJSON_DEPRECATED("use doc[key].to() instead") JsonObject createNestedObject(const TString & key) { return operator[](key).template to(); } ARDUINOJSON_DEPRECATED("always returns zero") size_t memoryUsage() const { return 0; } private: JsonVariant getVariant() { return JsonVariant(&data_, &resources_); } JsonVariantConst getVariant() const { return JsonVariantConst(&data_, &resources_); } detail::ResourceManager * getResourceManager() { return &resources_; } detail::VariantData * getData() { return &data_; } const detail::VariantData * getData() const { return &data_; } detail::VariantData * getOrCreateData() { return &data_; } detail::ResourceManager resources_; detail::VariantData data_; }; inline void convertToJson(const JsonDocument & src, JsonVariant dst) { dst.set(src.as()); } ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template struct VariantDataVisitor { typedef TResult result_type; template TResult visit(const T &) { return TResult(); } }; template struct JsonVariantVisitor { typedef TResult result_type; template TResult visit(const T &) { return TResult(); } }; template class VisitorAdapter { public: using result_type = typename TVisitor::result_type; VisitorAdapter(TVisitor & visitor, const ResourceManager * resources) : visitor_(&visitor) , resources_(resources) { } result_type visit(const ArrayData & value) { return visitor_->visit(JsonArrayConst(&value, resources_)); } result_type visit(const ObjectData & value) { return visitor_->visit(JsonObjectConst(&value, resources_)); } template result_type visit(const T & value) { return visitor_->visit(value); } private: TVisitor * visitor_; const ResourceManager * resources_; }; template typename TVisitor::result_type accept(JsonVariantConst variant, TVisitor & visit) { auto data = VariantAttorney::getData(variant); if (!data) return visit.visit(nullptr); auto resources = VariantAttorney::getResourceManager(variant); VisitorAdapter adapter(visit, resources); return data->accept(adapter); } struct ComparerBase : JsonVariantVisitor {}; template struct Comparer; template struct Comparer::value>> : ComparerBase { T rhs; explicit Comparer(T value) : rhs(value) { } CompareResult visit(JsonString lhs) { int i = stringCompare(adaptString(rhs), adaptString(lhs)); if (i < 0) return COMPARE_RESULT_GREATER; else if (i > 0) return COMPARE_RESULT_LESS; else return COMPARE_RESULT_EQUAL; } CompareResult visit(nullptr_t) { if (adaptString(rhs).isNull()) return COMPARE_RESULT_EQUAL; else return COMPARE_RESULT_DIFFER; } using ComparerBase::visit; }; template struct Comparer::value || is_floating_point::value>> : ComparerBase { T rhs; explicit Comparer(T value) : rhs(value) { } CompareResult visit(JsonFloat lhs) { return arithmeticCompare(lhs, rhs); } CompareResult visit(JsonInteger lhs) { return arithmeticCompare(lhs, rhs); } CompareResult visit(JsonUInt lhs) { return arithmeticCompare(lhs, rhs); } CompareResult visit(bool lhs) { return visit(static_cast(lhs)); } using ComparerBase::visit; }; struct NullComparer : ComparerBase { CompareResult visit(nullptr_t) { return COMPARE_RESULT_EQUAL; } using ComparerBase::visit; }; template <> struct Comparer : NullComparer { explicit Comparer(nullptr_t) : NullComparer() { } }; struct ArrayComparer : ComparerBase { JsonArrayConst rhs_; explicit ArrayComparer(JsonArrayConst rhs) : rhs_(rhs) { } CompareResult visit(JsonArrayConst lhs) { if (rhs_ == lhs) return COMPARE_RESULT_EQUAL; else return COMPARE_RESULT_DIFFER; } using ComparerBase::visit; }; struct ObjectComparer : ComparerBase { JsonObjectConst rhs_; explicit ObjectComparer(JsonObjectConst rhs) : rhs_(rhs) { } CompareResult visit(JsonObjectConst lhs) { if (lhs == rhs_) return COMPARE_RESULT_EQUAL; else return COMPARE_RESULT_DIFFER; } using ComparerBase::visit; }; struct RawComparer : ComparerBase { RawString rhs_; explicit RawComparer(RawString rhs) : rhs_(rhs) { } CompareResult visit(RawString lhs) { size_t size = rhs_.size() < lhs.size() ? rhs_.size() : lhs.size(); int n = memcmp(lhs.data(), rhs_.data(), size); if (n < 0) return COMPARE_RESULT_LESS; else if (n > 0) return COMPARE_RESULT_GREATER; else return COMPARE_RESULT_EQUAL; } using ComparerBase::visit; }; struct VariantComparer : ComparerBase { JsonVariantConst rhs; explicit VariantComparer(JsonVariantConst value) : rhs(value) { } CompareResult visit(JsonArrayConst lhs) { ArrayComparer comparer(lhs); return reverseResult(comparer); } CompareResult visit(JsonObjectConst lhs) { ObjectComparer comparer(lhs); return reverseResult(comparer); } CompareResult visit(JsonFloat lhs) { Comparer comparer(lhs); return reverseResult(comparer); } CompareResult visit(JsonString lhs) { Comparer comparer(lhs); return reverseResult(comparer); } CompareResult visit(RawString value) { RawComparer comparer(value); return reverseResult(comparer); } CompareResult visit(JsonInteger lhs) { Comparer comparer(lhs); return reverseResult(comparer); } CompareResult visit(JsonUInt lhs) { Comparer comparer(lhs); return reverseResult(comparer); } CompareResult visit(bool lhs) { Comparer comparer(lhs); return reverseResult(comparer); } CompareResult visit(nullptr_t) { NullComparer comparer; return reverseResult(comparer); } private: template CompareResult reverseResult(TComparer & comparer) { CompareResult reversedResult = accept(rhs, comparer); switch (reversedResult) { case COMPARE_RESULT_GREATER: return COMPARE_RESULT_LESS; case COMPARE_RESULT_LESS: return COMPARE_RESULT_GREATER; default: return reversedResult; } } }; template struct Comparer::value>> : VariantComparer { explicit Comparer(const T & value) : VariantComparer(static_cast(value)) { } }; template CompareResult compare(ArduinoJson::JsonVariantConst lhs, const T & rhs) { Comparer comparer(rhs); return accept(lhs, comparer); } inline ArrayData::iterator ArrayData::at(size_t index, const ResourceManager * resources) const { auto it = createIterator(resources); while (!it.done() && index) { it.next(resources); --index; } return it; } inline VariantData * ArrayData::getOrAddElement(size_t index, ResourceManager * resources) { auto it = createIterator(resources); while (!it.done() && index > 0) { it.next(resources); index--; } if (it.done()) index++; VariantData * element = it.data(); while (index > 0) { element = addElement(resources); if (!element) return nullptr; index--; } return element; } inline VariantData * ArrayData::getElement(size_t index, const ResourceManager * resources) const { return at(index, resources).data(); } inline void ArrayData::removeElement(size_t index, ResourceManager * resources) { remove(at(index, resources), resources); } template inline bool ArrayData::addValue(T && value, ResourceManager * resources) { ARDUINOJSON_ASSERT(resources != nullptr); auto slot = resources->allocSlot(); if (!slot) return false; JsonVariant variant(slot->data(), resources); if (!variant.set(detail::forward(value))) { resources->freeSlot(slot); return false; } addSlot(slot, resources); return true; } ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE template inline detail::enable_if_t::value, bool> copyArray(const T & src, JsonVariant dst) { return dst.set(src); } template inline detail::enable_if_t::value, bool> copyArray(T (&src)[N], const TDestination & dst) { return copyArray(src, N, dst); } template inline detail::enable_if_t::value, bool> 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.template 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 detail::enable_if_t::value, size_t> 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 detail::enable_if_t::value && detail::is_base_of::value, size_t> copyArray(const TSource & src, T & dst) { return copyArray(src.template as(), dst); } ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_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); } inline CollectionIterator::CollectionIterator(VariantSlot * slot, SlotId slotId) : slot_(slot) , currentId_(slotId) { nextId_ = slot_ ? slot_->next() : NULL_SLOT; } inline const char * CollectionIterator::key() const { ARDUINOJSON_ASSERT(slot_ != nullptr); return slot_->key(); } inline void CollectionIterator::setKey(const char * s) { ARDUINOJSON_ASSERT(slot_ != nullptr); ARDUINOJSON_ASSERT(s != nullptr); return slot_->setKey(s); } inline void CollectionIterator::setKey(StringNode * s) { ARDUINOJSON_ASSERT(slot_ != nullptr); ARDUINOJSON_ASSERT(s != nullptr); return slot_->setKey(s); } inline bool CollectionIterator::ownsKey() const { ARDUINOJSON_ASSERT(slot_ != nullptr); return slot_->ownsKey(); } inline void CollectionIterator::next(const ResourceManager * resources) { ARDUINOJSON_ASSERT(currentId_ != NULL_SLOT); slot_ = resources->getSlot(nextId_); currentId_ = nextId_; if (slot_) nextId_ = slot_->next(); } inline CollectionData::iterator CollectionData::addSlot(ResourceManager * resources) { auto slot = resources->allocSlot(); if (!slot) return {}; if (tail_ != NULL_SLOT) { auto tail = resources->getSlot(tail_); tail->setNext(slot.id()); tail_ = slot.id(); } else { head_ = slot.id(); tail_ = slot.id(); } return iterator(slot, slot.id()); } inline void CollectionData::addSlot(SlotWithId slot, ResourceManager * resources) { if (tail_ != NULL_SLOT) { auto tail = resources->getSlot(tail_); tail->setNext(slot.id()); tail_ = slot.id(); } else { head_ = slot.id(); tail_ = slot.id(); } } inline void CollectionData::clear(ResourceManager * resources) { auto next = head_; while (next != NULL_SLOT) { auto currId = next; auto slot = resources->getSlot(next); next = slot->next(); resources->freeSlot(SlotWithId(slot, currId)); } head_ = NULL_SLOT; tail_ = NULL_SLOT; } inline SlotWithId CollectionData::getPreviousSlot(VariantSlot * target, const ResourceManager * resources) const { auto prev = SlotWithId(); auto currentId = head_; while (currentId != NULL_SLOT) { auto currentSlot = resources->getSlot(currentId); if (currentSlot == target) return prev; prev = SlotWithId(currentSlot, currentId); currentId = currentSlot->next(); } return SlotWithId(); } inline void CollectionData::remove(iterator it, ResourceManager * resources) { if (it.done()) return; auto curr = it.slot_; auto prev = getPreviousSlot(curr, resources); auto next = curr->next(); if (prev) prev->setNext(next); else head_ = next; if (next == NULL_SLOT) tail_ = prev.id(); resources->freeSlot({it.slot_, it.currentId_}); } inline size_t CollectionData::nesting(const ResourceManager * resources) const { size_t maxChildNesting = 0; for (auto it = createIterator(resources); !it.done(); it.next(resources)) { size_t childNesting = it->nesting(resources); if (childNesting > maxChildNesting) maxChildNesting = childNesting; } return maxChildNesting + 1; } inline size_t CollectionData::size(const ResourceManager * resources) const { size_t count = 0; for (auto it = createIterator(resources); !it.done(); it.next(resources)) count++; return count; } inline void ResourceManager::freeSlot(SlotWithId slot) { if (slot->ownsKey()) dereferenceString(slot->key()); slot->data()->setNull(this); variantPools_.freeSlot(slot); } inline void VariantPool::create(SlotCount cap, Allocator * allocator) { ARDUINOJSON_ASSERT(cap > 0); slots_ = reinterpret_cast(allocator->allocate(slotsToBytes(cap))); capacity_ = slots_ ? cap : 0; usage_ = 0; } inline void VariantPool::destroy(Allocator * allocator) { if (slots_) allocator->deallocate(slots_); slots_ = nullptr; capacity_ = 0; usage_ = 0; } inline void VariantPool::shrinkToFit(Allocator * allocator) { auto newSlots = reinterpret_cast(allocator->reallocate(slots_, slotsToBytes(usage_))); if (newSlots) { slots_ = newSlots; capacity_ = usage_; } } inline SlotWithId VariantPool::allocSlot() { if (!slots_) return {}; if (usage_ >= capacity_) return {}; auto index = usage_++; auto slot = &slots_[index]; return {new (slot) VariantSlot, SlotId(index)}; } inline VariantSlot * VariantPool::getSlot(SlotId id) const { ARDUINOJSON_ASSERT(id < usage_); return &slots_[id]; } inline SlotCount VariantPool::usage() const { return usage_; } inline void VariantPool::clear() { usage_ = 0; } inline SlotCount VariantPool::bytesToSlots(size_t n) { return static_cast(n / sizeof(VariantSlot)); } inline size_t VariantPool::slotsToBytes(SlotCount n) { return n * sizeof(VariantSlot); } inline SlotWithId VariantPoolList::allocFromFreeList() { ARDUINOJSON_ASSERT(freeList_ != NULL_SLOT); auto id = freeList_; auto slot = getSlot(freeList_); freeList_ = slot->next(); return {new (slot) VariantSlot, id}; } inline void VariantPoolList::freeSlot(SlotWithId slot) { slot->setNext(freeList_); freeList_ = slot.id(); } template inline VariantData * ObjectData::getMember(TAdaptedString key, const ResourceManager * resources) const { return findKey(key, resources).data(); } template VariantData * ObjectData::getOrAddMember(TAdaptedString key, ResourceManager * resources) { auto it = findKey(key, resources); if (!it.done()) return it.data(); return addMember(key, resources); } template inline ObjectData::iterator ObjectData::findKey(TAdaptedString key, const ResourceManager * resources) const { if (key.isNull()) return iterator(); for (auto it = createIterator(resources); !it.done(); it.next(resources)) { if (stringEquals(key, adaptString(it.key()))) return it; } return iterator(); } template inline void ObjectData::removeMember(TAdaptedString key, ResourceManager * resources) { remove(findKey(key, resources), resources); } 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::positiveBinaryPowersOfTen()[index]) { value *= traits::negativeBinaryPowersOfTen()[index]; powersOf10 = int16_t(powersOf10 + bit); } bit >>= 1; } } if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) { for (; index >= 0; index--) { if (value < traits::negativeBinaryPowersOfTen()[index] * 10) { value *= traits::positiveBinaryPowersOfTen()[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) { } TextFormatter & operator=(const TextFormatter &) = delete; 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 enable_if_t::value> writeInteger(T value) { typedef make_unsigned_t 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 enable_if_t::value> 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_; }; class DummyWriter { public: size_t write(uint8_t) { return 1; } size_t write(const uint8_t *, size_t n) { return n; } }; template