// 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_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_DOUBLE # if ARDUINOJSON_SIZEOF_POINTER >= 4 // 32 & 64 bits systems # define ARDUINOJSON_USE_DOUBLE 1 # else # define ARDUINOJSON_USE_DOUBLE 0 # endif #endif #ifndef ARDUINOJSON_USE_LONG_LONG # if ARDUINOJSON_SIZEOF_POINTER >= 4 // 32 & 64 bits systems # define ARDUINOJSON_USE_LONG_LONG 1 # else # define ARDUINOJSON_USE_LONG_LONG 0 # endif #endif #ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT # define ARDUINOJSON_DEFAULT_NESTING_LIMIT 10 #endif #ifndef ARDUINOJSON_SLOT_ID_SIZE # if ARDUINOJSON_SIZEOF_POINTER <= 2 # define ARDUINOJSON_SLOT_ID_SIZE 1 # elif ARDUINOJSON_SIZEOF_POINTER == 4 # define ARDUINOJSON_SLOT_ID_SIZE 2 # else # define ARDUINOJSON_SLOT_ID_SIZE 4 # endif #endif #ifndef ARDUINOJSON_POOL_CAPACITY # if ARDUINOJSON_SLOT_ID_SIZE == 1 # define ARDUINOJSON_POOL_CAPACITY 16 // 96 bytes # elif ARDUINOJSON_SLOT_ID_SIZE == 2 # define ARDUINOJSON_POOL_CAPACITY 128 // 1024 bytes # else # define ARDUINOJSON_POOL_CAPACITY 256 // 4096 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 ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_DOUBLE # define ARDUINOJSON_USE_EXTENSIONS 1 #else # define ARDUINOJSON_USE_EXTENSIONS 0 #endif #if defined(nullptr) # error nullptr is defined as a macro. Remove the faulty #define or #undef nullptr #endif #if ARDUINOJSON_ENABLE_ARDUINO_STRING || ARDUINOJSON_ENABLE_ARDUINO_STREAM || \ ARDUINOJSON_ENABLE_ARDUINO_PRINT || \ (ARDUINOJSON_ENABLE_PROGMEM && defined(ARDUINO)) #include #endif #if !ARDUINOJSON_DEBUG # ifdef __clang__ # pragma clang system_header # elif defined __GNUC__ # pragma GCC system_header # endif #endif #define ARDUINOJSON_CONCAT_(A, B) A##B #define ARDUINOJSON_CONCAT2(A, B) ARDUINOJSON_CONCAT_(A, B) #define ARDUINOJSON_CONCAT3(A, B, C) \ ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT2(A, B), C) #define ARDUINOJSON_CONCAT4(A, B, C, D) \ ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT3(A, B, C), D) #define ARDUINOJSON_CONCAT5(A, B, C, D, E) \ ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT4(A, B, C, D), E) #define ARDUINOJSON_BIN2ALPHA_0000() A #define ARDUINOJSON_BIN2ALPHA_0001() B #define ARDUINOJSON_BIN2ALPHA_0010() C #define ARDUINOJSON_BIN2ALPHA_0011() D #define ARDUINOJSON_BIN2ALPHA_0100() E #define ARDUINOJSON_BIN2ALPHA_0101() F #define ARDUINOJSON_BIN2ALPHA_0110() G #define ARDUINOJSON_BIN2ALPHA_0111() H #define ARDUINOJSON_BIN2ALPHA_1000() I #define ARDUINOJSON_BIN2ALPHA_1001() J #define ARDUINOJSON_BIN2ALPHA_1010() K #define ARDUINOJSON_BIN2ALPHA_1011() L #define ARDUINOJSON_BIN2ALPHA_1100() M #define ARDUINOJSON_BIN2ALPHA_1101() N #define ARDUINOJSON_BIN2ALPHA_1110() O #define ARDUINOJSON_BIN2ALPHA_1111() P #define ARDUINOJSON_BIN2ALPHA_(A, B, C, D) ARDUINOJSON_BIN2ALPHA_##A##B##C##D() #define ARDUINOJSON_BIN2ALPHA(A, B, C, D) ARDUINOJSON_BIN2ALPHA_(A, B, C, D) #define ARDUINOJSON_VERSION "7.2.0" #define ARDUINOJSON_VERSION_MAJOR 7 #define ARDUINOJSON_VERSION_MINOR 2 #define ARDUINOJSON_VERSION_REVISION 0 #define ARDUINOJSON_VERSION_MACRO V720 #ifndef ARDUINOJSON_VERSION_NAMESPACE # define ARDUINOJSON_VERSION_NAMESPACE \ ARDUINOJSON_CONCAT5( \ ARDUINOJSON_VERSION_MACRO, \ ARDUINOJSON_BIN2ALPHA(ARDUINOJSON_ENABLE_PROGMEM, \ ARDUINOJSON_USE_LONG_LONG, \ ARDUINOJSON_USE_DOUBLE, 1), \ ARDUINOJSON_BIN2ALPHA( \ ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \ ARDUINOJSON_ENABLE_COMMENTS, ARDUINOJSON_DECODE_UNICODE), \ ARDUINOJSON_SLOT_ID_SIZE, ARDUINOJSON_STRING_LENGTH_SIZE) #endif #define ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE \ namespace ArduinoJson { \ inline namespace ARDUINOJSON_VERSION_NAMESPACE { #define ARDUINOJSON_END_PUBLIC_NAMESPACE \ } \ } #define ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE \ namespace ArduinoJson { \ inline namespace ARDUINOJSON_VERSION_NAMESPACE { \ namespace detail { #define ARDUINOJSON_END_PRIVATE_NAMESPACE \ } \ } \ } ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE template struct Converter; ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template class InvalidConversion; // Error here? See https://arduinojson.org/v7/invalid-conversion/ ARDUINOJSON_END_PRIVATE_NAMESPACE #include #include #include ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class Allocator { public: virtual void* allocate(size_t size) = 0; virtual void deallocate(void* ptr) = 0; virtual void* reallocate(void* ptr, size_t new_size) = 0; protected: ~Allocator() = default; }; namespace detail { class DefaultAllocator : public Allocator { public: void* allocate(size_t size) override { return malloc(size); } void deallocate(void* ptr) override { free(ptr); } void* reallocate(void* ptr, size_t new_size) override { return realloc(ptr, new_size); } static Allocator* instance() { static DefaultAllocator allocator; return &allocator; } private: DefaultAllocator() = default; ~DefaultAllocator() = default; }; } // namespace detail ARDUINOJSON_END_PUBLIC_NAMESPACE #if ARDUINOJSON_DEBUG #include # define ARDUINOJSON_ASSERT(X) assert(X) #else # define ARDUINOJSON_ASSERT(X) ((void)0) #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template struct uint_; template <> struct uint_<8> { typedef uint8_t type; }; template <> struct uint_<16> { typedef uint16_t type; }; template <> struct uint_<32> { typedef uint32_t type; }; template using uint_t = typename uint_::type; using SlotId = uint_t; using SlotCount = SlotId; const SlotId NULL_SLOT = SlotId(-1); template class Slot { public: Slot() : ptr_(nullptr), id_(NULL_SLOT) {} Slot(T* p, SlotId id) : ptr_(p), id_(id) { ARDUINOJSON_ASSERT((p == nullptr) == (id == NULL_SLOT)); } explicit operator bool() const { return ptr_ != nullptr; } SlotId id() const { return id_; } T* ptr() const { return ptr_; } T* operator->() const { ARDUINOJSON_ASSERT(ptr_ != nullptr); return ptr_; } private: T* ptr_; SlotId id_; }; template class MemoryPool { public: void create(SlotCount cap, Allocator* allocator) { ARDUINOJSON_ASSERT(cap > 0); slots_ = reinterpret_cast(allocator->allocate(slotsToBytes(cap))); capacity_ = slots_ ? cap : 0; usage_ = 0; } void destroy(Allocator* allocator) { if (slots_) allocator->deallocate(slots_); slots_ = nullptr; capacity_ = 0; usage_ = 0; } Slot allocSlot() { if (!slots_) return {}; if (usage_ >= capacity_) return {}; auto index = usage_++; return {slots_ + index, SlotId(index)}; } T* getSlot(SlotId id) const { ARDUINOJSON_ASSERT(id < usage_); return slots_ + id; } void clear() { usage_ = 0; } void shrinkToFit(Allocator* allocator) { auto newSlots = reinterpret_cast( allocator->reallocate(slots_, slotsToBytes(usage_))); if (newSlots) { slots_ = newSlots; capacity_ = usage_; } } SlotCount usage() const { return usage_; } static SlotCount bytesToSlots(size_t n) { return static_cast(n / sizeof(T)); } static size_t slotsToBytes(SlotCount n) { return n * sizeof(T); } private: SlotCount capacity_; SlotCount usage_; T* slots_; }; template struct conditional { typedef TrueType type; }; template struct conditional { typedef FalseType type; }; template using conditional_t = typename conditional::type; template struct enable_if {}; template struct enable_if { typedef T type; }; template using enable_if_t = typename enable_if::type; template struct function_traits; template struct function_traits { using return_type = ReturnType; using arg1_type = Arg1; }; template struct function_traits { using return_type = ReturnType; using arg1_type = Arg1; using arg2_type = Arg2; }; template struct integral_constant { static const T value = v; }; typedef integral_constant true_type; typedef integral_constant false_type; template struct is_array : false_type {}; template struct is_array : true_type {}; template struct is_array : true_type {}; template struct remove_reference { typedef T type; }; template struct remove_reference { typedef T type; }; template using remove_reference_t = typename remove_reference::type; template class is_base_of { protected: // <- to avoid GCC's "all member functions in class are private" static int probe(const TBase*); static char probe(...); public: static const bool value = sizeof(probe(reinterpret_cast*>(0))) == sizeof(int); }; template T&& declval(); template struct is_class { protected: // <- to avoid GCC's "all member functions in class are private" template static int probe(void (U::*)(void)); template static char probe(...); public: static const bool value = sizeof(probe(0)) == sizeof(int); }; template struct is_const : false_type {}; template struct is_const : true_type {}; ARDUINOJSON_END_PRIVATE_NAMESPACE #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable : 4244) #endif #ifdef __ICCARM__ #pragma diag_suppress=Pa093 #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template struct is_convertible { protected: // <- to avoid GCC's "all member functions in class are private" static int probe(To); static char probe(...); static From& from_; public: static const bool value = sizeof(probe(from_)) == sizeof(int); }; ARDUINOJSON_END_PRIVATE_NAMESPACE #ifdef _MSC_VER # pragma warning(pop) #endif #ifdef __ICCARM__ #pragma diag_default=Pa093 #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template struct is_same : false_type {}; template struct is_same : true_type {}; template struct remove_cv { typedef T type; }; template struct remove_cv { typedef T type; }; template struct remove_cv { typedef T type; }; template struct remove_cv { typedef T type; }; template using remove_cv_t = typename remove_cv::type; template struct is_floating_point : integral_constant>::value || is_same>::value> {}; template struct is_integral : integral_constant, signed char>::value || is_same, unsigned char>::value || is_same, signed short>::value || is_same, unsigned short>::value || is_same, signed int>::value || is_same, unsigned int>::value || is_same, signed long>::value || is_same, unsigned long>::value || is_same, signed long long>::value || is_same, unsigned long long>::value || is_same, char>::value || is_same, bool>::value> {}; template struct is_enum { static const bool value = is_convertible::value && !is_class::value && !is_integral::value && !is_floating_point::value; }; template struct is_pointer : false_type {}; template struct is_pointer : true_type {}; template struct is_signed : integral_constant, char>::value || is_same, signed char>::value || is_same, signed short>::value || is_same, signed int>::value || is_same, signed long>::value || is_same, signed long long>::value || is_same, float>::value || is_same, double>::value> {}; template struct is_unsigned : integral_constant, unsigned char>::value || is_same, unsigned short>::value || is_same, unsigned int>::value || is_same, unsigned long>::value || is_same, unsigned long long>::value || is_same, bool>::value> {}; template struct type_identity { typedef T type; }; template struct make_unsigned; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; template using make_unsigned_t = typename make_unsigned::type; template struct remove_const { typedef T type; }; template struct remove_const { typedef T type; }; template using remove_const_t = typename remove_const::type; template struct make_void { using type = void; }; template using void_t = typename make_void::type; using nullptr_t = decltype(nullptr); template T&& forward(remove_reference_t& t) noexcept { return static_cast(t); } template remove_reference_t&& move(T&& t) { return static_cast&&>(t); } template void swap_(T& a, T& b) { T tmp = move(a); a = move(b); b = move(tmp); } ARDUINOJSON_END_PRIVATE_NAMESPACE #include ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE using PoolCount = SlotId; template class MemoryPoolList { struct FreeSlot { SlotId next; }; static_assert(sizeof(FreeSlot) <= sizeof(T), "T is too small"); public: using Pool = MemoryPool; MemoryPoolList() = default; ~MemoryPoolList() { ARDUINOJSON_ASSERT(count_ == 0); } friend void swap(MemoryPoolList& a, MemoryPoolList& 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_); } MemoryPoolList& operator=(MemoryPoolList&& 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; } Slot 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(Slot slot) { reinterpret_cast(slot.ptr())->next = freeList_; freeList_ = slot.id(); } T* 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; } size_t size() const { return Pool::slotsToBytes(usage()); } 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(Pool))); ARDUINOJSON_ASSERT(pools_ != nullptr); // realloc to smaller can't fail capacity_ = count_; } } private: Slot allocFromFreeList() { ARDUINOJSON_ASSERT(freeList_ != NULL_SLOT); auto id = freeList_; auto slot = getSlot(freeList_); freeList_ = reinterpret_cast(slot)->next; return {slot, id}; } Slot allocFromLastPool() { ARDUINOJSON_ASSERT(count_ > 0); auto poolIndex = SlotId(count_ - 1); auto slot = pools_[poolIndex].allocSlot(); if (!slot) return {}; return {slot.ptr(), SlotId(poolIndex * ARDUINOJSON_POOL_CAPACITY + slot.id())}; } Pool* 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(Pool)); if (!newPools) return false; memcpy(newPools, preallocatedPools_, sizeof(preallocatedPools_)); } else { newPools = allocator->reallocate(pools_, newCapacity * sizeof(Pool)); if (!newPools) return false; } pools_ = static_cast(newPools); capacity_ = newCapacity; return true; } Pool preallocatedPools_[ARDUINOJSON_INITIAL_POOL_COUNT]; Pool* 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); }; ARDUINOJSON_END_PRIVATE_NAMESPACE #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable : 4310) #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template struct numeric_limits; template struct numeric_limits::value>> { static constexpr T lowest() { return 0; } static constexpr T highest() { return T(-1); } }; template struct numeric_limits< T, enable_if_t::value && is_signed::value>> { static constexpr T lowest() { return T(T(1) << (sizeof(T) * 8 - 1)); } static constexpr T highest() { return T(~lowest()); } }; ARDUINOJSON_END_PRIVATE_NAMESPACE #ifdef _MSC_VER # pragma warning(pop) #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE struct StringNode { using references_type = uint_t; using length_type = uint_t; struct StringNode* next; references_type references; length_type length; char data[1]; static constexpr size_t maxLength = numeric_limits::highest(); static constexpr size_t sizeForLength(size_t n) { return n + 1 + offsetof(StringNode, data); } static StringNode* create(size_t length, Allocator* allocator) { if (length > maxLength) return nullptr; auto size = sizeForLength(length); if (size < length) // integer overflow return nullptr; // (not testable on 64-bit) auto node = reinterpret_cast(allocator->allocate(size)); if (node) { node->length = length_type(length); node->references = 1; } return node; } static StringNode* resize(StringNode* node, size_t length, Allocator* allocator) { ARDUINOJSON_ASSERT(node != nullptr); StringNode* newNode; if (length <= maxLength) newNode = reinterpret_cast( allocator->reallocate(node, sizeForLength(length))); else newNode = nullptr; if (newNode) newNode->length = length_type(length); else allocator->deallocate(node); return newNode; } static void destroy(StringNode* node, Allocator* allocator) { allocator->deallocate(node); } }; constexpr size_t sizeofString(size_t n) { return StringNode::sizeForLength(n); } ARDUINOJSON_END_PRIVATE_NAMESPACE #ifdef _MSC_VER // Visual Studio # define FORCE_INLINE // __forceinline causes C4714 when returning std::string # ifndef ARDUINOJSON_DEPRECATED # define ARDUINOJSON_DEPRECATED(msg) __declspec(deprecated(msg)) # endif #elif defined(__GNUC__) // GCC or Clang # define FORCE_INLINE __attribute__((always_inline)) # ifndef ARDUINOJSON_DEPRECATED # if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) # define ARDUINOJSON_DEPRECATED(msg) __attribute__((deprecated(msg))) # else # define ARDUINOJSON_DEPRECATED(msg) __attribute__((deprecated)) # endif # endif #else // Other compilers # define FORCE_INLINE # ifndef ARDUINOJSON_DEPRECATED # define ARDUINOJSON_DEPRECATED(msg) # endif #endif #if defined(__has_attribute) # if __has_attribute(no_sanitize) # define ARDUINOJSON_NO_SANITIZE(check) __attribute__((no_sanitize(check))) # else # define ARDUINOJSON_NO_SANITIZE(check) # endif #else # define ARDUINOJSON_NO_SANITIZE(check) #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template struct StringAdapter; template struct SizedStringAdapter; template typename StringAdapter::AdaptedString adaptString(const TString& s) { return StringAdapter::adapt(s); } template typename StringAdapter::AdaptedString adaptString(TChar* p) { return StringAdapter::adapt(p); } template typename SizedStringAdapter::AdaptedString adaptString(TChar* p, size_t n) { return SizedStringAdapter::adapt(p, n); } template struct IsChar : integral_constant::value && sizeof(T) == 1> {}; class ZeroTerminatedRamString { public: static const size_t typeSortKey = 3; ZeroTerminatedRamString(const char* str) : str_(str) {} bool isNull() const { return !str_; } FORCE_INLINE size_t size() const { return str_ ? ::strlen(str_) : 0; } char operator[](size_t i) const { ARDUINOJSON_ASSERT(str_ != 0); ARDUINOJSON_ASSERT(i <= size()); return str_[i]; } const char* data() const { return str_; } friend int stringCompare(ZeroTerminatedRamString a, ZeroTerminatedRamString b) { ARDUINOJSON_ASSERT(!a.isNull()); ARDUINOJSON_ASSERT(!b.isNull()); return ::strcmp(a.str_, b.str_); } friend bool stringEquals(ZeroTerminatedRamString a, ZeroTerminatedRamString b) { return stringCompare(a, b) == 0; } bool isLinked() const { return false; } protected: const char* str_; }; template struct StringAdapter::value>> { typedef ZeroTerminatedRamString AdaptedString; static AdaptedString adapt(const TChar* p) { return AdaptedString(reinterpret_cast(p)); } }; template struct StringAdapter::value>> { typedef ZeroTerminatedRamString AdaptedString; static AdaptedString adapt(const TChar* p) { return AdaptedString(reinterpret_cast(p)); } }; class StaticStringAdapter : public ZeroTerminatedRamString { public: StaticStringAdapter(const char* str) : ZeroTerminatedRamString(str) {} bool isLinked() const { return true; } }; template <> struct StringAdapter { typedef StaticStringAdapter AdaptedString; static AdaptedString adapt(const char* p) { return AdaptedString(p); } }; class SizedRamString { public: static const size_t typeSortKey = 2; SizedRamString(const char* str, size_t sz) : str_(str), size_(sz) {} bool isNull() const { return !str_; } size_t size() const { return size_; } char operator[](size_t i) const { ARDUINOJSON_ASSERT(str_ != 0); ARDUINOJSON_ASSERT(i <= size()); return str_[i]; } const char* data() const { return str_; } bool isLinked() const { return false; } protected: const char* str_; size_t size_; }; template struct SizedStringAdapter::value>> { typedef SizedRamString AdaptedString; static AdaptedString adapt(const TChar* p, size_t n) { return AdaptedString(reinterpret_cast(p), n); } }; ARDUINOJSON_END_PRIVATE_NAMESPACE #if ARDUINOJSON_ENABLE_STD_STREAM #include #endif ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class JsonString { public: enum Ownership { Copied, Linked }; JsonString() : data_(0), size_(0), ownership_(Linked) {} JsonString(const char* data, Ownership ownership = Linked) : data_(data), size_(data ? ::strlen(data) : 0), ownership_(ownership) {} JsonString(const char* data, size_t sz, Ownership ownership = Linked) : data_(data), size_(sz), ownership_(ownership) {} const char* c_str() const { return data_; } bool isNull() const { return !data_; } bool isLinked() const { return ownership_ == Linked; } size_t size() const { return size_; } explicit operator bool() const { return data_ != 0; } friend bool operator==(JsonString lhs, JsonString rhs) { if (lhs.size_ != rhs.size_) return false; if (lhs.data_ == rhs.data_) return true; if (!lhs.data_) return false; if (!rhs.data_) return false; return memcmp(lhs.data_, rhs.data_, lhs.size_) == 0; } friend bool operator!=(JsonString lhs, JsonString rhs) { return !(lhs == rhs); } #if ARDUINOJSON_ENABLE_STD_STREAM friend std::ostream& operator<<(std::ostream& lhs, const JsonString& rhs) { lhs.write(rhs.c_str(), static_cast(rhs.size())); return lhs; } #endif private: const char* data_; size_t size_; Ownership ownership_; }; ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE class JsonStringAdapter : public SizedRamString { public: JsonStringAdapter(const JsonString& s) : SizedRamString(s.c_str(), s.size()), linked_(s.isLinked()) {} bool isLinked() const { return linked_; } private: bool linked_; }; template <> struct StringAdapter { typedef JsonStringAdapter AdaptedString; static AdaptedString adapt(const JsonString& s) { return AdaptedString(s); } }; namespace string_traits_impl { template struct has_cstr : false_type {}; template struct has_cstr().c_str()), const char*>::value>> : true_type {}; template struct has_data : false_type {}; template struct has_data().data()), const char*>::value>> : true_type {}; template struct has_length : false_type {}; template struct has_length< T, enable_if_t().length())>::value>> : true_type {}; template struct has_size : false_type {}; template struct has_size< T, enable_if_t().size()), size_t>::value>> : true_type {}; } // namespace string_traits_impl template struct string_traits { enum { has_cstr = string_traits_impl::has_cstr::value, has_length = string_traits_impl::has_length::value, has_data = string_traits_impl::has_data::value, has_size = string_traits_impl::has_size::value }; }; template struct StringAdapter< T, enable_if_t<(string_traits::has_cstr || string_traits::has_data) && (string_traits::has_length || string_traits::has_size)>> { typedef SizedRamString AdaptedString; static AdaptedString adapt(const T& s) { return AdaptedString(get_data(s), get_size(s)); } private: template static enable_if_t::has_size, size_t> get_size(const U& s) { return s.size(); } template static enable_if_t::has_size, size_t> get_size(const U& s) { return s.length(); } template static enable_if_t::has_data, const char*> get_data( const U& s) { return s.data(); } template static enable_if_t::has_data, const char*> get_data( const U& s) { return s.c_str(); } }; ARDUINOJSON_END_PRIVATE_NAMESPACE #if ARDUINOJSON_ENABLE_PROGMEM #ifdef ARDUINO #else class __FlashStringHelper; #include #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE struct pgm_p { pgm_p(const void* p) : address(reinterpret_cast(p)) {} const char* address; }; ARDUINOJSON_END_PRIVATE_NAMESPACE #ifndef strlen_P inline size_t strlen_P(ArduinoJson::detail::pgm_p s) { const char* p = s.address; ARDUINOJSON_ASSERT(p != NULL); while (pgm_read_byte(p)) p++; return size_t(p - s.address); } #endif #ifndef strncmp_P inline int strncmp_P(const char* a, ArduinoJson::detail::pgm_p b, size_t n) { const char* s1 = a; const char* s2 = b.address; ARDUINOJSON_ASSERT(s1 != NULL); ARDUINOJSON_ASSERT(s2 != NULL); while (n-- > 0) { char c1 = *s1++; char c2 = static_cast(pgm_read_byte(s2++)); if (c1 < c2) return -1; if (c1 > c2) return 1; if (c1 == 0 /* and c2 as well */) return 0; } return 0; } #endif #ifndef strcmp_P inline int strcmp_P(const char* a, ArduinoJson::detail::pgm_p b) { const char* s1 = a; const char* s2 = b.address; ARDUINOJSON_ASSERT(s1 != NULL); ARDUINOJSON_ASSERT(s2 != NULL); for (;;) { char c1 = *s1++; char c2 = static_cast(pgm_read_byte(s2++)); if (c1 < c2) return -1; if (c1 > c2) return 1; if (c1 == 0 /* and c2 as well */) return 0; } } #endif #ifndef memcmp_P inline int memcmp_P(const void* a, ArduinoJson::detail::pgm_p b, size_t n) { const uint8_t* p1 = reinterpret_cast(a); const char* p2 = b.address; ARDUINOJSON_ASSERT(p1 != NULL); ARDUINOJSON_ASSERT(p2 != NULL); while (n-- > 0) { uint8_t v1 = *p1++; uint8_t v2 = pgm_read_byte(p2++); if (v1 != v2) return v1 - v2; } return 0; } #endif #ifndef memcpy_P inline void* memcpy_P(void* dst, ArduinoJson::detail::pgm_p src, size_t n) { uint8_t* d = reinterpret_cast(dst); const char* s = src.address; ARDUINOJSON_ASSERT(d != NULL); ARDUINOJSON_ASSERT(s != NULL); while (n-- > 0) { *d++ = pgm_read_byte(s++); } return dst; } #endif #ifndef pgm_read_dword inline uint32_t pgm_read_dword(ArduinoJson::detail::pgm_p p) { uint32_t result; memcpy_P(&result, p.address, 4); return result; } #endif #ifndef pgm_read_float inline float pgm_read_float(ArduinoJson::detail::pgm_p p) { float result; memcpy_P(&result, p.address, sizeof(float)); return result; } #endif #ifndef pgm_read_double # if defined(__SIZEOF_DOUBLE__) && defined(__SIZEOF_FLOAT__) && \ __SIZEOF_DOUBLE__ == __SIZEOF_FLOAT__ inline double pgm_read_double(ArduinoJson::detail::pgm_p p) { return pgm_read_float(p.address); } # else inline double pgm_read_double(ArduinoJson::detail::pgm_p p) { double result; memcpy_P(&result, p.address, sizeof(double)); return result; } # endif #endif #ifndef pgm_read_ptr inline void* pgm_read_ptr(ArduinoJson::detail::pgm_p p) { void* result; memcpy_P(&result, p.address, sizeof(result)); return result; } #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE class FlashString { public: static const size_t typeSortKey = 1; FlashString(const __FlashStringHelper* str, size_t sz) : str_(reinterpret_cast(str)), size_(sz) {} bool isNull() const { return !str_; } char operator[](size_t i) const { ARDUINOJSON_ASSERT(str_ != 0); ARDUINOJSON_ASSERT(i <= size_); return static_cast(pgm_read_byte(str_ + i)); } const char* data() const { return nullptr; } size_t size() const { return size_; } friend bool stringEquals(FlashString a, SizedRamString b) { ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey); ARDUINOJSON_ASSERT(!a.isNull()); ARDUINOJSON_ASSERT(!b.isNull()); if (a.size() != b.size()) return false; return ::memcmp_P(b.data(), a.str_, a.size_) == 0; } friend int stringCompare(FlashString a, SizedRamString b) { ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey); ARDUINOJSON_ASSERT(!a.isNull()); ARDUINOJSON_ASSERT(!b.isNull()); size_t minsize = a.size() < b.size() ? a.size() : b.size(); int res = ::memcmp_P(b.data(), a.str_, minsize); if (res) return -res; if (a.size() < b.size()) return -1; if (a.size() > b.size()) return 1; return 0; } friend void stringGetChars(FlashString s, char* p, size_t n) { ARDUINOJSON_ASSERT(s.size() <= n); ::memcpy_P(p, s.str_, n); } bool isLinked() const { return false; } private: const char* str_; size_t size_; }; template <> struct StringAdapter { typedef FlashString AdaptedString; static AdaptedString adapt(const __FlashStringHelper* s) { return AdaptedString(s, s ? strlen_P(reinterpret_cast(s)) : 0); } }; template <> struct SizedStringAdapter { typedef FlashString AdaptedString; static AdaptedString adapt(const __FlashStringHelper* s, size_t n) { return AdaptedString(s, n); } }; ARDUINOJSON_END_PRIVATE_NAMESPACE #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template enable_if_t stringCompare(TAdaptedString1 s1, TAdaptedString2 s2) { ARDUINOJSON_ASSERT(!s1.isNull()); ARDUINOJSON_ASSERT(!s2.isNull()); size_t size1 = s1.size(); size_t size2 = s2.size(); size_t n = size1 < size2 ? size1 : size2; for (size_t i = 0; i < n; i++) { if (s1[i] != s2[i]) return s1[i] - s2[i]; } if (size1 < size2) return -1; if (size1 > size2) return 1; return 0; } template enable_if_t<(TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), int> stringCompare(TAdaptedString1 s1, TAdaptedString2 s2) { return -stringCompare(s2, s1); } template enable_if_t stringEquals(TAdaptedString1 s1, TAdaptedString2 s2) { ARDUINOJSON_ASSERT(!s1.isNull()); ARDUINOJSON_ASSERT(!s2.isNull()); size_t size1 = s1.size(); size_t size2 = s2.size(); if (size1 != size2) return false; for (size_t i = 0; i < size1; i++) { if (s1[i] != s2[i]) return false; } return true; } template enable_if_t<(TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), bool> stringEquals(TAdaptedString1 s1, TAdaptedString2 s2) { return stringEquals(s2, s1); } template static void stringGetChars(TAdaptedString s, char* p, size_t n) { ARDUINOJSON_ASSERT(s.size() <= n); for (size_t i = 0; i < n; i++) { p[i] = s[i]; } } class 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; }; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE template class SerializedValue { public: explicit SerializedValue(T str) : str_(str) {} operator T() const { return str_; } const char* data() const { return str_.c_str(); } size_t size() const { return str_.length(); } private: T str_; }; template class SerializedValue { public: explicit SerializedValue(TChar* p, size_t n) : data_(p), size_(n) {} operator TChar*() const { return data_; } TChar* data() const { return data_; } size_t size() const { return size_; } private: TChar* data_; size_t size_; }; using RawString = SerializedValue; template inline SerializedValue serialized(T str) { return SerializedValue(str); } template inline SerializedValue serialized(TChar* p) { return SerializedValue(p, detail::adaptString(p).size()); } template inline SerializedValue serialized(TChar* p, size_t n) { return SerializedValue(p, n); } ARDUINOJSON_END_PUBLIC_NAMESPACE #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wconversion" #elif defined(__GNUC__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wconversion" #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE #ifndef isnan template bool isnan(T x) { return x != x; } #endif #ifndef isinf template bool isinf(T x) { return x != 0.0 && x * 2 == x; } #endif template struct alias_cast_t { union { F raw; T data; }; }; template T alias_cast(F raw_data) { alias_cast_t ac; ac.raw = raw_data; return ac.data; } ARDUINOJSON_END_PRIVATE_NAMESPACE #if ARDUINOJSON_ENABLE_PROGMEM #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE #if ARDUINOJSON_ENABLE_PROGMEM # ifndef ARDUINOJSON_DEFINE_PROGMEM_ARRAY # define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, ...) \ static type const name[] PROGMEM = __VA_ARGS__; # endif template inline const T* pgm_read(const T* const* p) { return reinterpret_cast(pgm_read_ptr(p)); } inline uint32_t pgm_read(const uint32_t* p) { return pgm_read_dword(p); } inline double pgm_read(const double* p) { return pgm_read_double(p); } inline float pgm_read(const float* p) { return pgm_read_float(p); } #else # ifndef ARDUINOJSON_DEFINE_PROGMEM_ARRAY # define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, ...) \ static type const name[] = __VA_ARGS__; # endif template inline T pgm_read(const T* p) { return *p; } #endif template class pgm_ptr { public: explicit pgm_ptr(const T* ptr) : ptr_(ptr) {} T operator[](intptr_t index) const { return pgm_read(ptr_ + index); } private: const T* ptr_; }; template struct FloatTraits {}; template struct FloatTraits { typedef uint64_t mantissa_type; static const short mantissa_bits = 52; static const mantissa_type mantissa_max = (mantissa_type(1) << mantissa_bits) - 1; typedef int16_t exponent_type; static const exponent_type exponent_max = 308; static pgm_ptr positiveBinaryPowersOfTen() { ARDUINOJSON_DEFINE_PROGMEM_ARRAY( // uint64_t, factors, { 0x4024000000000000, // 1e1 0x4059000000000000, // 1e2 0x40C3880000000000, // 1e4 0x4197D78400000000, // 1e8 0x4341C37937E08000, // 1e16 0x4693B8B5B5056E17, // 1e32 0x4D384F03E93FF9F5, // 1e64 0x5A827748F9301D32, // 1e128 0x75154FDD7F73BF3C, // 1e256 }); return pgm_ptr(reinterpret_cast(factors)); } static pgm_ptr negativeBinaryPowersOfTen() { ARDUINOJSON_DEFINE_PROGMEM_ARRAY( // uint64_t, factors, { 0x3FB999999999999A, // 1e-1 0x3F847AE147AE147B, // 1e-2 0x3F1A36E2EB1C432D, // 1e-4 0x3E45798EE2308C3A, // 1e-8 0x3C9CD2B297D889BC, // 1e-16 0x3949F623D5A8A733, // 1e-32 0x32A50FFD44F4A73D, // 1e-64 0x255BBA08CF8C979D, // 1e-128 0x0AC8062864AC6F43 // 1e-256 }); return pgm_ptr(reinterpret_cast(factors)); } static T nan() { return forge(0x7ff8000000000000); } static T inf() { return forge(0x7ff0000000000000); } static T highest() { return forge(0x7FEFFFFFFFFFFFFF); } template // int64_t static T highest_for( enable_if_t::value && is_signed::value && sizeof(TOut) == 8, signed>* = 0) { return forge(0x43DFFFFFFFFFFFFF); // 9.2233720368547748e+18 } template // uint64_t static T highest_for( enable_if_t::value && is_unsigned::value && sizeof(TOut) == 8, unsigned>* = 0) { return forge(0x43EFFFFFFFFFFFFF); // 1.8446744073709549568e+19 } static T lowest() { return forge(0xFFEFFFFFFFFFFFFF); } static T forge(uint64_t bits) { return alias_cast(bits); } }; template struct FloatTraits { typedef uint32_t mantissa_type; static const short mantissa_bits = 23; static const mantissa_type mantissa_max = (mantissa_type(1) << mantissa_bits) - 1; typedef int8_t exponent_type; static const exponent_type exponent_max = 38; static pgm_ptr positiveBinaryPowersOfTen() { ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors, { 0x41200000, // 1e1f 0x42c80000, // 1e2f 0x461c4000, // 1e4f 0x4cbebc20, // 1e8f 0x5a0e1bca, // 1e16f 0x749dc5ae // 1e32f }); return pgm_ptr(reinterpret_cast(factors)); } static pgm_ptr negativeBinaryPowersOfTen() { ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors, { 0x3dcccccd, // 1e-1f 0x3c23d70a, // 1e-2f 0x38d1b717, // 1e-4f 0x322bcc77, // 1e-8f 0x24e69595, // 1e-16f 0x0a4fb11f // 1e-32f }); return pgm_ptr(reinterpret_cast(factors)); } static T forge(uint32_t bits) { return alias_cast(bits); } static T nan() { return forge(0x7fc00000); } static T inf() { return forge(0x7f800000); } static T highest() { return forge(0x7f7fffff); } template // int32_t static T highest_for( enable_if_t::value && is_signed::value && sizeof(TOut) == 4, signed>* = 0) { return forge(0x4EFFFFFF); // 2.14748352E9 } template // uint32_t static T highest_for( enable_if_t::value && is_unsigned::value && sizeof(TOut) == 4, unsigned>* = 0) { return forge(0x4F7FFFFF); // 4.29496704E9 } template // int64_t static T highest_for( enable_if_t::value && is_signed::value && sizeof(TOut) == 8, signed>* = 0) { return forge(0x5EFFFFFF); // 9.22337148709896192E18 } template // uint64_t static T highest_for( enable_if_t::value && is_unsigned::value && sizeof(TOut) == 8, unsigned>* = 0) { return forge(0x5F7FFFFF); // 1.844674297419792384E19 } static T lowest() { return forge(0xFf7fffff); } }; template inline TFloat make_float(TFloat m, TExponent e) { using traits = FloatTraits; auto powersOfTen = e > 0 ? traits::positiveBinaryPowersOfTen() : traits::negativeBinaryPowersOfTen(); if (e <= 0) e = TExponent(-e); for (uint8_t index = 0; e != 0; index++) { if (e & 1) m *= powersOfTen[index]; e >>= 1; } return m; } ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE #if ARDUINOJSON_USE_DOUBLE typedef double JsonFloat; #else typedef float JsonFloat; #endif ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template enable_if_t::value && is_unsigned::value && is_integral::value && sizeof(TOut) <= sizeof(TIn), bool> canConvertNumber(TIn value) { return value <= TIn(numeric_limits::highest()); } template enable_if_t::value && is_unsigned::value && is_integral::value && sizeof(TIn) < sizeof(TOut), bool> canConvertNumber(TIn) { return true; } template enable_if_t::value && is_floating_point::value, bool> canConvertNumber(TIn) { return true; } template enable_if_t::value && is_signed::value && is_integral::value && is_signed::value && sizeof(TOut) < sizeof(TIn), bool> canConvertNumber(TIn value) { return value >= TIn(numeric_limits::lowest()) && value <= TIn(numeric_limits::highest()); } template enable_if_t::value && is_signed::value && is_integral::value && is_signed::value && sizeof(TIn) <= sizeof(TOut), bool> canConvertNumber(TIn) { return true; } template enable_if_t::value && is_signed::value && is_integral::value && is_unsigned::value && sizeof(TOut) >= sizeof(TIn), bool> canConvertNumber(TIn value) { if (value < 0) return false; return TOut(value) <= numeric_limits::highest(); } template enable_if_t::value && is_signed::value && is_integral::value && is_unsigned::value && sizeof(TOut) < sizeof(TIn), bool> canConvertNumber(TIn value) { if (value < 0) return false; return value <= TIn(numeric_limits::highest()); } template enable_if_t::value && is_integral::value && sizeof(TOut) < sizeof(TIn), bool> canConvertNumber(TIn value) { return value >= numeric_limits::lowest() && value <= numeric_limits::highest(); } template enable_if_t::value && is_integral::value && sizeof(TOut) >= sizeof(TIn), bool> canConvertNumber(TIn value) { return value >= numeric_limits::lowest() && value <= FloatTraits::template highest_for(); } template enable_if_t::value && is_floating_point::value, bool> canConvertNumber(TIn) { return true; } 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 ResourceManager; 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(); } VariantData* data() { return reinterpret_cast(slot_); } const VariantData* data() const { return reinterpret_cast(slot_); } private: CollectionIterator(VariantData* slot, SlotId slotId); VariantData* 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; 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); } SlotId head() const { return head_; } protected: void appendOne(Slot slot, const ResourceManager* resources); void appendPair(Slot key, Slot value, const ResourceManager* resources); void removeOne(iterator it, ResourceManager* resources); void removePair(iterator it, ResourceManager* resources); private: Slot getPreviousSlot(VariantData*, const ResourceManager*) const; }; inline const VariantData* collectionToVariant( const CollectionData* collection) { const void* data = collection; // prevent warning cast-align return reinterpret_cast(data); } inline VariantData* collectionToVariant(CollectionData* collection) { void* data = collection; // prevent warning cast-align return reinterpret_cast(data); } class ArrayData : public CollectionData { public: VariantData* addElement(ResourceManager* resources); static VariantData* addElement(ArrayData* array, ResourceManager* resources) { if (!array) return nullptr; return array->addElement(resources); } template bool addValue(T&& value, ResourceManager* resources); template static bool addValue(ArrayData* array, T&& value, ResourceManager* resources) { if (!array) return false; return array->addValue(value, resources); } VariantData* getOrAddElement(size_t index, ResourceManager* resources); VariantData* getElement(size_t index, const ResourceManager* resources) const; static VariantData* getElement(const ArrayData* array, size_t index, const ResourceManager* resources) { if (!array) return nullptr; return array->getElement(index, resources); } void removeElement(size_t index, ResourceManager* resources); static void removeElement(ArrayData* array, size_t index, ResourceManager* resources) { if (!array) return; array->removeElement(index, resources); } void remove(iterator it, ResourceManager* resources) { CollectionData::removeOne(it, resources); } static void remove(ArrayData* array, iterator it, ResourceManager* resources) { if (array) return array->remove(it, 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: template // also works with StringNode* VariantData* addMember(TAdaptedString key, ResourceManager* resources); 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); } void remove(iterator it, ResourceManager* resources) { CollectionData::removePair(it, resources); } static void remove(ObjectData* obj, ObjectData::iterator it, ResourceManager* resources) { if (!obj) return; obj->remove(it, resources); } size_t size(const ResourceManager* resources) const { return CollectionData::size(resources) / 2; } static size_t size(const ObjectData* obj, const ResourceManager* resources) { if (!obj) return 0; return obj->size(resources); } private: template iterator findKey(TAdaptedString key, const ResourceManager* resources) const; }; enum class VariantTypeBits : uint8_t { OwnedStringBit = 0x01, // 0000 0001 NumberBit = 0x08, // 0000 1000 #if ARDUINOJSON_USE_EXTENSIONS ExtensionBit = 0x10, // 0001 0000 #endif CollectionMask = 0x60, }; enum class VariantType : uint8_t { Null = 0, // 0000 0000 RawString = 0x03, // 0000 0011 LinkedString = 0x04, // 0000 0100 OwnedString = 0x05, // 0000 0101 Boolean = 0x06, // 0000 0110 Uint32 = 0x0A, // 0000 1010 Int32 = 0x0C, // 0000 1100 Float = 0x0E, // 0000 1110 #if ARDUINOJSON_USE_LONG_LONG Uint64 = 0x1A, // 0001 1010 Int64 = 0x1C, // 0001 1100 #endif #if ARDUINOJSON_USE_DOUBLE Double = 0x1E, // 0001 1110 #endif Object = 0x20, Array = 0x40, }; inline bool operator&(VariantType type, VariantTypeBits bit) { return (uint8_t(type) & uint8_t(bit)) != 0; } union VariantContent { VariantContent() {} float asFloat; bool asBoolean; uint32_t asUint32; int32_t asInt32; #if ARDUINOJSON_USE_EXTENSIONS SlotId asSlotId; #endif ArrayData asArray; ObjectData asObject; CollectionData asCollection; const char* asLinkedString; struct StringNode* asOwnedString; }; #if ARDUINOJSON_USE_EXTENSIONS union VariantExtension { # if ARDUINOJSON_USE_LONG_LONG uint64_t asUint64; int64_t asInt64; # endif # if ARDUINOJSON_USE_DOUBLE double asDouble; # endif }; #endif template T parseNumber(const char* s); class VariantData { VariantContent content_; // must be first to allow cast from array to variant VariantType type_; SlotId next_; public: static void* operator new(size_t, void* p) noexcept { return p; } static void operator delete(void*, void*) noexcept {} VariantData() : type_(VariantType::Null), next_(NULL_SLOT) {} SlotId next() const { return next_; } void setNext(SlotId slot) { next_ = slot; } template typename TVisitor::result_type accept( TVisitor& visit, const ResourceManager* resources) const { #if ARDUINOJSON_USE_EXTENSIONS auto extension = getExtension(resources); #else (void)resources; // silence warning #endif switch (type_) { case VariantType::Float: return visit.visit(content_.asFloat); #if ARDUINOJSON_USE_DOUBLE case VariantType::Double: return visit.visit(extension->asDouble); #endif case VariantType::Array: return visit.visit(content_.asArray); case VariantType::Object: return visit.visit(content_.asObject); case VariantType::LinkedString: return visit.visit(JsonString(content_.asLinkedString)); case VariantType::OwnedString: return visit.visit(JsonString(content_.asOwnedString->data, content_.asOwnedString->length, JsonString::Copied)); case VariantType::RawString: return visit.visit(RawString(content_.asOwnedString->data, content_.asOwnedString->length)); case VariantType::Int32: return visit.visit(static_cast(content_.asInt32)); case VariantType::Uint32: return visit.visit(static_cast(content_.asUint32)); #if ARDUINOJSON_USE_LONG_LONG case VariantType::Int64: return visit.visit(extension->asInt64); case VariantType::Uint64: return visit.visit(extension->asUint64); #endif case VariantType::Boolean: return visit.visit(content_.asBoolean != 0); default: return visit.visit(nullptr); } } template static typename TVisitor::result_type accept(const VariantData* var, const ResourceManager* resources, TVisitor& visit) { if (var != 0) return var->accept(visit, resources); else return visit.visit(nullptr); } VariantData* addElement(ResourceManager* resources) { auto array = isNull() ? &toArray() : asArray(); return detail::ArrayData::addElement(array, resources); } static VariantData* addElement(VariantData* var, ResourceManager* resources) { if (!var) return nullptr; return var->addElement(resources); } template bool addValue(T&& value, ResourceManager* resources) { auto array = isNull() ? &toArray() : asArray(); return detail::ArrayData::addValue(array, detail::forward(value), resources); } template static bool addValue(VariantData* var, T&& value, ResourceManager* resources) { if (!var) return false; return var->addValue(value, resources); } bool asBoolean(const ResourceManager* resources) const { #if ARDUINOJSON_USE_EXTENSIONS auto extension = getExtension(resources); #else (void)resources; // silence warning #endif switch (type_) { case VariantType::Boolean: return content_.asBoolean; case VariantType::Uint32: case VariantType::Int32: return content_.asUint32 != 0; case VariantType::Float: return content_.asFloat != 0; #if ARDUINOJSON_USE_DOUBLE case VariantType::Double: return extension->asDouble != 0; #endif case VariantType::Null: return false; #if ARDUINOJSON_USE_LONG_LONG case VariantType::Uint64: case VariantType::Int64: return extension->asUint64 != 0; #endif 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 ResourceManager* resources) const { static_assert(is_floating_point::value, "T must be a floating point"); #if ARDUINOJSON_USE_EXTENSIONS auto extension = getExtension(resources); #else (void)resources; // silence warning #endif switch (type_) { case VariantType::Boolean: return static_cast(content_.asBoolean); case VariantType::Uint32: return static_cast(content_.asUint32); case VariantType::Int32: return static_cast(content_.asInt32); #if ARDUINOJSON_USE_LONG_LONG case VariantType::Uint64: return static_cast(extension->asUint64); case VariantType::Int64: return static_cast(extension->asInt64); #endif case VariantType::LinkedString: case VariantType::OwnedString: return parseNumber(content_.asOwnedString->data); case VariantType::Float: return static_cast(content_.asFloat); #if ARDUINOJSON_USE_DOUBLE case VariantType::Double: return static_cast(extension->asDouble); #endif default: return 0; } } template T asIntegral(const ResourceManager* resources) const { static_assert(is_integral::value, "T must be an integral type"); #if ARDUINOJSON_USE_EXTENSIONS auto extension = getExtension(resources); #else (void)resources; // silence warning #endif switch (type_) { case VariantType::Boolean: return content_.asBoolean; case VariantType::Uint32: return convertNumber(content_.asUint32); case VariantType::Int32: return convertNumber(content_.asInt32); #if ARDUINOJSON_USE_LONG_LONG case VariantType::Uint64: return convertNumber(extension->asUint64); case VariantType::Int64: return convertNumber(extension->asInt64); #endif case VariantType::LinkedString: return parseNumber(content_.asLinkedString); case VariantType::OwnedString: return parseNumber(content_.asOwnedString->data); case VariantType::Float: return convertNumber(content_.asFloat); #if ARDUINOJSON_USE_DOUBLE case VariantType::Double: return convertNumber(extension->asDouble); #endif 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 VariantType::RawString: return JsonString(content_.asOwnedString->data, content_.asOwnedString->length, JsonString::Copied); default: return JsonString(); } } JsonString asString() const { switch (type_) { case VariantType::LinkedString: return JsonString(content_.asLinkedString, JsonString::Linked); case VariantType::OwnedString: return JsonString(content_.asOwnedString->data, content_.asOwnedString->length, JsonString::Copied); default: return JsonString(); } } #if ARDUINOJSON_USE_EXTENSIONS const VariantExtension* getExtension(const ResourceManager* resources) const; #endif 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 type_ == VariantType::Array; } bool isBoolean() const { return type_ == VariantType::Boolean; } bool isCollection() const { return type_ & VariantTypeBits::CollectionMask; } bool isFloat() const { return type_ & VariantTypeBits::NumberBit; } template bool isInteger(const ResourceManager* resources) const { #if ARDUINOJSON_USE_LONG_LONG auto extension = getExtension(resources); #else (void)resources; // silence warning #endif switch (type_) { case VariantType::Uint32: return canConvertNumber(content_.asUint32); case VariantType::Int32: return canConvertNumber(content_.asInt32); #if ARDUINOJSON_USE_LONG_LONG case VariantType::Uint64: return canConvertNumber(extension->asUint64); case VariantType::Int64: return canConvertNumber(extension->asInt64); #endif default: return false; } } bool isNull() const { return type_ == VariantType::Null; } static bool isNull(const VariantData* var) { if (!var) return true; return var->isNull(); } bool isObject() const { return type_ == VariantType::Object; } bool isString() const { return type_ == VariantType::LinkedString || type_ == VariantType::OwnedString; } 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() { // TODO: remove type_ = VariantType::Null; } void setBoolean(bool value) { ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first type_ = VariantType::Boolean; content_.asBoolean = value; } template enable_if_t setFloat(T value, ResourceManager*) { ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first type_ = VariantType::Float; content_.asFloat = value; return true; } template enable_if_t setFloat(T value, ResourceManager*); template enable_if_t::value, bool> setInteger(T value, ResourceManager* resources); template enable_if_t::value, bool> setInteger( T value, ResourceManager* resources); void setRawString(StringNode* s) { ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first ARDUINOJSON_ASSERT(s); type_ = VariantType::RawString; content_.asOwnedString = s; } template void setRawString(SerializedValue value, ResourceManager* resources); template static void setRawString(VariantData* var, SerializedValue value, ResourceManager* resources) { if (!var) return; var->clear(resources); var->setRawString(value, resources); } template bool setString(TAdaptedString value, ResourceManager* resources); bool setString(StringNode* s, ResourceManager*) { setOwnedString(s); return true; } template static void setString(VariantData* var, TAdaptedString value, ResourceManager* resources) { if (!var) return; var->clear(resources); var->setString(value, resources); } void setLinkedString(const char* s) { ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first ARDUINOJSON_ASSERT(s); type_ = VariantType::LinkedString; content_.asLinkedString = s; } void setOwnedString(StringNode* s) { ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first ARDUINOJSON_ASSERT(s); type_ = VariantType::OwnedString; content_.asOwnedString = s; } size_t size(const ResourceManager* resources) const { if (isObject()) return content_.asObject.size(resources); if (isArray()) return content_.asArray.size(resources); return 0; } static size_t size(const VariantData* var, const ResourceManager* resources) { return var != 0 ? var->size(resources) : 0; } ArrayData& toArray() { ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first type_ = VariantType::Array; new (&content_.asArray) ArrayData(); return content_.asArray; } static ArrayData* toArray(VariantData* var, ResourceManager* resources) { if (!var) return 0; var->clear(resources); return &var->toArray(); } ObjectData& toObject() { ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first type_ = VariantType::Object; new (&content_.asObject) ObjectData(); return content_.asObject; } static ObjectData* toObject(VariantData* var, ResourceManager* resources) { if (!var) return 0; var->clear(resources); return &var->toObject(); } VariantType type() const { return type_; } void clear(ResourceManager* resources); static void clear(VariantData* var, ResourceManager* resources) { if (!var) return; var->clear(resources); } }; class VariantData; class VariantWithId; class ResourceManager { union SlotData { VariantData variant; #if ARDUINOJSON_USE_EXTENSIONS VariantExtension extension; #endif }; public: constexpr static size_t slotSize = sizeof(SlotData); 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 variantPools_.size() + stringPool_.size(); } bool overflowed() const { return overflowed_; } Slot allocVariant(); void freeVariant(Slot slot); VariantData* getVariant(SlotId id) const; #if ARDUINOJSON_USE_EXTENSIONS Slot allocExtension(); void freeExtension(SlotId slot); VariantExtension* getExtension(SlotId id) const; #endif 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_; MemoryPoolList variantPools_; }; template struct IsString : false_type {}; template struct IsString::AdaptedString>> : true_type {}; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class JsonArray; class JsonObject; class JsonVariant; ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template struct VariantTo {}; template <> struct VariantTo { typedef JsonArray type; }; template <> struct VariantTo { typedef JsonObject type; }; template <> struct VariantTo { typedef JsonVariant type; }; class VariantAttorney { public: template static auto getResourceManager(TClient& client) -> decltype(client.getResourceManager()) { return client.getResourceManager(); } template static auto getData(TClient& client) -> decltype(client.getData()) { return client.getData(); } template static VariantData* getOrCreateData(TClient& client) { return client.getOrCreateData(); } }; enum CompareResult { COMPARE_RESULT_DIFFER = 0, COMPARE_RESULT_EQUAL = 1, COMPARE_RESULT_GREATER = 2, COMPARE_RESULT_LESS = 4, COMPARE_RESULT_GREATER_OR_EQUAL = 3, COMPARE_RESULT_LESS_OR_EQUAL = 5 }; template CompareResult arithmeticCompare(const T& lhs, const T& rhs) { if (lhs < rhs) return COMPARE_RESULT_LESS; else if (lhs > rhs) return COMPARE_RESULT_GREATER; else return COMPARE_RESULT_EQUAL; } template CompareResult arithmeticCompare( const T1& lhs, const T2& rhs, enable_if_t::value && is_integral::value && sizeof(T1) < sizeof(T2)>* = 0) { return arithmeticCompare(static_cast(lhs), rhs); } template CompareResult arithmeticCompare( const T1& lhs, const T2& rhs, enable_if_t::value && is_integral::value && sizeof(T2) < sizeof(T1)>* = 0) { return arithmeticCompare(lhs, static_cast(rhs)); } template CompareResult arithmeticCompare( const T1& lhs, const T2& rhs, enable_if_t::value && is_integral::value && is_signed::value == is_signed::value && sizeof(T2) == sizeof(T1)>* = 0) { return arithmeticCompare(lhs, static_cast(rhs)); } template CompareResult arithmeticCompare( const T1& lhs, const T2& rhs, enable_if_t::value && is_integral::value && is_unsigned::value && is_signed::value && sizeof(T2) == sizeof(T1)>* = 0) { if (rhs < 0) return COMPARE_RESULT_GREATER; return arithmeticCompare(lhs, static_cast(rhs)); } template CompareResult arithmeticCompare( const T1& lhs, const T2& rhs, enable_if_t::value && is_integral::value && is_signed::value && is_unsigned::value && sizeof(T2) == sizeof(T1)>* = 0) { if (lhs < 0) return COMPARE_RESULT_LESS; return arithmeticCompare(static_cast(lhs), rhs); } template CompareResult arithmeticCompare( const T1& lhs, const T2& rhs, enable_if_t::value || is_floating_point::value>* = 0) { return arithmeticCompare(static_cast(lhs), static_cast(rhs)); } template CompareResult arithmeticCompareNegateLeft( JsonUInt, const T2&, enable_if_t::value>* = 0) { return COMPARE_RESULT_LESS; } template CompareResult arithmeticCompareNegateLeft( JsonUInt lhs, const T2& rhs, enable_if_t::value>* = 0) { if (rhs > 0) return COMPARE_RESULT_LESS; return arithmeticCompare(-rhs, static_cast(lhs)); } template CompareResult arithmeticCompareNegateRight( const T1&, JsonUInt, enable_if_t::value>* = 0) { return COMPARE_RESULT_GREATER; } template CompareResult arithmeticCompareNegateRight( const T1& lhs, JsonUInt rhs, enable_if_t::value>* = 0) { if (lhs > 0) return COMPARE_RESULT_GREATER; return arithmeticCompare(static_cast(rhs), -lhs); } struct VariantTag {}; template struct IsVariant : is_base_of {}; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class JsonVariantConst; ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template CompareResult compare(JsonVariantConst lhs, const T& rhs); // VariantCompare.cpp struct VariantOperatorTag {}; template struct VariantOperators : VariantOperatorTag { template friend enable_if_t::value && !is_array::value, T> operator|( const TVariant& variant, const T& defaultValue) { if (variant.template is()) return variant.template as(); else return defaultValue; } friend const char* operator|(const TVariant& variant, const char* defaultValue) { if (variant.template is()) return variant.template as(); else return defaultValue; } template friend enable_if_t::value, JsonVariantConst> operator|( const TVariant& variant, T defaultValue) { if (variant) return variant; else return defaultValue; } template friend bool operator==(T* lhs, TVariant rhs) { return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; } template friend bool operator==(const T& lhs, TVariant rhs) { return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; } template friend bool operator==(TVariant lhs, T* rhs) { return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; } template friend enable_if_t::value, bool> operator==(TVariant lhs, const T& rhs) { return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; } template friend bool operator!=(T* lhs, TVariant rhs) { return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; } template friend bool operator!=(const T& lhs, TVariant rhs) { return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; } template friend bool operator!=(TVariant lhs, T* rhs) { return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; } template friend enable_if_t::value, bool> operator!=(TVariant lhs, const T& rhs) { return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; } template friend bool operator<(T* lhs, TVariant rhs) { return compare(rhs, lhs) == COMPARE_RESULT_GREATER; } template friend bool operator<(const T& lhs, TVariant rhs) { return compare(rhs, lhs) == COMPARE_RESULT_GREATER; } template friend bool operator<(TVariant lhs, T* rhs) { return compare(lhs, rhs) == COMPARE_RESULT_LESS; } template friend enable_if_t::value, bool> operator<( TVariant lhs, const T& rhs) { return compare(lhs, rhs) == COMPARE_RESULT_LESS; } template friend bool operator<=(T* lhs, TVariant rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } template friend bool operator<=(const T& lhs, TVariant rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } template friend bool operator<=(TVariant lhs, T* rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template friend enable_if_t::value, bool> operator<=(TVariant lhs, const T& rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template friend bool operator>(T* lhs, TVariant rhs) { return compare(rhs, lhs) == COMPARE_RESULT_LESS; } template friend bool operator>(const T& lhs, TVariant rhs) { return compare(rhs, lhs) == COMPARE_RESULT_LESS; } template friend bool operator>(TVariant lhs, T* rhs) { return compare(lhs, rhs) == COMPARE_RESULT_GREATER; } template friend enable_if_t::value, bool> operator>( TVariant lhs, const T& rhs) { return compare(lhs, rhs) == COMPARE_RESULT_GREATER; } template friend bool operator>=(T* lhs, TVariant rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template friend bool operator>=(const T& lhs, TVariant rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template friend bool operator>=(TVariant lhs, T* rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } template friend enable_if_t::value, bool> operator>=(TVariant lhs, const T& rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } }; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class JsonArray; class JsonObject; class JsonVariantConst : public detail::VariantTag, public detail::VariantOperators { friend class detail::VariantAttorney; template using ConversionSupported = detail::is_same::fromJson)>::arg1_type, JsonVariantConst>; public: JsonVariantConst() : data_(nullptr), resources_(nullptr) {} explicit JsonVariantConst(const detail::VariantData* data, const detail::ResourceManager* resources) : data_(data), resources_(resources) {} bool isNull() const { return detail::VariantData::isNull(data_); } bool isUnbound() const { return !data_; } size_t nesting() const { return detail::VariantData::nesting(data_, resources_); } size_t size() const { return detail::VariantData::size(data_, resources_); } template ::value, bool> = true> T as() const { return Converter::fromJson(*this); } template ::value, bool> = true> detail::InvalidConversion as() const; template detail::enable_if_t::value, bool> is() const { return Converter::checkJson(*this); } template detail::enable_if_t::value, bool> is() const { return false; } template operator T() const { return as(); } template detail::enable_if_t::value, JsonVariantConst> operator[](T index) const { return JsonVariantConst( detail::VariantData::getElement(data_, size_t(index), resources_), resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](const TString& key) const { return JsonVariantConst(detail::VariantData::getMember( data_, detail::adaptString(key), resources_), resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](TChar* key) const { return JsonVariantConst(detail::VariantData::getMember( data_, detail::adaptString(key), resources_), resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](const TVariant& key) const { if (key.template is()) return operator[](key.template as()); else return operator[](key.template as()); } template ARDUINOJSON_DEPRECATED("use var[key].is() instead") detail::enable_if_t::value, bool> containsKey( const TString& key) const { return detail::VariantData::getMember(getData(), detail::adaptString(key), resources_) != 0; } template ARDUINOJSON_DEPRECATED("use obj[\"key\"].is() instead") detail::enable_if_t::value, bool> containsKey( TChar* key) const { return detail::VariantData::getMember(getData(), detail::adaptString(key), resources_) != 0; } template ARDUINOJSON_DEPRECATED("use var[key].is() instead") detail::enable_if_t::value, bool> containsKey( const TVariant& key) const { return containsKey(key.template as()); } ARDUINOJSON_DEPRECATED("always returns zero") size_t memoryUsage() const { return 0; } protected: const detail::VariantData* getData() const { return data_; } const detail::ResourceManager* getResourceManager() const { return resources_; } private: const detail::VariantData* data_; const detail::ResourceManager* resources_; }; class JsonVariant; ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template class ElementProxy; template class MemberProxy; template class VariantRefBase : public VariantTag { friend class VariantAttorney; public: void clear() const { VariantData::clear(getOrCreateData(), getResourceManager()); } bool isNull() const { return VariantData::isNull(getData()); } bool isUnbound() const { return !getData(); } template T as() const; template ::value>> operator T() const { return as(); } template enable_if_t::value, JsonArray> to() const; template enable_if_t::value, JsonObject> to() const; template enable_if_t::value, JsonVariant> to() const; template FORCE_INLINE bool is() const; template bool set(const T& value) const { return doSet>>(value); } template bool set(T* value) const { return doSet>(value); } size_t size() const { return VariantData::size(getData(), getResourceManager()); } size_t nesting() const { return VariantData::nesting(getData(), getResourceManager()); } template enable_if_t::value, T> add() const { return add().template to(); } template enable_if_t::value, T> add() const; template bool add(const T& value) const { return detail::VariantData::addValue(getOrCreateData(), value, getResourceManager()); } template bool add(T* value) const { return detail::VariantData::addValue(getOrCreateData(), value, getResourceManager()); } void remove(size_t index) const { VariantData::removeElement(getData(), index, getResourceManager()); } template enable_if_t::value> remove(TChar* key) const { VariantData::removeMember(getData(), adaptString(key), getResourceManager()); } template enable_if_t::value> remove(const TString& key) const { VariantData::removeMember(getData(), adaptString(key), getResourceManager()); } template enable_if_t::value> remove(const TVariant& key) const { if (key.template is()) remove(key.template as()); else remove(key.template as()); } ElementProxy operator[](size_t index) const; template ARDUINOJSON_DEPRECATED("use obj[key].is() instead") enable_if_t::value, bool> containsKey( const TString& key) const; template ARDUINOJSON_DEPRECATED("use obj[\"key\"].is() instead") enable_if_t::value, bool> containsKey(TChar* key) const; template ARDUINOJSON_DEPRECATED("use obj[key].is() instead") enable_if_t::value, bool> containsKey( const TVariant& key) const; template FORCE_INLINE enable_if_t::value, MemberProxy> operator[](const TString& key) const; template FORCE_INLINE enable_if_t::value, MemberProxy> operator[](TChar* key) const; template enable_if_t::value, JsonVariantConst> operator[]( const TVariant& key) const { if (key.template is()) return operator[](key.template as()); else return operator[](key.template as()); } ARDUINOJSON_DEPRECATED("use add() instead") JsonVariant add() const; ARDUINOJSON_DEPRECATED("use add() instead") JsonArray createNestedArray() const; template ARDUINOJSON_DEPRECATED("use var[key].to() instead") JsonArray createNestedArray(TChar* key) const; template ARDUINOJSON_DEPRECATED("use var[key].to() instead") JsonArray createNestedArray(const TString& key) const; ARDUINOJSON_DEPRECATED("use add() instead") JsonObject createNestedObject() const; template ARDUINOJSON_DEPRECATED("use var[key].to() instead") JsonObject createNestedObject(TChar* key) const; template ARDUINOJSON_DEPRECATED("use var[key].to() instead") JsonObject createNestedObject(const TString& key) const; ARDUINOJSON_DEPRECATED("always returns zero") size_t memoryUsage() const { return 0; } ARDUINOJSON_DEPRECATED("performs a deep copy") void shallowCopy(JsonVariantConst src) const { set(src); } private: TDerived& derived() { return static_cast(*this); } const TDerived& derived() const { return static_cast(*this); } ResourceManager* getResourceManager() const { return VariantAttorney::getResourceManager(derived()); } VariantData* getData() const { return VariantAttorney::getData(derived()); } VariantData* getOrCreateData() const { return VariantAttorney::getOrCreateData(derived()); } FORCE_INLINE ArduinoJson::JsonVariant getVariant() const; FORCE_INLINE ArduinoJson::JsonVariantConst getVariantConst() const { return ArduinoJson::JsonVariantConst(getData(), getResourceManager()); } template FORCE_INLINE enable_if_t::value, T> getVariant() const { return getVariantConst(); } template FORCE_INLINE enable_if_t::value, T> getVariant() const { return getVariant(); } template bool doSet(T&& value) const { return doSet( detail::forward(value), is_same::return_type, bool>{}); } template bool doSet(T&& value, false_type) const; template bool doSet(T&& value, true_type) const; ArduinoJson::JsonVariant getOrCreateVariant() const; }; template class ElementProxy : public VariantRefBase>, public VariantOperators> { friend class VariantAttorney; public: ElementProxy(TUpstream upstream, size_t index) : upstream_(upstream), index_(index) {} ElementProxy(const ElementProxy& src) : upstream_(src.upstream_), index_(src.index_) {} ElementProxy& operator=(const ElementProxy& src) { this->set(src); return *this; } template ElementProxy& operator=(const T& src) { this->set(src); return *this; } template ElementProxy& operator=(T* src) { this->set(src); return *this; } private: ResourceManager* getResourceManager() const { return VariantAttorney::getResourceManager(upstream_); } FORCE_INLINE VariantData* getData() const { return VariantData::getElement( VariantAttorney::getData(upstream_), index_, VariantAttorney::getResourceManager(upstream_)); } VariantData* getOrCreateData() const { auto data = VariantAttorney::getOrCreateData(upstream_); if (!data) return nullptr; return data->getOrAddElement( index_, VariantAttorney::getResourceManager(upstream_)); } TUpstream upstream_; size_t index_; }; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class JsonVariant : public detail::VariantRefBase, public detail::VariantOperators { friend class detail::VariantAttorney; public: JsonVariant() : data_(0), resources_(0) {} JsonVariant(detail::VariantData* data, detail::ResourceManager* resources) : data_(data), resources_(resources) {} private: detail::ResourceManager* getResourceManager() const { return resources_; } detail::VariantData* getData() const { return data_; } detail::VariantData* getOrCreateData() const { return data_; } detail::VariantData* data_; detail::ResourceManager* resources_; }; namespace detail { bool copyVariant(JsonVariant dst, JsonVariantConst src); } template <> struct Converter : private detail::VariantAttorney { static void toJson(JsonVariantConst src, JsonVariant dst) { copyVariant(dst, src); } static JsonVariant fromJson(JsonVariant src) { return src; } static bool checkJson(JsonVariant src) { auto data = getData(src); return !!data; } }; template <> struct Converter : private detail::VariantAttorney { static void toJson(JsonVariantConst src, JsonVariant dst) { copyVariant(dst, src); } static JsonVariantConst fromJson(JsonVariantConst src) { return JsonVariantConst(getData(src), getResourceManager(src)); } static bool checkJson(JsonVariantConst src) { auto data = getData(src); return !!data; } }; template class Ptr { public: Ptr(T value) : value_(value) {} T* operator->() { return &value_; } T& operator*() { return value_; } private: T value_; }; class JsonArrayIterator { friend class JsonArray; public: JsonArrayIterator() {} explicit JsonArrayIterator(detail::ArrayData::iterator iterator, detail::ResourceManager* resources) : iterator_(iterator), resources_(resources) {} JsonVariant operator*() { return JsonVariant(iterator_.data(), resources_); } Ptr operator->() { return operator*(); } bool operator==(const JsonArrayIterator& other) const { return iterator_ == other.iterator_; } bool operator!=(const JsonArrayIterator& other) const { return iterator_ != other.iterator_; } JsonArrayIterator& operator++() { iterator_.next(resources_); return *this; } private: detail::ArrayData::iterator iterator_; detail::ResourceManager* resources_; }; class JsonArrayConstIterator { friend class JsonArray; public: JsonArrayConstIterator() {} explicit JsonArrayConstIterator(detail::ArrayData::iterator iterator, const detail::ResourceManager* resources) : iterator_(iterator), resources_(resources) {} JsonVariantConst operator*() const { return JsonVariantConst(iterator_.data(), resources_); } Ptr operator->() { return operator*(); } bool operator==(const JsonArrayConstIterator& other) const { return iterator_ == other.iterator_; } bool operator!=(const JsonArrayConstIterator& other) const { return iterator_ != other.iterator_; } JsonArrayConstIterator& operator++() { iterator_.next(resources_); return *this; } private: detail::ArrayData::iterator iterator_; const detail::ResourceManager* resources_; }; class JsonObject; class JsonArrayConst : public detail::VariantOperators { friend class JsonArray; friend class detail::VariantAttorney; public: typedef JsonArrayConstIterator iterator; iterator begin() const { if (!data_) return iterator(); return iterator(data_->createIterator(resources_), resources_); } iterator end() const { return iterator(); } JsonArrayConst() : data_(0), resources_(0) {} JsonArrayConst(const detail::ArrayData* data, const detail::ResourceManager* resources) : data_(data), resources_(resources) {} template detail::enable_if_t::value, JsonVariantConst> operator[](T index) const { return JsonVariantConst( detail::ArrayData::getElement(data_, size_t(index), resources_), resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](const TVariant& variant) const { if (variant.template is()) return operator[](variant.template as()); else return JsonVariantConst(); } operator JsonVariantConst() const { return JsonVariantConst(getData(), resources_); } bool isNull() const { return data_ == 0; } operator bool() const { return data_ != 0; } size_t nesting() const { return detail::VariantData::nesting(getData(), resources_); } size_t size() const { return data_ ? data_->size(resources_) : 0; } ARDUINOJSON_DEPRECATED("always returns zero") size_t memoryUsage() const { return 0; } private: const detail::VariantData* getData() const { return collectionToVariant(data_); } const detail::ArrayData* data_; const detail::ResourceManager* resources_; }; inline bool operator==(JsonArrayConst lhs, JsonArrayConst rhs) { if (!lhs && !rhs) return true; if (!lhs || !rhs) return false; auto a = lhs.begin(); auto b = rhs.begin(); for (;;) { if (a == b) // same pointer or both null return true; if (a == lhs.end() || b == rhs.end()) return false; if (*a != *b) return false; ++a; ++b; } } class JsonObject; class JsonArray : public detail::VariantOperators { friend class detail::VariantAttorney; public: typedef JsonArrayIterator iterator; JsonArray() : data_(0), resources_(0) {} JsonArray(detail::ArrayData* data, detail::ResourceManager* resources) : data_(data), resources_(resources) {} operator JsonVariant() { void* data = data_; // prevent warning cast-align return JsonVariant(reinterpret_cast(data), resources_); } operator JsonArrayConst() const { return JsonArrayConst(data_, resources_); } template detail::enable_if_t::value, T> add() const { return add().to(); } template detail::enable_if_t::value, T> add() const { return JsonVariant(detail::ArrayData::addElement(data_, resources_), resources_); } template bool add(const T& value) const { return detail::ArrayData::addValue(data_, value, resources_); } template bool add(T* value) const { return detail::ArrayData::addValue(data_, value, resources_); } iterator begin() const { if (!data_) return iterator(); return iterator(data_->createIterator(resources_), resources_); } iterator end() const { return iterator(); } bool set(JsonArrayConst src) const { if (!data_) return false; clear(); for (auto element : src) { if (!add(element)) return false; } return true; } void remove(iterator it) const { detail::ArrayData::remove(data_, it.iterator_, resources_); } void remove(size_t index) const { detail::ArrayData::removeElement(data_, index, resources_); } template detail::enable_if_t::value> remove( TVariant variant) const { if (variant.template is()) remove(variant.template as()); } void clear() const { detail::ArrayData::clear(data_, resources_); } template detail::enable_if_t::value, detail::ElementProxy> operator[](T index) const { return {*this, size_t(index)}; } template detail::enable_if_t::value, detail::ElementProxy> operator[](const TVariant& variant) const { if (variant.template is()) return operator[](variant.template as()); else return {*this, size_t(-1)}; } operator JsonVariantConst() const { return JsonVariantConst(collectionToVariant(data_), resources_); } bool isNull() const { return data_ == 0; } operator bool() const { return data_ != 0; } size_t nesting() const { return detail::VariantData::nesting(collectionToVariant(data_), resources_); } size_t size() const { return data_ ? data_->size(resources_) : 0; } ARDUINOJSON_DEPRECATED("use add() instead") JsonVariant add() const { return add(); } ARDUINOJSON_DEPRECATED("use add() instead") JsonArray createNestedArray() const { return add(); } ARDUINOJSON_DEPRECATED("use add() instead") JsonObject createNestedObject() const; ARDUINOJSON_DEPRECATED("always returns zero") size_t memoryUsage() const { return 0; } private: detail::ResourceManager* getResourceManager() const { return resources_; } detail::VariantData* getData() const { return collectionToVariant(data_); } detail::VariantData* getOrCreateData() const { return collectionToVariant(data_); } detail::ArrayData* data_; detail::ResourceManager* resources_; }; class JsonPair { public: JsonPair(detail::ObjectData::iterator iterator, detail::ResourceManager* resources) { if (!iterator.done()) { key_ = iterator->asString(); iterator.next(resources); value_ = JsonVariant(iterator.data(), resources); } } JsonString key() const { return key_; } JsonVariant value() { return value_; } private: JsonString key_; JsonVariant value_; }; class JsonPairConst { public: JsonPairConst(detail::ObjectData::iterator iterator, const detail::ResourceManager* resources) { if (!iterator.done()) { key_ = iterator->asString(); iterator.next(resources); value_ = JsonVariantConst(iterator.data(), resources); } } JsonString key() const { return key_; } JsonVariantConst value() const { return value_; } private: JsonString key_; JsonVariantConst value_; }; 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_); // key iterator_.next(resources_); // value 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_); // key iterator_.next(resources_); // value return *this; } private: detail::ObjectData::iterator iterator_; const detail::ResourceManager* resources_; }; class JsonObjectConst : public detail::VariantOperators { friend class JsonObject; friend class detail::VariantAttorney; public: typedef JsonObjectConstIterator iterator; JsonObjectConst() : data_(0), resources_(0) {} JsonObjectConst(const detail::ObjectData* data, const detail::ResourceManager* resources) : data_(data), resources_(resources) {} operator JsonVariantConst() const { return JsonVariantConst(getData(), resources_); } bool isNull() const { return data_ == 0; } operator bool() const { return data_ != 0; } size_t nesting() const { return detail::VariantData::nesting(getData(), resources_); } size_t size() const { return data_ ? data_->size(resources_) : 0; } iterator begin() const { if (!data_) return iterator(); return iterator(data_->createIterator(resources_), resources_); } iterator end() const { return iterator(); } template ARDUINOJSON_DEPRECATED("use obj[key].is() instead") detail::enable_if_t::value, bool> containsKey( const TString& key) const { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } template ARDUINOJSON_DEPRECATED("use obj[\"key\"].is() instead") bool containsKey(TChar* key) const { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } template ARDUINOJSON_DEPRECATED("use obj[key].is() instead") detail::enable_if_t::value, bool> containsKey( const TVariant& key) const { return containsKey(key.template as()); } template detail::enable_if_t::value, JsonVariantConst> operator[](const TString& key) const { return JsonVariantConst(detail::ObjectData::getMember( data_, detail::adaptString(key), resources_), resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](TChar* key) const { return JsonVariantConst(detail::ObjectData::getMember( data_, detail::adaptString(key), resources_), resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](const TVariant& key) const { if (key.template is()) return operator[](key.template as()); else return JsonVariantConst(); } ARDUINOJSON_DEPRECATED("always returns zero") size_t memoryUsage() const { return 0; } private: const detail::VariantData* getData() const { return collectionToVariant(data_); } const detail::ObjectData* data_; const detail::ResourceManager* resources_; }; inline bool operator==(JsonObjectConst lhs, JsonObjectConst rhs) { if (!lhs && !rhs) // both are null return true; if (!lhs || !rhs) // only one is null return false; size_t count = 0; for (auto kvp : lhs) { auto rhsValue = rhs[kvp.key()]; if (rhsValue.isUnbound()) return false; if (kvp.value() != rhsValue) return false; count++; } return count == rhs.size(); } ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template class MemberProxy : public VariantRefBase>, public VariantOperators> { friend class VariantAttorney; public: MemberProxy(TUpstream upstream, TStringRef key) : upstream_(upstream), key_(key) {} MemberProxy(const MemberProxy& src) : upstream_(src.upstream_), key_(src.key_) {} MemberProxy& operator=(const MemberProxy& src) { this->set(src); return *this; } template MemberProxy& operator=(const T& src) { this->set(src); return *this; } template MemberProxy& operator=(T* src) { this->set(src); return *this; } private: ResourceManager* getResourceManager() const { return VariantAttorney::getResourceManager(upstream_); } VariantData* getData() const { return VariantData::getMember( VariantAttorney::getData(upstream_), adaptString(key_), VariantAttorney::getResourceManager(upstream_)); } VariantData* getOrCreateData() const { auto data = VariantAttorney::getOrCreateData(upstream_); if (!data) return nullptr; return data->getOrAddMember(adaptString(key_), VariantAttorney::getResourceManager(upstream_)); } private: TUpstream upstream_; TStringRef key_; }; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class JsonArray; class JsonObject : public detail::VariantOperators { friend class detail::VariantAttorney; public: typedef JsonObjectIterator iterator; JsonObject() : data_(0), resources_(0) {} JsonObject(detail::ObjectData* data, detail::ResourceManager* resource) : data_(data), resources_(resource) {} operator JsonVariant() const { void* data = data_; // prevent warning cast-align return JsonVariant(reinterpret_cast(data), resources_); } operator JsonObjectConst() const { return JsonObjectConst(data_, resources_); } operator JsonVariantConst() const { return JsonVariantConst(collectionToVariant(data_), resources_); } bool isNull() const { return data_ == 0; } operator bool() const { return data_ != 0; } size_t nesting() const { return detail::VariantData::nesting(collectionToVariant(data_), resources_); } size_t size() const { return data_ ? data_->size(resources_) : 0; } iterator begin() const { if (!data_) return iterator(); return iterator(data_->createIterator(resources_), resources_); } iterator end() const { return iterator(); } void clear() const { detail::ObjectData::clear(data_, resources_); } bool set(JsonObjectConst src) { if (!data_ || !src.data_) return false; clear(); for (auto kvp : src) { if (!operator[](kvp.key()).set(kvp.value())) return false; } return true; } template detail::enable_if_t::value, detail::MemberProxy> operator[](const TString& key) const { return {*this, key}; } template detail::enable_if_t::value, detail::MemberProxy> operator[](TChar* key) const { return {*this, key}; } template detail::enable_if_t::value, detail::MemberProxy> operator[](const TVariant& key) const { if (key.template is()) return {*this, key.template as()}; else return {*this, nullptr}; } FORCE_INLINE void remove(iterator it) const { detail::ObjectData::remove(data_, it.iterator_, resources_); } template detail::enable_if_t::value> remove( const TString& key) const { detail::ObjectData::removeMember(data_, detail::adaptString(key), resources_); } template detail::enable_if_t::value> remove( const TVariant& key) const { if (key.template is()) remove(key.template as()); } template FORCE_INLINE void remove(TChar* key) const { detail::ObjectData::removeMember(data_, detail::adaptString(key), resources_); } template ARDUINOJSON_DEPRECATED("use obj[key].is() instead") detail::enable_if_t::value, bool> containsKey( const TString& key) const { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } template ARDUINOJSON_DEPRECATED("use obj[\"key\"].is() instead") detail::enable_if_t::value, bool> containsKey( TChar* key) const { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } template ARDUINOJSON_DEPRECATED("use obj[key].is() instead") detail::enable_if_t::value, bool> containsKey( const TVariant& key) const { return containsKey(key.template as()); } template ARDUINOJSON_DEPRECATED("use obj[key].to() instead") JsonArray createNestedArray(TChar* key) const { return operator[](key).template to(); } template ARDUINOJSON_DEPRECATED("use obj[key].to() instead") JsonArray createNestedArray(const TString& key) const { return operator[](key).template to(); } template ARDUINOJSON_DEPRECATED("use obj[key].to() instead") JsonObject createNestedObject(TChar* key) { return operator[](key).template to(); } template ARDUINOJSON_DEPRECATED("use obj[key].to() instead") JsonObject createNestedObject(const TString& key) { return operator[](key).template to(); } ARDUINOJSON_DEPRECATED("always returns zero") size_t memoryUsage() const { return 0; } private: detail::ResourceManager* getResourceManager() const { return resources_; } detail::VariantData* getData() const { return detail::collectionToVariant(data_); } detail::VariantData* getOrCreateData() const { return detail::collectionToVariant(data_); } detail::ObjectData* data_; detail::ResourceManager* resources_; }; class JsonDocument : public detail::VariantOperators { friend class detail::VariantAttorney; public: explicit JsonDocument(Allocator* alloc = detail::DefaultAllocator::instance()) : resources_(alloc) {} JsonDocument(const JsonDocument& src) : JsonDocument(src.allocator()) { set(src); } JsonDocument(JsonDocument&& src) : JsonDocument(detail::DefaultAllocator::instance()) { swap(*this, src); } template JsonDocument( const T& src, Allocator* alloc = detail::DefaultAllocator::instance(), detail::enable_if_t::value || detail::is_same::value || detail::is_same::value || detail::is_same::value || detail::is_same::value>* = 0) : JsonDocument(alloc) { set(src); } JsonDocument& operator=(JsonDocument src) { swap(*this, src); return *this; } template JsonDocument& operator=(const T& src) { set(src); return *this; } Allocator* allocator() const { return resources_.allocator(); } void shrinkToFit() { resources_.shrinkToFit(); } template T as() { return getVariant().template as(); } template T as() const { return getVariant().template as(); } void clear() { resources_.clear(); data_.reset(); } template bool is() { return getVariant().template is(); } template bool is() const { return getVariant().template is(); } bool isNull() const { return getVariant().isNull(); } bool overflowed() const { return resources_.overflowed(); } size_t nesting() const { return data_.nesting(&resources_); } size_t size() const { return data_.size(&resources_); } bool set(const JsonDocument& src) { return to().set(src.as()); } template detail::enable_if_t::value, bool> set( const T& src) { return to().set(src); } template typename detail::VariantTo::type to() { clear(); return getVariant().template to(); } template ARDUINOJSON_DEPRECATED("use doc[\"key\"].is() instead") bool containsKey(TChar* key) const { return data_.getMember(detail::adaptString(key), &resources_) != 0; } template ARDUINOJSON_DEPRECATED("use doc[key].is() instead") detail::enable_if_t::value, bool> containsKey( const TString& key) const { return data_.getMember(detail::adaptString(key), &resources_) != 0; } template ARDUINOJSON_DEPRECATED("use doc[key].is() instead") detail::enable_if_t::value, bool> containsKey( const TVariant& key) const { return containsKey(key.template as()); } template detail::enable_if_t::value, detail::MemberProxy> operator[](const TString& key) { return {*this, key}; } template detail::enable_if_t::value, detail::MemberProxy> operator[](TChar* key) { return {*this, key}; } template detail::enable_if_t::value, JsonVariantConst> operator[](const TString& key) const { return JsonVariantConst( data_.getMember(detail::adaptString(key), &resources_), &resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](TChar* key) const { return JsonVariantConst( data_.getMember(detail::adaptString(key), &resources_), &resources_); } template detail::enable_if_t::value, detail::ElementProxy> operator[](T index) { return {*this, size_t(index)}; } JsonVariantConst operator[](size_t index) const { return JsonVariantConst(data_.getElement(index, &resources_), &resources_); } template detail::enable_if_t::value, JsonVariantConst> operator[](const TVariant& key) const { if (key.template is()) return operator[](key.template as()); if (key.template is()) return operator[](key.template as()); return {}; } template detail::enable_if_t::value, T> add() { return add().to(); } template detail::enable_if_t::value, T> add() { return JsonVariant(data_.addElement(&resources_), &resources_); } template bool add(const TValue& value) { return data_.addValue(value, &resources_); } template bool add(TChar* value) { return data_.addValue(value, &resources_); } template detail::enable_if_t::value> remove(T index) { detail::VariantData::removeElement(getData(), size_t(index), getResourceManager()); } template detail::enable_if_t::value> remove(TChar* key) { detail::VariantData::removeMember(getData(), detail::adaptString(key), getResourceManager()); } template detail::enable_if_t::value> remove( const TString& key) { detail::VariantData::removeMember(getData(), detail::adaptString(key), getResourceManager()); } template detail::enable_if_t::value> remove( const TVariant& key) { if (key.template is()) remove(key.template as()); if (key.template is()) remove(key.template as()); } operator JsonVariant() { return getVariant(); } operator JsonVariantConst() const { return getVariant(); } friend void swap(JsonDocument& a, JsonDocument& b) { swap(a.resources_, b.resources_); swap_(a.data_, b.data_); } ARDUINOJSON_DEPRECATED("use add() instead") JsonVariant add() { return add(); } ARDUINOJSON_DEPRECATED("use add() instead") JsonArray createNestedArray() { return add(); } template ARDUINOJSON_DEPRECATED("use doc[key].to() instead") JsonArray createNestedArray(TChar* key) { return operator[](key).template to(); } template ARDUINOJSON_DEPRECATED("use doc[key].to() instead") JsonArray createNestedArray(const TString& key) { return operator[](key).template to(); } ARDUINOJSON_DEPRECATED("use add() instead") JsonObject createNestedObject() { return add(); } template ARDUINOJSON_DEPRECATED("use doc[key].to() instead") JsonObject createNestedObject(TChar* key) { return operator[](key).template to(); } template ARDUINOJSON_DEPRECATED("use doc[key].to() instead") JsonObject createNestedObject(const TString& key) { return operator[](key).template to(); } ARDUINOJSON_DEPRECATED("always returns zero") size_t memoryUsage() const { return 0; } private: JsonVariant getVariant() { return JsonVariant(&data_, &resources_); } JsonVariantConst getVariant() const { return JsonVariantConst(&data_, &resources_); } detail::ResourceManager* getResourceManager() { return &resources_; } detail::VariantData* getData() { return &data_; } const detail::VariantData* getData() const { return &data_; } detail::VariantData* getOrCreateData() { return &data_; } detail::ResourceManager resources_; detail::VariantData data_; }; inline void convertToJson(const JsonDocument& src, JsonVariant dst) { dst.set(src.as()); } ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template struct VariantDataVisitor { typedef TResult result_type; template TResult visit(const T&) { return TResult(); } }; template struct JsonVariantVisitor { typedef TResult result_type; template TResult visit(const T&) { return TResult(); } }; template class VisitorAdapter { public: using result_type = typename TVisitor::result_type; VisitorAdapter(TVisitor& visitor, const ResourceManager* resources) : visitor_(&visitor), resources_(resources) {} result_type visit(const ArrayData& value) { return visitor_->visit(JsonArrayConst(&value, resources_)); } result_type visit(const ObjectData& value) { return visitor_->visit(JsonObjectConst(&value, resources_)); } template result_type visit(const T& value) { return visitor_->visit(value); } private: TVisitor* visitor_; const ResourceManager* resources_; }; template typename TVisitor::result_type accept(JsonVariantConst variant, TVisitor& visit) { auto data = VariantAttorney::getData(variant); if (!data) return visit.visit(nullptr); auto resources = VariantAttorney::getResourceManager(variant); VisitorAdapter adapter(visit, resources); return data->accept(adapter, resources); } struct ComparerBase : JsonVariantVisitor {}; template struct Comparer; template struct Comparer::value>> : 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< T, enable_if_t::value || is_floating_point::value>> : ComparerBase { T rhs; explicit Comparer(T value) : rhs(value) {} template enable_if_t::value || is_integral::value, CompareResult> visit(const U& lhs) { return arithmeticCompare(lhs, rhs); } template enable_if_t::value && !is_integral::value, CompareResult> visit(const U& lhs) { return ComparerBase::visit(lhs); } }; 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< T, enable_if_t::value>> : VariantComparer { explicit Comparer(const T& value) : VariantComparer(static_cast(value)) {} }; template CompareResult compare(ArduinoJson::JsonVariantConst lhs, const T& rhs) { Comparer comparer(rhs); return accept(lhs, comparer); } inline ArrayData::iterator ArrayData::at( size_t index, const ResourceManager* resources) const { auto it = createIterator(resources); while (!it.done() && index) { it.next(resources); --index; } return it; } inline VariantData* ArrayData::addElement(ResourceManager* resources) { auto slot = resources->allocVariant(); if (!slot) return nullptr; CollectionData::appendOne(slot, resources); return slot.ptr(); } inline VariantData* ArrayData::getOrAddElement(size_t index, ResourceManager* resources) { auto it = createIterator(resources); while (!it.done() && index > 0) { it.next(resources); index--; } if (it.done()) index++; VariantData* element = it.data(); while (index > 0) { element = addElement(resources); if (!element) return nullptr; index--; } return element; } inline VariantData* ArrayData::getElement( size_t index, const ResourceManager* resources) const { return at(index, resources).data(); } inline void ArrayData::removeElement(size_t index, ResourceManager* resources) { remove(at(index, resources), resources); } template inline bool ArrayData::addValue(T&& value, ResourceManager* resources) { ARDUINOJSON_ASSERT(resources != nullptr); auto slot = resources->allocVariant(); if (!slot) return false; JsonVariant variant(slot.ptr(), resources); if (!variant.set(detail::forward(value))) { resources->freeVariant(slot); return false; } CollectionData::appendOne(slot, resources); return true; } constexpr size_t sizeofArray(size_t n) { return n * ResourceManager::slotSize; } ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE template inline detail::enable_if_t::value, bool> copyArray( const T& src, JsonVariant dst) { return dst.set(src); } template inline detail::enable_if_t< !detail::is_base_of::value, bool> copyArray(T (&src)[N], const TDestination& dst) { return copyArray(src, N, dst); } template inline detail::enable_if_t< !detail::is_base_of::value, bool> copyArray(const T* src, size_t len, const TDestination& dst) { bool ok = true; for (size_t i = 0; i < len; i++) { ok &= copyArray(src[i], dst.template add()); } return ok; } template inline bool copyArray(const char* src, size_t, const TDestination& dst) { return dst.set(src); } template inline bool copyArray(const T& src, JsonDocument& dst) { return copyArray(src, dst.to()); } template inline bool copyArray(const T* src, size_t len, JsonDocument& dst) { return copyArray(src, len, dst.to()); } template inline detail::enable_if_t::value, size_t> copyArray( JsonVariantConst src, T& dst) { dst = src.as(); return 1; } template inline size_t copyArray(JsonArrayConst src, T (&dst)[N]) { return copyArray(src, dst, N); } template inline size_t copyArray(JsonArrayConst src, T* dst, size_t len) { size_t i = 0; for (JsonArrayConst::iterator it = src.begin(); it != src.end() && i < len; ++it) copyArray(*it, dst[i++]); return i; } template inline size_t copyArray(JsonVariantConst src, char (&dst)[N]) { JsonString s = src; size_t len = N - 1; if (len > s.size()) len = s.size(); memcpy(dst, s.c_str(), len); dst[len] = 0; return 1; } template inline detail::enable_if_t::value && detail::is_base_of::value, size_t> copyArray(const TSource& src, T& dst) { return copyArray(src.template as(), dst); } ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE #if ARDUINOJSON_ENABLE_ALIGNMENT inline bool isAligned(size_t value) { const size_t mask = sizeof(void*) - 1; size_t addr = value; return (addr & mask) == 0; } inline size_t addPadding(size_t bytes) { const size_t mask = sizeof(void*) - 1; return (bytes + mask) & ~mask; } template struct AddPadding { static const size_t mask = sizeof(void*) - 1; static const size_t value = (bytes + mask) & ~mask; }; #else inline bool isAligned(size_t) { return true; } inline size_t addPadding(size_t bytes) { return bytes; } template struct AddPadding { static const size_t value = bytes; }; #endif template inline bool isAligned(T* ptr) { return isAligned(reinterpret_cast(ptr)); } template inline T* addPadding(T* p) { size_t address = addPadding(reinterpret_cast(p)); return reinterpret_cast(address); } inline CollectionIterator::CollectionIterator(VariantData* slot, SlotId slotId) : slot_(slot), currentId_(slotId) { nextId_ = slot_ ? slot_->next() : NULL_SLOT; } inline void CollectionIterator::next(const ResourceManager* resources) { ARDUINOJSON_ASSERT(currentId_ != NULL_SLOT); slot_ = resources->getVariant(nextId_); currentId_ = nextId_; if (slot_) nextId_ = slot_->next(); } inline CollectionData::iterator CollectionData::createIterator( const ResourceManager* resources) const { return iterator(resources->getVariant(head_), head_); } inline void CollectionData::appendOne(Slot slot, const ResourceManager* resources) { if (tail_ != NULL_SLOT) { auto tail = resources->getVariant(tail_); tail->setNext(slot.id()); tail_ = slot.id(); } else { head_ = slot.id(); tail_ = slot.id(); } } inline void CollectionData::appendPair(Slot key, Slot value, const ResourceManager* resources) { key->setNext(value.id()); if (tail_ != NULL_SLOT) { auto tail = resources->getVariant(tail_); tail->setNext(key.id()); tail_ = value.id(); } else { head_ = key.id(); tail_ = value.id(); } } inline void CollectionData::clear(ResourceManager* resources) { auto next = head_; while (next != NULL_SLOT) { auto currId = next; auto slot = resources->getVariant(next); next = slot->next(); resources->freeVariant({slot, currId}); } head_ = NULL_SLOT; tail_ = NULL_SLOT; } inline Slot CollectionData::getPreviousSlot( VariantData* target, const ResourceManager* resources) const { auto prev = Slot(); auto currentId = head_; while (currentId != NULL_SLOT) { auto currentSlot = resources->getVariant(currentId); if (currentSlot == target) break; prev = Slot(currentSlot, currentId); currentId = currentSlot->next(); } return prev; } inline void CollectionData::removeOne(iterator it, ResourceManager* resources) { if (it.done()) return; auto curr = it.slot_; auto prev = getPreviousSlot(curr, resources); auto next = curr->next(); if (prev) prev->setNext(next); else head_ = next; if (next == NULL_SLOT) tail_ = prev.id(); resources->freeVariant({it.slot_, it.currentId_}); } inline void CollectionData::removePair(ObjectData::iterator it, ResourceManager* resources) { if (it.done()) return; auto keySlot = it.slot_; auto valueId = it.nextId_; auto valueSlot = resources->getVariant(valueId); keySlot->setNext(valueSlot->next()); resources->freeVariant({valueSlot, valueId}); removeOne(it, 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 Slot ResourceManager::allocVariant() { auto p = variantPools_.allocSlot(allocator_); if (!p) { overflowed_ = true; return {}; } return {new (&p->variant) VariantData, p.id()}; } inline void ResourceManager::freeVariant(Slot variant) { variant->clear(this); variantPools_.freeSlot({alias_cast(variant.ptr()), variant.id()}); } inline VariantData* ResourceManager::getVariant(SlotId id) const { return reinterpret_cast(variantPools_.getSlot(id)); } #if ARDUINOJSON_USE_EXTENSIONS inline Slot ResourceManager::allocExtension() { auto p = variantPools_.allocSlot(allocator_); if (!p) { overflowed_ = true; return {}; } return {&p->extension, p.id()}; } inline void ResourceManager::freeExtension(SlotId id) { auto p = getExtension(id); variantPools_.freeSlot({reinterpret_cast(p), id}); } inline VariantExtension* ResourceManager::getExtension(SlotId id) const { return &variantPools_.getSlot(id)->extension; } #endif template inline VariantData* ObjectData::getMember( TAdaptedString key, const ResourceManager* resources) const { auto it = findKey(key, resources); if (it.done()) return nullptr; it.next(resources); return it.data(); } template VariantData* ObjectData::getOrAddMember(TAdaptedString key, ResourceManager* resources) { auto data = getMember(key, resources); if (data) return data; return addMember(key, resources); } template inline ObjectData::iterator ObjectData::findKey( TAdaptedString key, const ResourceManager* resources) const { if (key.isNull()) return iterator(); bool isKey = true; for (auto it = createIterator(resources); !it.done(); it.next(resources)) { if (isKey && stringEquals(key, adaptString(it->asString()))) return it; isKey = !isKey; } return iterator(); } template inline void ObjectData::removeMember(TAdaptedString key, ResourceManager* resources) { remove(findKey(key, resources), resources); } template inline VariantData* ObjectData::addMember(TAdaptedString key, ResourceManager* resources) { auto keySlot = resources->allocVariant(); if (!keySlot) return nullptr; auto valueSlot = resources->allocVariant(); if (!valueSlot) return nullptr; if (!keySlot->setString(key, resources)) return nullptr; CollectionData::appendPair(keySlot, valueSlot, resources); return valueSlot.ptr(); } constexpr size_t sizeofObject(size_t n) { return 2 * n * ResourceManager::slotSize; } 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 isSerializing) { return &"//''\"\"\\\\b\bf\fn\nr\rt\t"[isSerializing ? 4 : 0]; } }; struct FloatParts { uint32_t integral; uint32_t decimal; int16_t exponent; int8_t decimalPlaces; }; template inline 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; } constexpr uint32_t pow10(int exponent) { return (exponent == 0) ? 1 : 10 * pow10(exponent - 1); } inline FloatParts decomposeFloat(JsonFloat value, int8_t decimalPlaces) { uint32_t maxDecimalPart = pow10(decimalPlaces); int16_t exponent = normalize(value); uint32_t integral = uint32_t(value); for (uint32_t tmp = integral; tmp >= 10; tmp /= 10) { maxDecimalPart /= 10; decimalPlaces--; } JsonFloat remainder = (value - JsonFloat(integral)) * JsonFloat(maxDecimalPart); uint32_t decimal = uint32_t(remainder); remainder = remainder - JsonFloat(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--; } return {integral, decimal, exponent, decimalPlaces}; } 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) { writeFloat(JsonFloat(value), sizeof(T) >= 8 ? 9 : 6); } void writeFloat(JsonFloat value, int8_t decimalPlaces) { 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 auto parts = decomposeFloat(value, decimalPlaces); writeInteger(parts.integral); if (parts.decimalPlaces) writeDecimals(parts.decimal, parts.decimalPlaces); if (parts.exponent) { writeRaw('e'); writeInteger(parts.exponent); } } template enable_if_t::value> writeInteger(T value) { typedef make_unsigned_t unsigned_type; unsigned_type unsigned_value; if (value < 0) { writeRaw('-'); unsigned_value = unsigned_type(unsigned_type(~value) + 1); } else { unsigned_value = unsigned_type(value); } writeInteger(unsigned_value); } template enable_if_t::value> writeInteger(T value) { char buffer[22]; char* end = buffer + sizeof(buffer); char* begin = end; do { *--begin = char(value % 10 + '0'); value = T(value / 10); } while (value); writeRaw(begin, end); } void writeDecimals(uint32_t value, int8_t width) { char buffer[16]; char* end = buffer + sizeof(buffer); char* begin = end; while (width--) { *--begin = char(value % 10 + '0'); value /= 10; } *--begin = '.'; writeRaw(begin, end); } void writeRaw(const char* s) { writer_.write(reinterpret_cast(s), strlen(s)); } void writeRaw(const char* s, size_t n) { writer_.write(reinterpret_cast(s), n); } void writeRaw(const char* begin, const char* end) { writer_.write(reinterpret_cast(begin), static_cast(end - begin)); } template void writeRaw(const char (&s)[N]) { writer_.write(reinterpret_cast(s), N - 1); } void writeRaw(char c) { writer_.write(static_cast(c)); } protected: CountingDecorator writer_; }; class DummyWriter { public: size_t write(uint8_t) { return 1; } size_t write(const uint8_t*, size_t n) { return n; } }; template