// 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 // up to 255 slots # elif ARDUINOJSON_SIZEOF_POINTER == 4 # define ARDUINOJSON_SLOT_ID_SIZE 2 // up to 65535 slots # else # define ARDUINOJSON_SLOT_ID_SIZE 4 // up to 4294967295 slots # 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_CONCAT4(A, B, C, D) \ ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT2(A, B), ARDUINOJSON_CONCAT2(C, D)) #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.0.4" #define ARDUINOJSON_VERSION_MAJOR 7 #define ARDUINOJSON_VERSION_MINOR 0 #define ARDUINOJSON_VERSION_REVISION 4 #define ARDUINOJSON_VERSION_MACRO V704 #ifndef ARDUINOJSON_VERSION_NAMESPACE # define ARDUINOJSON_VERSION_NAMESPACE \ ARDUINOJSON_CONCAT4(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) #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/ template struct ConverterNeedsWriteableRef; 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_t; template <> struct uint_t<8> { typedef uint8_t type; }; template <> struct uint_t<16> { typedef uint16_t type; }; template <> struct uint_t<32> { typedef uint32_t type; }; template struct conditional { typedef TrueType type; }; template struct conditional { typedef FalseType type; }; template struct enable_if {}; template struct enable_if { typedef T type; }; template struct integral_constant { static const T value = v; }; typedef integral_constant true_type; typedef integral_constant false_type; template struct is_array : false_type {}; template struct is_array : true_type {}; template struct is_array : true_type {}; template struct remove_reference { typedef T type; }; template struct remove_reference { typedef T 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::type*>( 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 struct is_floating_point : integral_constant< bool, // is_same::type>::value || is_same::type>::value> {}; template struct is_integral : integral_constant::type, signed char>::value || is_same::type, unsigned char>::value || is_same::type, signed short>::value || is_same::type, unsigned short>::value || is_same::type, signed int>::value || is_same::type, unsigned int>::value || is_same::type, signed long>::value || is_same::type, unsigned long>::value || is_same::type, signed long long>::value || is_same::type, unsigned long long>::value || is_same::type, char>::value || is_same::type, bool>::value> {}; template struct is_enum { static const bool value = is_convertible::value && !is_class::value && !is_integral::value && !is_floating_point::value; }; template struct is_pointer : false_type {}; template struct is_pointer : true_type {}; template struct is_signed : integral_constant::type, char>::value || is_same::type, signed char>::value || is_same::type, signed short>::value || is_same::type, signed int>::value || is_same::type, signed long>::value || is_same::type, signed long long>::value || is_same::type, float>::value || is_same::type, double>::value> {}; template struct is_unsigned : integral_constant::type, unsigned char>::value || is_same::type, unsigned short>::value || is_same::type, unsigned int>::value || is_same::type, unsigned long>::value || is_same::type, unsigned long long>::value || is_same::type, bool>::value> {}; template struct type_identity { typedef T type; }; template struct make_unsigned; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template struct make_void { typedef void type; }; template struct remove_const { typedef T type; }; template struct remove_const { typedef T 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>::type> { static constexpr T lowest() { return 0; } static constexpr T highest() { return T(-1); } }; template struct numeric_limits< T, typename enable_if::value && is_signed::value>::type> { 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::type; using length_type = uint_t::type; 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 node = reinterpret_cast( allocator->allocate(sizeForLength(length))); 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(typename remove_reference::type& t) noexcept { return static_cast(t); } template typename remove_reference::type&& move(T&& t) { return static_cast::type&&>(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>::type> { typedef ZeroTerminatedRamString AdaptedString; static AdaptedString adapt(const TChar* p) { return AdaptedString(reinterpret_cast(p)); } }; template struct StringAdapter::value>::type> { typedef ZeroTerminatedRamString AdaptedString; static AdaptedString adapt(const TChar* p) { return AdaptedString(reinterpret_cast(p)); } }; class StaticStringAdapter : public ZeroTerminatedRamString { public: StaticStringAdapter(const char* str) : ZeroTerminatedRamString(str) {} 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>::type> { 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>::type> : true_type {}; template struct has_data : false_type {}; template struct has_data().data()), const char*>::value>::type> : true_type {}; template struct has_length : false_type {}; template struct has_length< T, typename enable_if< is_same().length()), size_t>::value>::type> : true_type {}; template struct has_size : false_type {}; template struct has_size< T, typename enable_if< is_same().size()), size_t>::value>::type> : 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< T, typename enable_if< (string_traits::has_cstr || string_traits::has_data) && (string_traits::has_length || string_traits::has_size)>::type> { typedef SizedRamString AdaptedString; static AdaptedString adapt(const T& s) { return AdaptedString(get_data(s), get_size(s)); } private: template static typename enable_if::has_size, size_t>::type get_size( const U& s) { return s.size(); } template static typename enable_if::has_size, size_t>::type get_size( const U& s) { return s.length(); } template static typename enable_if::has_data, const char*>::type get_data(const U& s) { return s.data(); } template static typename enable_if::has_data, const char*>::type 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 typename enable_if::type stringCompare(TAdaptedString1 s1, TAdaptedString2 s2) { ARDUINOJSON_ASSERT(!s1.isNull()); ARDUINOJSON_ASSERT(!s2.isNull()); size_t size1 = s1.size(); size_t size2 = s2.size(); size_t n = size1 < size2 ? size1 : size2; for (size_t i = 0; i < n; i++) { if (s1[i] != s2[i]) return s1[i] - s2[i]; } if (size1 < size2) return -1; if (size1 > size2) return 1; return 0; } template typename enable_if< (TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), int>::type stringCompare(TAdaptedString1 s1, TAdaptedString2 s2) { return -stringCompare(s2, s1); } template typename enable_if::type stringEquals(TAdaptedString1 s1, TAdaptedString2 s2) { ARDUINOJSON_ASSERT(!s1.isNull()); ARDUINOJSON_ASSERT(!s2.isNull()); size_t size1 = s1.size(); size_t size2 = s2.size(); if (size1 != size2) return false; for (size_t i = 0; i < size1; i++) { if (s1[i] != s2[i]) return false; } return true; } template typename enable_if< (TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), bool>::type stringEquals(TAdaptedString1 s1, TAdaptedString2 s2) { return stringEquals(s2, s1); } template static void stringGetChars(TAdaptedString s, char* p, size_t n) { ARDUINOJSON_ASSERT(s.size() <= n); for (size_t i = 0; i < n; i++) { p[i] = s[i]; } } 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::type; 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 id) { variantPools_.freeSlot(id); } 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< T, typename make_void::AdaptedString>::type> : 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( typename enable_if::value && is_signed::value && sizeof(TOut) == 8, signed>::type* = 0) { return forge(0x43DFFFFFFFFFFFFF); // 9.2233720368547748e+18 } template // uint64_t static T highest_for( typename enable_if::value && is_unsigned::value && sizeof(TOut) == 8, unsigned>::type* = 0) { return forge(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( typename enable_if::value && is_signed::value && sizeof(TOut) == 4, signed>::type* = 0) { return forge(0x4EFFFFFF); // 2.14748352E9 } template // uint32_t static T highest_for( typename enable_if::value && is_unsigned::value && sizeof(TOut) == 4, unsigned>::type* = 0) { return forge(0x4F7FFFFF); // 4.29496704E9 } template // int64_t static T highest_for( typename enable_if::value && is_signed::value && sizeof(TOut) == 8, signed>::type* = 0) { return forge(0x5EFFFFFF); // 9.22337148709896192E18 } template // uint64_t static T highest_for( typename enable_if::value && is_unsigned::value && sizeof(TOut) == 8, unsigned>::type* = 0) { return forge(0x5F7FFFFF); // 1.844674297419792384E19 } static T lowest() { return forge(0xFf7fffff); } }; template 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 typename enable_if::value && is_unsigned::value && is_integral::value && sizeof(TOut) <= sizeof(TIn), bool>::type canConvertNumber(TIn value) { return value <= TIn(numeric_limits::highest()); } template typename enable_if::value && is_unsigned::value && is_integral::value && sizeof(TIn) < sizeof(TOut), bool>::type canConvertNumber(TIn) { return true; } template typename enable_if::value && is_floating_point::value, bool>::type canConvertNumber(TIn) { return true; } template typename enable_if::value && is_signed::value && is_integral::value && is_signed::value && sizeof(TOut) < sizeof(TIn), bool>::type canConvertNumber(TIn value) { return value >= TIn(numeric_limits::lowest()) && value <= TIn(numeric_limits::highest()); } template typename enable_if::value && is_signed::value && is_integral::value && is_signed::value && sizeof(TIn) <= sizeof(TOut), bool>::type canConvertNumber(TIn) { return true; } template typename enable_if::value && is_signed::value && is_integral::value && is_unsigned::value && sizeof(TOut) >= sizeof(TIn), bool>::type canConvertNumber(TIn value) { if (value < 0) return false; return TOut(value) <= numeric_limits::highest(); } template typename enable_if::value && is_signed::value && is_integral::value && is_unsigned::value && sizeof(TOut) < sizeof(TIn), bool>::type canConvertNumber(TIn value) { if (value < 0) return false; return value <= TIn(numeric_limits::highest()); } template typename enable_if::value && is_integral::value && sizeof(TOut) < sizeof(TIn), bool>::type canConvertNumber(TIn value) { return value >= numeric_limits::lowest() && value <= numeric_limits::highest(); } template typename enable_if::value && is_integral::value && sizeof(TOut) >= sizeof(TIn), bool>::type canConvertNumber(TIn value) { return value >= numeric_limits::lowest() && value <= FloatTraits::template highest_for(); } template TOut convertNumber(TIn value) { return canConvertNumber(value) ? TOut(value) : 0; } 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_; } protected: iterator addSlot(ResourceManager*); private: SlotWithId getPreviousSlot(VariantSlot*, const ResourceManager*) const; void releaseSlot(SlotWithId, ResourceManager*); }; 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); } 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); } 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 typename enable_if::value>::type setInteger(T value) { setType(VALUE_IS_SIGNED_INTEGER); content_.asSignedInteger = value; } template typename enable_if::value>::type 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, typename enable_if::value && is_integral::value && sizeof(T1) < sizeof(T2)>::type* = 0) { return arithmeticCompare(static_cast(lhs), rhs); } template CompareResult arithmeticCompare( const T1& lhs, const T2& rhs, typename enable_if::value && is_integral::value && sizeof(T2) < sizeof(T1)>::type* = 0) { return arithmeticCompare(lhs, static_cast(rhs)); } template CompareResult arithmeticCompare( const T1& lhs, const T2& rhs, typename enable_if::value && is_integral::value && is_signed::value == is_signed::value && sizeof(T2) == sizeof(T1)>::type* = 0) { return arithmeticCompare(lhs, static_cast(rhs)); } template CompareResult arithmeticCompare( const T1& lhs, const T2& rhs, typename enable_if::value && is_integral::value && is_unsigned::value && is_signed::value && sizeof(T2) == sizeof(T1)>::type* = 0) { if (rhs < 0) return COMPARE_RESULT_GREATER; return arithmeticCompare(lhs, static_cast(rhs)); } template CompareResult arithmeticCompare( const T1& lhs, const T2& rhs, typename enable_if::value && is_integral::value && is_signed::value && is_unsigned::value && sizeof(T2) == sizeof(T1)>::type* = 0) { if (lhs < 0) return COMPARE_RESULT_LESS; return arithmeticCompare(static_cast(lhs), rhs); } template CompareResult arithmeticCompare( const T1& lhs, const T2& rhs, typename enable_if::value || is_floating_point::value>::type* = 0) { return arithmeticCompare(static_cast(lhs), static_cast(rhs)); } template CompareResult arithmeticCompareNegateLeft( JsonUInt, const T2&, typename enable_if::value>::type* = 0) { return COMPARE_RESULT_LESS; } template CompareResult arithmeticCompareNegateLeft( JsonUInt lhs, const T2& rhs, typename enable_if::value>::type* = 0) { if (rhs > 0) return COMPARE_RESULT_LESS; return arithmeticCompare(-rhs, static_cast(lhs)); } template CompareResult arithmeticCompareNegateRight( const T1&, JsonUInt, typename enable_if::value>::type* = 0) { return COMPARE_RESULT_GREATER; } template CompareResult arithmeticCompareNegateRight( const T1& lhs, JsonUInt rhs, typename enable_if::value>::type* = 0) { if (lhs > 0) return COMPARE_RESULT_GREATER; return arithmeticCompare(static_cast(rhs), -lhs); } struct VariantTag {}; template struct IsVariant : is_base_of {}; 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 typename enable_if::value && !is_array::value, T>::type operator|(const TVariant& variant, const T& defaultValue) { if (variant.template is()) return variant.template as(); else return defaultValue; } friend const char* operator|(const TVariant& variant, const char* defaultValue) { if (variant.template is()) return variant.template as(); else return defaultValue; } template friend typename enable_if::value, JsonVariantConst>::type operator|(const TVariant& variant, T defaultValue) { if (variant) return variant; else return defaultValue; } template friend bool operator==(T* lhs, TVariant rhs) { return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; } template friend bool operator==(const T& lhs, TVariant rhs) { return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; } template friend bool operator==(TVariant lhs, T* rhs) { return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; } template friend typename enable_if::value, bool>::type operator==(TVariant lhs, const T& rhs) { return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; } template friend bool operator!=(T* lhs, TVariant rhs) { return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; } template friend bool operator!=(const T& lhs, TVariant rhs) { return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; } template friend bool operator!=(TVariant lhs, T* rhs) { return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; } template friend typename enable_if::value, bool>::type operator!=(TVariant lhs, const T& rhs) { return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; } template friend bool operator<(T* lhs, TVariant rhs) { return compare(rhs, lhs) == COMPARE_RESULT_GREATER; } template friend bool operator<(const T& lhs, TVariant rhs) { return compare(rhs, lhs) == COMPARE_RESULT_GREATER; } template friend bool operator<(TVariant lhs, T* rhs) { return compare(lhs, rhs) == COMPARE_RESULT_LESS; } template friend typename enable_if::value, bool>::type operator<(TVariant lhs, const T& rhs) { return compare(lhs, rhs) == COMPARE_RESULT_LESS; } template friend bool operator<=(T* lhs, TVariant rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } template friend bool operator<=(const T& lhs, TVariant rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } template friend bool operator<=(TVariant lhs, T* rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template friend typename enable_if::value, bool>::type operator<=(TVariant lhs, const T& rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template friend bool operator>(T* lhs, TVariant rhs) { return compare(rhs, lhs) == COMPARE_RESULT_LESS; } template friend bool operator>(const T& lhs, TVariant rhs) { return compare(rhs, lhs) == COMPARE_RESULT_LESS; } template friend bool operator>(TVariant lhs, T* rhs) { return compare(lhs, rhs) == COMPARE_RESULT_GREATER; } template friend typename enable_if::value, bool>::type operator>(TVariant lhs, const T& rhs) { return compare(lhs, rhs) == COMPARE_RESULT_GREATER; } template friend bool operator>=(T* lhs, TVariant rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template friend bool operator>=(const T& lhs, TVariant rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template friend bool operator>=(TVariant lhs, T* rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } template friend typename enable_if::value, bool>::type operator>=(TVariant lhs, const T& rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } }; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class JsonArray; class JsonObject; class JsonVariantConst : public detail::VariantTag, public detail::VariantOperators { friend class detail::VariantAttorney; 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 typename detail::enable_if::value && !detail::is_same::value, T>::type as() const { return Converter::fromJson(*this); } template typename detail::enable_if::value && !detail::is_same::value, bool>::type is() const { return Converter::checkJson(*this); } template operator T() const { return as(); } JsonVariantConst operator[](size_t index) const { return JsonVariantConst( detail::VariantData::getElement(data_, index, resources_), resources_); } template typename detail::enable_if::value, JsonVariantConst>::type operator[](const TString& key) const { return JsonVariantConst(detail::VariantData::getMember( data_, detail::adaptString(key), resources_), resources_); } template typename detail::enable_if::value, JsonVariantConst>::type operator[](TChar* key) const { return JsonVariantConst(detail::VariantData::getMember( data_, detail::adaptString(key), resources_), resources_); } template typename detail::enable_if::value, bool>::type containsKey(const TString& key) const { return detail::VariantData::getMember(getData(), detail::adaptString(key), resources_) != 0; } template typename detail::enable_if::value, bool>::type containsKey(TChar* key) const { return detail::VariantData::getMember(getData(), detail::adaptString(key), resources_) != 0; } 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 typename enable_if::value, T>::type as() const { return Converter::fromJson(getVariantConst()); } template typename enable_if::value, T>::type as() const; template ::value>::type> operator T() const { return as(); } template typename enable_if::value, JsonArray>::type to() const; template typename enable_if::value, JsonObject>::type to() const; template typename enable_if::value, JsonVariant>::type to() const; template FORCE_INLINE typename enable_if::value, bool>::type is() const; template FORCE_INLINE typename enable_if::value, bool>::type is() const { return Converter::checkJson(getVariantConst()); } template bool set(const T& value) const; template bool set(T* value) const; size_t size() const { return VariantData::size(getData(), getResourceManager()); } size_t nesting() const { return VariantData::nesting(getData(), getResourceManager()); } template typename enable_if::value, T>::type add() const { return add().template to(); } template typename enable_if::value, T>::type add() const; template bool add(const T& value) const { return add().set(value); } template bool add(T* value) const { return add().set(value); } void remove(size_t index) const { VariantData::removeElement(getData(), index, getResourceManager()); } template typename enable_if::value>::type remove(TChar* key) const { VariantData::removeMember(getData(), adaptString(key), getResourceManager()); } template typename enable_if::value>::type remove( const TString& key) const { VariantData::removeMember(getData(), adaptString(key), getResourceManager()); } ElementProxy operator[](size_t index) const; template typename enable_if::value, bool>::type containsKey( const TString& key) const; template typename enable_if::value, bool>::type containsKey( TChar* key) const; template FORCE_INLINE typename enable_if::value, MemberProxy>::type operator[](const TString& key) const; template FORCE_INLINE typename enable_if::value, MemberProxy>::type operator[](TChar* key) const; 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()); } 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 detail::InvalidConversion fromJson( JsonVariantConst); static bool checkJson(JsonVariant src) { auto data = getData(src); return !!data; } static bool checkJson(JsonVariantConst) { return false; } }; 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) {} JsonArrayConst(const detail::ArrayData* data, const detail::ResourceManager* resources) : data_(data), resources_(resources) {} JsonVariantConst operator[](size_t index) const { return JsonVariantConst( detail::ArrayData::getElement(data_, index, 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; } 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 typename detail::enable_if::value, T>::type add() const { return add().to(); } template typename detail::enable_if::value, T>::type add() const { return JsonVariant(detail::ArrayData::addElement(data_, resources_), resources_); } template bool add(const T& value) const { return add().set(value); } template bool add(T* value) const { return add().set(value); } 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_); } void clear() const { detail::ArrayData::clear(data_, resources_); } detail::ElementProxy operator[](size_t index) const { return {*this, index}; } 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) {} 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 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 typename detail::enable_if::value, JsonVariantConst>::type operator[](const TString& key) const { return JsonVariantConst(detail::ObjectData::getMember( data_, detail::adaptString(key), resources_), resources_); } template typename detail::enable_if::value, JsonVariantConst>::type operator[](TChar* key) const { return JsonVariantConst(detail::ObjectData::getMember( data_, detail::adaptString(key), resources_), resources_); } 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 typename detail::enable_if::value, detail::MemberProxy>::type operator[](const TString& key) const { return {*this, key}; } template typename detail::enable_if::value, detail::MemberProxy>::type operator[](TChar* key) const { return {*this, key}; } FORCE_INLINE void remove(iterator it) const { detail::ObjectData::remove(data_, it.iterator_, resources_); } template FORCE_INLINE void remove(const TString& key) const { detail::ObjectData::removeMember(data_, detail::adaptString(key), resources_); } template FORCE_INLINE void remove(TChar* key) const { detail::ObjectData::removeMember(data_, detail::adaptString(key), resources_); } template typename detail::enable_if::value, bool>::type containsKey(const TString& key) const { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } template typename detail::enable_if::value, bool>::type containsKey(TChar* key) const { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } 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(), typename detail::enable_if< detail::is_same::value || detail::is_same::value || detail::is_same::value || detail::is_same::value || detail::is_same::value || detail::is_same::value>::type* = 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 typename detail::enable_if::value, bool>::type 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 bool containsKey(const TString& key) const { return data_.getMember(detail::adaptString(key), &resources_) != 0; } template typename detail::enable_if::value, detail::MemberProxy>::type operator[](const TString& key) { return {*this, key}; } template typename detail::enable_if::value, detail::MemberProxy>::type operator[](TChar* key) { return {*this, key}; } template typename detail::enable_if::value, JsonVariantConst>::type operator[](const TString& key) const { return JsonVariantConst( data_.getMember(detail::adaptString(key), &resources_), &resources_); } template typename detail::enable_if::value, JsonVariantConst>::type operator[](TChar* key) const { return JsonVariantConst( data_.getMember(detail::adaptString(key), &resources_), &resources_); } detail::ElementProxy operator[](size_t index) { return {*this, index}; } JsonVariantConst operator[](size_t index) const { return JsonVariantConst(data_.getElement(index, &resources_), &resources_); } template typename detail::enable_if::value, T>::type add() { return add().to(); } template typename detail::enable_if::value, T>::type add() { return JsonVariant(data_.addElement(&resources_), &resources_); } template bool add(const TValue& value) { return add().set(value); } template bool add(TChar* value) { return add().set(value); } void remove(size_t index) { detail::VariantData::removeElement(getData(), index, getResourceManager()); } template typename detail::enable_if::value>::type remove( TChar* key) { detail::VariantData::removeMember(getData(), detail::adaptString(key), getResourceManager()); } template typename detail::enable_if::value>::type remove( const TString& key) { detail::VariantData::removeMember(getData(), detail::adaptString(key), getResourceManager()); } 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>::type> : ComparerBase { T rhs; // TODO: store adapted string? 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>::type> : 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>::type> : 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); } ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE template inline typename detail::enable_if::value, bool>::type copyArray(const T& src, JsonVariant dst) { return dst.set(src); } template inline typename detail::enable_if< !detail::is_base_of::value, bool>::type copyArray(T (&src)[N], const TDestination& dst) { return copyArray(src, N, dst); } template inline typename detail::enable_if< !detail::is_base_of::value, bool>::type copyArray(const T* src, size_t len, const TDestination& dst) { bool ok = true; for (size_t i = 0; i < len; i++) { ok &= copyArray(src[i], dst.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 typename detail::enable_if::value, size_t>::type copyArray(JsonVariantConst src, T& dst) { dst = src.as(); return 1; } template inline size_t copyArray(JsonArrayConst src, T (&dst)[N]) { return copyArray(src, dst, N); } template inline size_t copyArray(JsonArrayConst src, T* dst, size_t len) { size_t i = 0; for (JsonArrayConst::iterator it = src.begin(); it != src.end() && i < len; ++it) copyArray(*it, dst[i++]); return i; } template inline size_t copyArray(JsonVariantConst src, char (&dst)[N]) { JsonString s = src; size_t len = N - 1; if (len > s.size()) len = s.size(); memcpy(dst, s.c_str(), len); dst[len] = 0; return 1; } template inline typename detail::enable_if< detail::is_array::value && detail::is_base_of::value, size_t>::type 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::clear(ResourceManager* resources) { auto next = head_; while (next != NULL_SLOT) { auto currId = next; auto slot = resources->getSlot(next); next = slot->next(); releaseSlot(SlotWithId(slot, currId), resources); } 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(); releaseSlot({it.slot_, it.currentId_}, resources); } 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 CollectionData::releaseSlot(SlotWithId slot, ResourceManager* resources) { if (slot->ownsKey()) resources->dereferenceString(slot->key()); slot->data()->setNull(resources); resources->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 typename enable_if::value>::type writeInteger(T value) { typedef typename make_unsigned::type unsigned_type; unsigned_type unsigned_value; if (value < 0) { writeRaw('-'); unsigned_value = unsigned_type(unsigned_type(~value) + 1); } else { unsigned_value = unsigned_type(value); } writeInteger(unsigned_value); } template typename enable_if::value>::type writeInteger(T value) { char buffer[22]; char* end = buffer + sizeof(buffer); char* begin = end; do { *--begin = char(value % 10 + '0'); value = T(value / 10); } while (value); writeRaw(begin, end); } void writeDecimals(uint32_t value, int8_t width) { char buffer[16]; char* end = buffer + sizeof(buffer); char* begin = end; while (width--) { *--begin = char(value % 10 + '0'); value /= 10; } *--begin = '.'; writeRaw(begin, end); } void writeRaw(const char* s) { writer_.write(reinterpret_cast(s), strlen(s)); } void writeRaw(const char* s, size_t n) { writer_.write(reinterpret_cast(s), n); } void writeRaw(const char* begin, const char* end) { writer_.write(reinterpret_cast(begin), static_cast(end - begin)); } template void writeRaw(const char (&s)[N]) { writer_.write(reinterpret_cast(s), N - 1); } void writeRaw(char c) { writer_.write(static_cast(c)); } protected: CountingDecorator writer_; }; class DummyWriter { public: size_t write(uint8_t) { return 1; } size_t write(const uint8_t*, size_t n) { return n; } }; template