diff --git a/Software/src/lib/bblanchon-ArduinoJson/ArduinoJson.h b/Software/src/lib/bblanchon-ArduinoJson/ArduinoJson.h index 7ef4fa1a..644d19c6 100644 --- a/Software/src/lib/bblanchon-ArduinoJson/ArduinoJson.h +++ b/Software/src/lib/bblanchon-ArduinoJson/ArduinoJson.h @@ -53,9 +53,6 @@ # define ARDUINOJSON_ENABLE_STRING_VIEW 0 # endif #endif -#ifndef ARDUINOJSON_USE_DOUBLE -# define ARDUINOJSON_USE_DOUBLE 1 -#endif #ifndef ARDUINOJSON_SIZEOF_POINTER # if defined(__SIZEOF_POINTER__) # define ARDUINOJSON_SIZEOF_POINTER __SIZEOF_POINTER__ @@ -65,6 +62,13 @@ # 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 @@ -77,20 +81,20 @@ #endif #ifndef ARDUINOJSON_SLOT_ID_SIZE # if ARDUINOJSON_SIZEOF_POINTER <= 2 -# define ARDUINOJSON_SLOT_ID_SIZE 1 // up to 255 slots +# define ARDUINOJSON_SLOT_ID_SIZE 1 # elif ARDUINOJSON_SIZEOF_POINTER == 4 -# define ARDUINOJSON_SLOT_ID_SIZE 2 // up to 65535 slots +# define ARDUINOJSON_SLOT_ID_SIZE 2 # else -# define ARDUINOJSON_SLOT_ID_SIZE 4 // up to 4294967295 slots +# define ARDUINOJSON_SLOT_ID_SIZE 4 # endif #endif #ifndef ARDUINOJSON_POOL_CAPACITY -# if ARDUINOJSON_SIZEOF_POINTER <= 2 -# define ARDUINOJSON_POOL_CAPACITY 16 // 128 bytes -# elif ARDUINOJSON_SIZEOF_POINTER == 4 -# define ARDUINOJSON_POOL_CAPACITY 64 // 1024 bytes +# 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 128 // 3072 bytes +# define ARDUINOJSON_POOL_CAPACITY 256 // 4096 bytes # endif #endif #ifndef ARDUINOJSON_INITIAL_POOL_COUNT @@ -189,6 +193,11 @@ # 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 @@ -206,8 +215,12 @@ #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_CONCAT2(A, B), ARDUINOJSON_CONCAT2(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 @@ -226,22 +239,22 @@ #define ARDUINOJSON_BIN2ALPHA_1111() P #define ARDUINOJSON_BIN2ALPHA_(A, B, C, D) ARDUINOJSON_BIN2ALPHA_##A##B##C##D() #define ARDUINOJSON_BIN2ALPHA(A, B, C, D) ARDUINOJSON_BIN2ALPHA_(A, B, C, D) -#define ARDUINOJSON_VERSION "7.0.4" +#define ARDUINOJSON_VERSION "7.3.0" #define ARDUINOJSON_VERSION_MAJOR 7 -#define ARDUINOJSON_VERSION_MINOR 0 -#define ARDUINOJSON_VERSION_REVISION 4 -#define ARDUINOJSON_VERSION_MACRO V704 +#define ARDUINOJSON_VERSION_MINOR 3 +#define ARDUINOJSON_VERSION_REVISION 0 +#define ARDUINOJSON_VERSION_MACRO V730 #ifndef ARDUINOJSON_VERSION_NAMESPACE -# define ARDUINOJSON_VERSION_NAMESPACE \ - ARDUINOJSON_CONCAT4(ARDUINOJSON_VERSION_MACRO, \ - ARDUINOJSON_BIN2ALPHA(ARDUINOJSON_ENABLE_PROGMEM, \ - ARDUINOJSON_USE_LONG_LONG, \ - ARDUINOJSON_USE_DOUBLE, 1), \ - ARDUINOJSON_BIN2ALPHA(ARDUINOJSON_ENABLE_NAN, \ - ARDUINOJSON_ENABLE_INFINITY, \ - ARDUINOJSON_ENABLE_COMMENTS, \ - ARDUINOJSON_DECODE_UNICODE), \ - ARDUINOJSON_SLOT_ID_SIZE) +# 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 { \ @@ -264,8 +277,6 @@ ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template class InvalidConversion; // Error here? See https://arduinojson.org/v7/invalid-conversion/ -template -struct ConverterNeedsWriteableRef; ARDUINOJSON_END_PRIVATE_NAMESPACE #include #include @@ -309,39 +320,155 @@ ARDUINOJSON_END_PUBLIC_NAMESPACE #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template -struct uint_t; +struct uint_; template <> -struct uint_t<8> { - typedef uint8_t type; +struct uint_<8> { + using type = uint8_t; }; template <> -struct uint_t<16> { - typedef uint16_t type; +struct uint_<16> { + using type = uint16_t; }; template <> -struct uint_t<32> { - typedef uint32_t type; +struct uint_<32> { + using type = uint32_t; +}; +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; + using type = TrueType; }; template struct conditional { - typedef FalseType type; + using type = FalseType; }; +template +using conditional_t = + typename conditional::type; +template +struct decay { + using type = T; +}; +template +struct decay : decay {}; +template +struct decay : decay {}; +template +struct decay : decay {}; +template +struct decay : decay {}; +template +using decay_t = typename decay::type; template struct enable_if {}; template struct enable_if { - typedef T type; + using type = T; +}; +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 +using bool_constant = integral_constant; +using true_type = bool_constant; +using false_type = bool_constant; template struct is_array : false_type {}; template @@ -350,12 +477,14 @@ template struct is_array : true_type {}; template struct remove_reference { - typedef T type; + using type = T; }; template struct remove_reference { - typedef T type; + using type = T; }; +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" @@ -363,8 +492,8 @@ class is_base_of { static char probe(...); public: static const bool value = - sizeof(probe(reinterpret_cast::type*>( - 0))) == sizeof(int); + sizeof(probe(reinterpret_cast*>(0))) == + sizeof(int); }; template T&& declval(); @@ -396,7 +525,7 @@ 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_; + static const From& from_; public: static const bool value = sizeof(probe(from_)) == sizeof(int); }; @@ -414,43 +543,44 @@ template struct is_same : true_type {}; template struct remove_cv { - typedef T type; + using type = T; }; template struct remove_cv { - typedef T type; + using type = T; }; template struct remove_cv { - typedef T type; + using type = T; }; template struct remove_cv { - typedef T type; + using type = T; }; +template +using remove_cv_t = typename remove_cv::type; template struct is_floating_point - : integral_constant< - bool, // - is_same::type>::value || - is_same::type>::value> {}; + : integral_constant>::value || + is_same>::value> {}; template struct is_integral : integral_constant::type, signed char>::value || - is_same::type, unsigned char>::value || - is_same::type, signed short>::value || - is_same::type, unsigned short>::value || - is_same::type, signed int>::value || - is_same::type, unsigned int>::value || - is_same::type, signed long>::value || - is_same::type, unsigned long>::value || - is_same::type, signed long long>::value || - is_same::type, unsigned long long>::value || - is_same::type, char>::value || - is_same::type, bool>::value> {}; + is_same, signed char>::value || + is_same, unsigned char>::value || + is_same, signed short>::value || + is_same, unsigned short>::value || + is_same, signed int>::value || + is_same, unsigned int>::value || + is_same, signed long>::value || + is_same, unsigned long>::value || + is_same, signed long long>::value || + is_same, unsigned long long>::value || + is_same, char>::value || + is_same, bool>::value> {}; template struct is_enum { - static const bool value = is_convertible::value && + static const bool value = is_convertible::value && !is_class::value && !is_integral::value && !is_floating_point::value; }; @@ -460,25 +590,25 @@ template struct is_pointer : true_type {}; template struct is_signed : integral_constant::type, char>::value || - is_same::type, signed char>::value || - is_same::type, signed short>::value || - is_same::type, signed int>::value || - is_same::type, signed long>::value || - is_same::type, signed long long>::value || - is_same::type, float>::value || - is_same::type, double>::value> {}; + is_same, 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::type, unsigned char>::value || - is_same::type, unsigned short>::value || - is_same::type, unsigned int>::value || - is_same::type, unsigned long>::value || - is_same::type, unsigned long long>::value || - is_same::type, bool>::value> {}; + is_same, 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; + using type = T; }; template struct make_unsigned; @@ -504,17 +634,204 @@ template <> struct make_unsigned : type_identity {}; template <> struct make_unsigned : type_identity {}; -template -struct make_void { - typedef void type; -}; +template +using make_unsigned_t = typename make_unsigned::type; template struct remove_const { - typedef T type; + using type = T; }; template struct remove_const { - typedef T type; + using type = T; +}; +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 @@ -525,7 +842,7 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template struct numeric_limits; template -struct numeric_limits::value>::type> { +struct numeric_limits::value>> { static constexpr T lowest() { return 0; } @@ -535,7 +852,7 @@ struct numeric_limits::value>::type> { }; template struct numeric_limits< - T, typename enable_if::value && is_signed::value>::type> { + T, enable_if_t::value && is_signed::value>> { static constexpr T lowest() { return T(T(1) << (sizeof(T) * 8 - 1)); } @@ -549,8 +866,8 @@ ARDUINOJSON_END_PRIVATE_NAMESPACE #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE struct StringNode { - using references_type = uint_t::type; - using length_type = uint_t::type; + using references_type = uint_t; + using length_type = uint_t; struct StringNode* next; references_type references; length_type length; @@ -562,8 +879,10 @@ struct StringNode { static StringNode* create(size_t length, Allocator* allocator) { if (length > maxLength) return nullptr; - auto node = reinterpret_cast( - allocator->allocate(sizeForLength(length))); + 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; @@ -592,23 +911,7 @@ struct StringNode { constexpr size_t sizeofString(size_t n) { return StringNode::sizeForLength(n); } -using nullptr_t = decltype(nullptr); -template -T&& forward(typename remove_reference::type& t) noexcept { - return static_cast(t); -} -template -typename remove_reference::type&& move(T&& t) { - return static_cast::type&&>(t); -} -template -void swap_(T& a, T& b) { - T tmp = move(a); - a = move(b); - b = move(tmp); -} ARDUINOJSON_END_PRIVATE_NAMESPACE -#include #ifdef _MSC_VER // Visual Studio # define FORCE_INLINE // __forceinline causes C4714 when returning std::string # ifndef ARDUINOJSON_DEPRECATED @@ -639,35 +942,52 @@ ARDUINOJSON_END_PRIVATE_NAMESPACE # define ARDUINOJSON_NO_SANITIZE(check) #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct IsStringLiteral : false_type {}; +template +struct IsStringLiteral : true_type {}; template struct StringAdapter; template struct SizedStringAdapter; template -typename StringAdapter::AdaptedString adaptString(const TString& s) { - return StringAdapter::adapt(s); +using StringAdapterFor = + StringAdapter::value, TString, + remove_cv_t>>>; +template +using AdaptedString = typename StringAdapterFor::AdaptedString; +template +AdaptedString adaptString(TString&& s) { + return StringAdapterFor::adapt(detail::forward(s)); } -template -typename StringAdapter::AdaptedString adaptString(TChar* p) { +template ::value, int> = 0> +AdaptedString adaptString(TChar* p) { return StringAdapter::adapt(p); } template -typename SizedStringAdapter::AdaptedString adaptString(TChar* p, - size_t n) { +AdaptedString adaptString(TChar* p, size_t n) { return SizedStringAdapter::adapt(p, n); } template struct IsChar : integral_constant::value && sizeof(T) == 1> {}; -class ZeroTerminatedRamString { +class RamString { public: - static const size_t typeSortKey = 3; - ZeroTerminatedRamString(const char* str) : str_(str) {} + static const size_t typeSortKey = 2; +#if ARDUINOJSON_SIZEOF_POINTER <= 2 + static constexpr size_t sizeMask = size_t(-1) >> 1; +#else + static constexpr size_t sizeMask = size_t(-1); +#endif + RamString(const char* str, size_t sz, bool isStatic = false) + : str_(str), size_(sz & sizeMask), static_(isStatic) { + ARDUINOJSON_ASSERT(size_ == sz); + } bool isNull() const { return !str_; } - FORCE_INLINE size_t size() const { - return str_ ? ::strlen(str_) : 0; + size_t size() const { + return size_; } char operator[](size_t i) const { ARDUINOJSON_ASSERT(str_ != 0); @@ -677,183 +997,71 @@ class ZeroTerminatedRamString { 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; + bool isStatic() const { + return static_; } protected: const char* str_; +#if ARDUINOJSON_SIZEOF_POINTER <= 2 + size_t size_ : sizeof(size_t) * 8 - 1; + bool static_ : 1; +#else + size_t size_; + bool static_; +#endif }; template -struct StringAdapter::value>::type> { - typedef ZeroTerminatedRamString AdaptedString; +struct StringAdapter::value>> { + using AdaptedString = RamString; static AdaptedString adapt(const TChar* p) { - return AdaptedString(reinterpret_cast(p)); + auto str = reinterpret_cast(p); + return AdaptedString(str, str ? ::strlen(str) : 0); + } +}; +template +struct StringAdapter { + using AdaptedString = RamString; + static AdaptedString adapt(const char (&p)[N]) { + return RamString(p, N - 1, true); } }; template -struct StringAdapter::value>::type> { - typedef ZeroTerminatedRamString AdaptedString; +struct StringAdapter::value>> { + using AdaptedString = RamString; static AdaptedString adapt(const TChar* p) { - return AdaptedString(reinterpret_cast(p)); + auto str = reinterpret_cast(p); + return AdaptedString(str, str ? ::strlen(str) : 0); } }; -class StaticStringAdapter : public ZeroTerminatedRamString { - public: - StaticStringAdapter(const char* str) : ZeroTerminatedRamString(str) {} - bool isLinked() const { - return true; - } -}; -template <> -struct StringAdapter { - typedef StaticStringAdapter AdaptedString; - static AdaptedString adapt(const char* p) { - return AdaptedString(p); - } -}; -class SizedRamString { - public: - static const size_t typeSortKey = 2; - SizedRamString(const char* str, size_t sz) : str_(str), size_(sz) {} - bool isNull() const { - return !str_; - } - size_t size() const { - return size_; - } - char operator[](size_t i) const { - ARDUINOJSON_ASSERT(str_ != 0); - ARDUINOJSON_ASSERT(i <= size()); - return str_[i]; - } - const char* data() const { - return str_; - } - bool isLinked() const { - return false; - } - protected: - const char* str_; - size_t size_; -}; template -struct SizedStringAdapter::value>::type> { - typedef SizedRamString AdaptedString; +struct SizedStringAdapter::value>> { + using AdaptedString = RamString; static AdaptedString adapt(const TChar* p, size_t n) { return AdaptedString(reinterpret_cast(p), n); } }; -ARDUINOJSON_END_PRIVATE_NAMESPACE -#if ARDUINOJSON_ENABLE_STD_STREAM -#include -#endif -ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE -class JsonString { - public: - enum Ownership { Copied, Linked }; - JsonString() : data_(0), size_(0), ownership_(Linked) {} - JsonString(const char* data, Ownership ownership = Linked) - : data_(data), size_(data ? ::strlen(data) : 0), ownership_(ownership) {} - JsonString(const char* data, size_t sz, Ownership ownership = Linked) - : data_(data), size_(sz), ownership_(ownership) {} - const char* c_str() const { - return data_; - } - bool isNull() const { - return !data_; - } - bool isLinked() const { - return ownership_ == Linked; - } - size_t size() const { - return size_; - } - explicit operator bool() const { - return data_ != 0; - } - friend bool operator==(JsonString lhs, JsonString rhs) { - if (lhs.size_ != rhs.size_) - return false; - if (lhs.data_ == rhs.data_) - return true; - if (!lhs.data_) - return false; - if (!rhs.data_) - return false; - return memcmp(lhs.data_, rhs.data_, lhs.size_) == 0; - } - friend bool operator!=(JsonString lhs, JsonString rhs) { - return !(lhs == rhs); - } -#if ARDUINOJSON_ENABLE_STD_STREAM - friend std::ostream& operator<<(std::ostream& lhs, const JsonString& rhs) { - lhs.write(rhs.c_str(), static_cast(rhs.size())); - return lhs; - } -#endif - private: - const char* data_; - size_t size_; - Ownership ownership_; -}; -ARDUINOJSON_END_PUBLIC_NAMESPACE -ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -class JsonStringAdapter : public SizedRamString { - public: - JsonStringAdapter(const JsonString& s) - : SizedRamString(s.c_str(), s.size()), linked_(s.isLinked()) {} - bool isLinked() const { - return linked_; - } - private: - bool linked_; -}; -template <> -struct StringAdapter { - typedef JsonStringAdapter AdaptedString; - static AdaptedString adapt(const JsonString& s) { - return AdaptedString(s); - } -}; namespace string_traits_impl { template struct has_cstr : false_type {}; template -struct has_cstr().c_str()), - const char*>::value>::type> - : true_type {}; +struct has_cstr().c_str()), + const char*>::value>> : true_type {}; template struct has_data : false_type {}; template -struct has_data().data()), - const char*>::value>::type> - : true_type {}; +struct has_data().data()), + const char*>::value>> : true_type {}; template struct has_length : false_type {}; template struct has_length< - T, typename enable_if< - is_same().length()), size_t>::value>::type> + T, enable_if_t().length())>::value>> : true_type {}; template struct has_size : false_type {}; template struct has_size< - T, typename enable_if< - is_same().size()), size_t>::value>::type> + T, enable_if_t().size()), size_t>::value>> : true_type {}; } // namespace string_traits_impl template @@ -868,32 +1076,29 @@ struct string_traits { template struct StringAdapter< T, - typename enable_if< - (string_traits::has_cstr || string_traits::has_data) && - (string_traits::has_length || string_traits::has_size)>::type> { - typedef SizedRamString AdaptedString; + enable_if_t<(string_traits::has_cstr || string_traits::has_data) && + (string_traits::has_length || string_traits::has_size)>> { + using AdaptedString = RamString; static AdaptedString adapt(const T& s) { return AdaptedString(get_data(s), get_size(s)); } private: template - static typename enable_if::has_size, size_t>::type get_size( - const U& s) { + static enable_if_t::has_size, size_t> get_size(const U& s) { return s.size(); } template - static typename enable_if::has_size, size_t>::type get_size( - const U& s) { + static enable_if_t::has_size, size_t> get_size(const U& s) { return s.length(); } template - static typename enable_if::has_data, const char*>::type - get_data(const U& s) { + static enable_if_t::has_data, const char*> get_data( + const U& s) { return s.data(); } template - static typename enable_if::has_data, const char*>::type - get_data(const U& s) { + static enable_if_t::has_data, const char*> get_data( + const U& s) { return s.c_str(); } }; @@ -1038,7 +1243,7 @@ class FlashString { size_t size() const { return size_; } - friend bool stringEquals(FlashString a, SizedRamString b) { + friend bool stringEquals(FlashString a, RamString b) { ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey); ARDUINOJSON_ASSERT(!a.isNull()); ARDUINOJSON_ASSERT(!b.isNull()); @@ -1046,7 +1251,7 @@ class FlashString { return false; return ::memcmp_P(b.data(), a.str_, a.size_) == 0; } - friend int stringCompare(FlashString a, SizedRamString b) { + friend int stringCompare(FlashString a, RamString b) { ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey); ARDUINOJSON_ASSERT(!a.isNull()); ARDUINOJSON_ASSERT(!b.isNull()); @@ -1064,7 +1269,7 @@ class FlashString { ARDUINOJSON_ASSERT(s.size() <= n); ::memcpy_P(p, s.str_, n); } - bool isLinked() const { + bool isStatic() const { return false; } private: @@ -1073,14 +1278,14 @@ class FlashString { }; template <> struct StringAdapter { - typedef FlashString AdaptedString; + using AdaptedString = FlashString; static AdaptedString adapt(const __FlashStringHelper* s) { return AdaptedString(s, s ? strlen_P(reinterpret_cast(s)) : 0); } }; template <> struct SizedStringAdapter { - typedef FlashString AdaptedString; + using AdaptedString = FlashString; static AdaptedString adapt(const __FlashStringHelper* s, size_t n) { return AdaptedString(s, n); } @@ -1089,8 +1294,7 @@ ARDUINOJSON_END_PRIVATE_NAMESPACE #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template -typename enable_if::type +enable_if_t stringCompare(TAdaptedString1 s1, TAdaptedString2 s2) { ARDUINOJSON_ASSERT(!s1.isNull()); ARDUINOJSON_ASSERT(!s2.isNull()); @@ -1108,14 +1312,12 @@ stringCompare(TAdaptedString1 s1, TAdaptedString2 s2) { return 0; } template -typename enable_if< - (TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), int>::type +enable_if_t<(TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), int> stringCompare(TAdaptedString1 s1, TAdaptedString2 s2) { return -stringCompare(s2, s1); } template -typename enable_if::type +enable_if_t stringEquals(TAdaptedString1 s1, TAdaptedString2 s2) { ARDUINOJSON_ASSERT(!s1.isNull()); ARDUINOJSON_ASSERT(!s2.isNull()); @@ -1130,8 +1332,7 @@ stringEquals(TAdaptedString1 s1, TAdaptedString2 s2) { return true; } template -typename enable_if< - (TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), bool>::type +enable_if_t<(TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), bool> stringEquals(TAdaptedString1 s1, TAdaptedString2 s2) { return stringEquals(s2, s1); } @@ -1142,8 +1343,6 @@ static void stringGetChars(TAdaptedString s, char* p, size_t n) { p[i] = s[i]; } } -class VariantSlot; -class VariantPool; class StringPool { public: StringPool() = default; @@ -1217,285 +1416,6 @@ class StringPool { private: StringNode* strings_ = nullptr; }; -class VariantSlot; -using SlotId = uint_t::type; -using SlotCount = SlotId; -const SlotId NULL_SLOT = SlotId(-1); -class SlotWithId { - public: - SlotWithId() : slot_(nullptr), id_(NULL_SLOT) {} - SlotWithId(VariantSlot* slot, SlotId id) : slot_(slot), id_(id) { - ARDUINOJSON_ASSERT((slot == nullptr) == (id == NULL_SLOT)); - } - SlotId id() const { - return id_; - } - operator VariantSlot*() { - return slot_; - } - VariantSlot* operator->() { - ARDUINOJSON_ASSERT(slot_ != nullptr); - return slot_; - } - private: - VariantSlot* slot_; - SlotId id_; -}; -class VariantPool { - public: - void create(SlotCount cap, Allocator* allocator); - void destroy(Allocator* allocator); - SlotWithId allocSlot(); - VariantSlot* getSlot(SlotId id) const; - void clear(); - void shrinkToFit(Allocator*); - SlotCount usage() const; - static SlotCount bytesToSlots(size_t); - static size_t slotsToBytes(SlotCount); - private: - SlotCount capacity_; - SlotCount usage_; - VariantSlot* slots_; -}; -using PoolCount = SlotId; -class VariantPoolList { - public: - VariantPoolList() = default; - ~VariantPoolList() { - ARDUINOJSON_ASSERT(count_ == 0); - } - friend void swap(VariantPoolList& a, VariantPoolList& b) { - bool aUsedPreallocated = a.pools_ == a.preallocatedPools_; - bool bUsedPreallocated = b.pools_ == b.preallocatedPools_; - if (aUsedPreallocated && bUsedPreallocated) { - for (PoolCount i = 0; i < ARDUINOJSON_INITIAL_POOL_COUNT; i++) - swap_(a.preallocatedPools_[i], b.preallocatedPools_[i]); - } else if (bUsedPreallocated) { - for (PoolCount i = 0; i < b.count_; i++) - a.preallocatedPools_[i] = b.preallocatedPools_[i]; - b.pools_ = a.pools_; - a.pools_ = a.preallocatedPools_; - } else if (aUsedPreallocated) { - for (PoolCount i = 0; i < a.count_; i++) - b.preallocatedPools_[i] = a.preallocatedPools_[i]; - a.pools_ = b.pools_; - b.pools_ = b.preallocatedPools_; - } else { - swap_(a.pools_, b.pools_); - } - swap_(a.count_, b.count_); - swap_(a.capacity_, b.capacity_); - swap_(a.freeList_, b.freeList_); - } - VariantPoolList& operator=(VariantPoolList&& src) { - ARDUINOJSON_ASSERT(count_ == 0); - if (src.pools_ == src.preallocatedPools_) { - memcpy(preallocatedPools_, src.preallocatedPools_, - sizeof(preallocatedPools_)); - pools_ = preallocatedPools_; - } else { - pools_ = src.pools_; - src.pools_ = nullptr; - } - count_ = src.count_; - capacity_ = src.capacity_; - src.count_ = 0; - src.capacity_ = 0; - return *this; - } - SlotWithId allocSlot(Allocator* allocator) { - if (freeList_ != NULL_SLOT) { - return allocFromFreeList(); - } - if (count_) { - auto slot = allocFromLastPool(); - if (slot) - return slot; - } - auto pool = addPool(allocator); - if (!pool) - return {}; - return allocFromLastPool(); - } - void freeSlot(SlotWithId slot); - VariantSlot* getSlot(SlotId id) const { - if (id == NULL_SLOT) - return nullptr; - auto poolIndex = SlotId(id / ARDUINOJSON_POOL_CAPACITY); - auto indexInPool = SlotId(id % ARDUINOJSON_POOL_CAPACITY); - ARDUINOJSON_ASSERT(poolIndex < count_); - return pools_[poolIndex].getSlot(indexInPool); - } - void clear(Allocator* allocator) { - for (PoolCount i = 0; i < count_; i++) - pools_[i].destroy(allocator); - count_ = 0; - freeList_ = NULL_SLOT; - if (pools_ != preallocatedPools_) { - allocator->deallocate(pools_); - pools_ = preallocatedPools_; - capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT; - } - } - SlotCount usage() const { - SlotCount total = 0; - for (PoolCount i = 0; i < count_; i++) - total = SlotCount(total + pools_[i].usage()); - return total; - } - void shrinkToFit(Allocator* allocator) { - if (count_ > 0) - pools_[count_ - 1].shrinkToFit(allocator); - if (pools_ != preallocatedPools_ && count_ != capacity_) { - pools_ = static_cast( - allocator->reallocate(pools_, count_ * sizeof(VariantPool))); - ARDUINOJSON_ASSERT(pools_ != nullptr); // realloc to smaller can't fail - capacity_ = count_; - } - } - private: - SlotWithId allocFromFreeList(); - SlotWithId allocFromLastPool() { - ARDUINOJSON_ASSERT(count_ > 0); - auto poolIndex = SlotId(count_ - 1); - auto slot = pools_[poolIndex].allocSlot(); - if (!slot) - return {}; - return {slot, SlotId(poolIndex * ARDUINOJSON_POOL_CAPACITY + slot.id())}; - } - VariantPool* addPool(Allocator* allocator) { - if (count_ == capacity_ && !increaseCapacity(allocator)) - return nullptr; - auto pool = &pools_[count_++]; - SlotCount poolCapacity = ARDUINOJSON_POOL_CAPACITY; - if (count_ == maxPools) // last pool is smaller because of NULL_SLOT - poolCapacity--; - pool->create(poolCapacity, allocator); - return pool; - } - bool increaseCapacity(Allocator* allocator) { - if (capacity_ == maxPools) - return false; - void* newPools; - auto newCapacity = PoolCount(capacity_ * 2); - if (pools_ == preallocatedPools_) { - newPools = allocator->allocate(newCapacity * sizeof(VariantPool)); - if (!newPools) - return false; - memcpy(newPools, preallocatedPools_, sizeof(preallocatedPools_)); - } else { - newPools = - allocator->reallocate(pools_, newCapacity * sizeof(VariantPool)); - if (!newPools) - return false; - } - pools_ = static_cast(newPools); - capacity_ = newCapacity; - return true; - } - VariantPool preallocatedPools_[ARDUINOJSON_INITIAL_POOL_COUNT]; - VariantPool* pools_ = preallocatedPools_; - PoolCount count_ = 0; - PoolCount capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT; - SlotId freeList_ = NULL_SLOT; - public: - static const PoolCount maxPools = - PoolCount(NULL_SLOT / ARDUINOJSON_POOL_CAPACITY + 1); -}; -class VariantSlot; -class VariantPool; -class ResourceManager { - public: - ResourceManager(Allocator* allocator = DefaultAllocator::instance()) - : allocator_(allocator), overflowed_(false) {} - ~ResourceManager() { - stringPool_.clear(allocator_); - variantPools_.clear(allocator_); - } - ResourceManager(const ResourceManager&) = delete; - ResourceManager& operator=(const ResourceManager& src) = delete; - friend void swap(ResourceManager& a, ResourceManager& b) { - swap(a.stringPool_, b.stringPool_); - swap(a.variantPools_, b.variantPools_); - swap_(a.allocator_, b.allocator_); - swap_(a.overflowed_, b.overflowed_); - } - Allocator* allocator() const { - return allocator_; - } - size_t size() const { - return VariantPool::slotsToBytes(variantPools_.usage()) + - stringPool_.size(); - } - bool overflowed() const { - return overflowed_; - } - SlotWithId allocSlot() { - auto p = variantPools_.allocSlot(allocator_); - if (!p) - overflowed_ = true; - return p; - } - void freeSlot(SlotWithId id) { - variantPools_.freeSlot(id); - } - VariantSlot* getSlot(SlotId id) const { - return variantPools_.getSlot(id); - } - template - StringNode* saveString(TAdaptedString str) { - if (str.isNull()) - return 0; - auto node = stringPool_.add(str, allocator_); - if (!node) - overflowed_ = true; - return node; - } - void saveString(StringNode* node) { - stringPool_.add(node); - } - template - StringNode* getString(const TAdaptedString& str) const { - return stringPool_.get(str); - } - StringNode* createString(size_t length) { - auto node = StringNode::create(length, allocator_); - if (!node) - overflowed_ = true; - return node; - } - StringNode* resizeString(StringNode* node, size_t length) { - node = StringNode::resize(node, length, allocator_); - if (!node) - overflowed_ = true; - return node; - } - void destroyString(StringNode* node) { - StringNode::destroy(node, allocator_); - } - void dereferenceString(const char* s) { - stringPool_.dereference(s, allocator_); - } - void clear() { - variantPools_.clear(allocator_); - overflowed_ = false; - stringPool_.clear(allocator_); - } - void shrinkToFit() { - variantPools_.shrinkToFit(allocator_); - } - private: - Allocator* allocator_; - bool overflowed_; - StringPool stringPool_; - VariantPoolList variantPools_; -}; -template -struct IsString : false_type {}; -template -struct IsString< - T, typename make_void::AdaptedString>::type> - : true_type {}; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE template @@ -1624,11 +1544,11 @@ template struct FloatTraits {}; template struct FloatTraits { - typedef uint64_t mantissa_type; + using mantissa_type = uint64_t; static const short mantissa_bits = 52; static const mantissa_type mantissa_max = (mantissa_type(1) << mantissa_bits) - 1; - typedef int16_t exponent_type; + using exponent_type = int16_t; static const exponent_type exponent_max = 308; static pgm_ptr positiveBinaryPowersOfTen() { ARDUINOJSON_DEFINE_PROGMEM_ARRAY( // @@ -1673,16 +1593,16 @@ struct FloatTraits { } template // int64_t static T highest_for( - typename enable_if::value && is_signed::value && - sizeof(TOut) == 8, - signed>::type* = 0) { + 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( - typename enable_if::value && is_unsigned::value && - sizeof(TOut) == 8, - unsigned>::type* = 0) { + enable_if_t::value && is_unsigned::value && + sizeof(TOut) == 8, + unsigned>* = 0) { return forge(0x43EFFFFFFFFFFFFF); // 1.8446744073709549568e+19 } static T lowest() { @@ -1694,11 +1614,11 @@ struct FloatTraits { }; template struct FloatTraits { - typedef uint32_t mantissa_type; + using mantissa_type = uint32_t; static const short mantissa_bits = 23; static const mantissa_type mantissa_max = (mantissa_type(1) << mantissa_bits) - 1; - typedef int8_t exponent_type; + using exponent_type = int8_t; static const exponent_type exponent_max = 38; static pgm_ptr positiveBinaryPowersOfTen() { ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors, @@ -1738,30 +1658,30 @@ struct FloatTraits { } template // int32_t static T highest_for( - typename enable_if::value && is_signed::value && - sizeof(TOut) == 4, - signed>::type* = 0) { + enable_if_t::value && is_signed::value && + sizeof(TOut) == 4, + signed>* = 0) { return forge(0x4EFFFFFF); // 2.14748352E9 } template // uint32_t static T highest_for( - typename enable_if::value && is_unsigned::value && - sizeof(TOut) == 4, - unsigned>::type* = 0) { + enable_if_t::value && is_unsigned::value && + sizeof(TOut) == 4, + unsigned>* = 0) { return forge(0x4F7FFFFF); // 4.29496704E9 } template // int64_t static T highest_for( - typename enable_if::value && is_signed::value && - sizeof(TOut) == 8, - signed>::type* = 0) { + enable_if_t::value && is_signed::value && + sizeof(TOut) == 8, + signed>* = 0) { return forge(0x5EFFFFFF); // 9.22337148709896192E18 } template // uint64_t static T highest_for( - typename enable_if::value && is_unsigned::value && - sizeof(TOut) == 8, - unsigned>::type* = 0) { + enable_if_t::value && is_unsigned::value && + sizeof(TOut) == 8, + unsigned>* = 0) { return forge(0x5F7FFFFF); // 1.844674297419792384E19 } static T lowest() { @@ -1785,86 +1705,91 @@ inline TFloat make_float(TFloat m, TExponent e) { ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE #if ARDUINOJSON_USE_DOUBLE -typedef double JsonFloat; +using JsonFloat = double; #else -typedef float JsonFloat; +using JsonFloat = float; #endif ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template -typename enable_if::value && is_unsigned::value && - is_integral::value && sizeof(TOut) <= sizeof(TIn), - bool>::type +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 -typename enable_if::value && is_unsigned::value && - is_integral::value && sizeof(TIn) < sizeof(TOut), - bool>::type +enable_if_t::value && is_unsigned::value && + is_integral::value && sizeof(TIn) < sizeof(TOut), + bool> canConvertNumber(TIn) { return true; } template -typename enable_if::value && is_floating_point::value, - bool>::type +enable_if_t::value && is_floating_point::value, bool> canConvertNumber(TIn) { return true; } template -typename enable_if::value && is_signed::value && - is_integral::value && is_signed::value && - sizeof(TOut) < sizeof(TIn), - bool>::type +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 -typename enable_if::value && is_signed::value && - is_integral::value && is_signed::value && - sizeof(TIn) <= sizeof(TOut), - bool>::type +enable_if_t::value && is_signed::value && + is_integral::value && is_signed::value && + sizeof(TIn) <= sizeof(TOut), + bool> canConvertNumber(TIn) { return true; } template -typename enable_if::value && is_signed::value && - is_integral::value && is_unsigned::value && - sizeof(TOut) >= sizeof(TIn), - bool>::type +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 -typename enable_if::value && is_signed::value && - is_integral::value && is_unsigned::value && - sizeof(TOut) < sizeof(TIn), - bool>::type +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 -typename enable_if::value && is_integral::value && - sizeof(TOut) < sizeof(TIn), - bool>::type +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 -typename enable_if::value && is_integral::value && - sizeof(TOut) >= sizeof(TIn), - bool>::type +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; } @@ -1874,9 +1799,73 @@ ARDUINOJSON_END_PRIVATE_NAMESPACE #elif defined(__GNUC__) # pragma GCC diagnostic pop #endif +#if ARDUINOJSON_ENABLE_STD_STREAM +#include +#endif +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class JsonString { + friend struct detail::StringAdapter; + public: + JsonString() : str_(nullptr, 0, true) {} + JsonString(const char* data, bool isStatic = false) + : str_(data, data ? ::strlen(data) : 0, isStatic) {} + template ::value && + !detail::is_same::value, + int> = 0> + JsonString(const char* data, TSize sz, bool isStatic = false) + : str_(data, size_t(sz), isStatic) {} + const char* c_str() const { + return str_.data(); + } + bool isNull() const { + return str_.isNull(); + } + bool isStatic() const { + return str_.isStatic(); + } + size_t size() const { + return str_.size(); + } + explicit operator bool() const { + return str_.data() != 0; + } + friend bool operator==(JsonString lhs, JsonString rhs) { + if (lhs.size() != rhs.size()) + return false; + if (lhs.c_str() == rhs.c_str()) + return true; + if (!lhs.c_str()) + return false; + if (!rhs.c_str()) + return false; + return memcmp(lhs.c_str(), rhs.c_str(), 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: + detail::RamString str_; +}; +namespace detail { +template <> +struct StringAdapter { + using AdaptedString = RamString; + static const AdaptedString& adapt(const JsonString& s) { + return s.str_; + } +}; +} // namespace detail +ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE class VariantData; -class VariantSlot; +class ResourceManager; class CollectionIterator { friend class CollectionData; public: @@ -1903,10 +1892,6 @@ class CollectionIterator { ARDUINOJSON_ASSERT(slot_ != nullptr); return *data(); } - const char* key() const; - bool ownsKey() const; - void setKey(StringNode*); - void setKey(const char*); VariantData* data() { return reinterpret_cast(slot_); } @@ -1914,8 +1899,8 @@ class CollectionIterator { return reinterpret_cast(slot_); } private: - CollectionIterator(VariantSlot* slot, SlotId slotId); - VariantSlot* slot_; + CollectionIterator(VariantData* slot, SlotId slotId); + VariantData* slot_; SlotId currentId_, nextId_; }; class CollectionData { @@ -1927,9 +1912,7 @@ class CollectionData { } static void operator delete(void*, void*) noexcept {} using iterator = CollectionIterator; - iterator createIterator(const ResourceManager* resources) const { - return iterator(resources->getSlot(head_), head_); - } + iterator createIterator(const ResourceManager* resources) const; size_t size(const ResourceManager*) const; size_t nesting(const ResourceManager*) const; void clear(ResourceManager* resources); @@ -1938,20 +1921,17 @@ class CollectionData { return; collection->clear(resources); } - void remove(iterator it, ResourceManager* resources); - static void remove(CollectionData* collection, iterator it, - ResourceManager* resources) { - if (collection) - return collection->remove(it, resources); - } SlotId head() const { return head_; } protected: - iterator addSlot(ResourceManager*); + 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: - SlotWithId getPreviousSlot(VariantSlot*, const ResourceManager*) const; - void releaseSlot(SlotWithId, ResourceManager*); + Slot getPreviousSlot(VariantData*, const ResourceManager*) const; }; inline const VariantData* collectionToVariant( const CollectionData* collection) { @@ -1964,14 +1944,21 @@ inline VariantData* collectionToVariant(CollectionData* collection) { } class ArrayData : public CollectionData { public: - VariantData* addElement(ResourceManager* resources) { - return addSlot(resources).data(); - } + VariantData* addElement(ResourceManager* resources); static VariantData* addElement(ArrayData* array, ResourceManager* resources) { if (!array) return nullptr; return array->addElement(resources); } + template + bool addValue(const T& value, ResourceManager* resources); + template + static bool addValue(ArrayData* array, const 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, @@ -1987,12 +1974,13 @@ class ArrayData : public CollectionData { return; array->removeElement(index, resources); } - bool copyFrom(const ArrayData& src, ResourceManager* resources); - static bool copy(ArrayData* dst, const ArrayData* src, - ResourceManager* resources) { - if (!dst || !src) - return false; - return dst->copyFrom(*src, resources); + 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; @@ -2000,11 +1988,11 @@ class ArrayData : public CollectionData { ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE #if ARDUINOJSON_USE_LONG_LONG -typedef int64_t JsonInteger; -typedef uint64_t JsonUInt; +using JsonInteger = int64_t; +using JsonUInt = uint64_t; #else -typedef long JsonInteger; -typedef unsigned long JsonUInt; +using JsonInteger = long; +using JsonUInt = unsigned long; #endif ARDUINOJSON_END_PUBLIC_NAMESPACE #define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T) \ @@ -2015,32 +2003,8 @@ ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE class ObjectData : public CollectionData { public: - VariantData* addMember(StringNode* key, ResourceManager* resources) { - ARDUINOJSON_ASSERT(key != nullptr); - auto it = addSlot(resources); - if (it.done()) - return nullptr; - it.setKey(key); - return it.data(); - } - template - VariantData* addMember(TAdaptedString key, ResourceManager* resources) { - ARDUINOJSON_ASSERT(!key.isNull()); - if (key.isLinked()) { - auto it = addSlot(resources); - if (!it.done()) - it.setKey(key.data()); - return it.data(); - } else { - auto storedKey = resources->saveString(key); - if (!storedKey) - return nullptr; - auto it = addSlot(resources); - if (!it.done()) - it.setKey(storedKey); - return it.data(); - } - } + template // also works with StringNode* + VariantData* addMember(TAdaptedString key, ResourceManager* resources); template VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources); template @@ -2062,119 +2026,139 @@ class ObjectData : public CollectionData { 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 { - VALUE_MASK = 0x7F, - OWNED_VALUE_BIT = 0x01, - VALUE_IS_NULL = 0, - VALUE_IS_RAW_STRING = 0x03, - VALUE_IS_LINKED_STRING = 0x04, - VALUE_IS_OWNED_STRING = 0x05, - VALUE_IS_BOOLEAN = 0x06, - NUMBER_BIT = 0x08, - VALUE_IS_UNSIGNED_INTEGER = 0x08, - VALUE_IS_SIGNED_INTEGER = 0x0A, - VALUE_IS_FLOAT = 0x0C, - COLLECTION_MASK = 0x60, - VALUE_IS_OBJECT = 0x20, - VALUE_IS_ARRAY = 0x40, - OWNED_KEY_BIT = 0x80 +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() {} - JsonFloat asFloat; + float asFloat; bool asBoolean; - JsonUInt asUnsignedInteger; - JsonInteger asSignedInteger; + 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; }; -struct StringNode; -class VariantSlot { - VariantContent content_; - uint8_t flags_; +#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_; - const char* key_; public: static void* operator new(size_t, void* p) noexcept { return p; } static void operator delete(void*, void*) noexcept {} - VariantSlot() : flags_(0), next_(NULL_SLOT), key_(0) {} - VariantData* data() { - return reinterpret_cast(&content_); - } - const VariantData* data() const { - return reinterpret_cast(&content_); - } + VariantData() : type_(VariantType::Null), next_(NULL_SLOT) {} SlotId next() const { return next_; } void setNext(SlotId slot) { next_ = slot; } - void setKey(const char* k) { - ARDUINOJSON_ASSERT(k); - flags_ &= VALUE_MASK; - key_ = k; - } - void setKey(StringNode* k) { - ARDUINOJSON_ASSERT(k); - flags_ |= OWNED_KEY_BIT; - key_ = k->data; - } - const char* key() const { - return key_; - } - bool ownsKey() const { - return (flags_ & OWNED_KEY_BIT) != 0; - } -}; -inline VariantData* slotData(VariantSlot* slot) { - return reinterpret_cast(slot); -} -constexpr size_t sizeofArray(size_t n) { - return n * sizeof(VariantSlot); -} -constexpr size_t sizeofObject(size_t n) { - return n * sizeof(VariantSlot); -} -template -T parseNumber(const char* s); -class VariantData { - VariantContent content_; // must be first to allow cast from array to variant - uint8_t flags_; - public: - VariantData() : flags_(VALUE_IS_NULL) {} template - typename TVisitor::result_type accept(TVisitor& visit) const { - switch (type()) { - case VALUE_IS_FLOAT: + 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); - case VALUE_IS_ARRAY: +#if ARDUINOJSON_USE_DOUBLE + case VariantType::Double: + return visit.visit(extension->asDouble); +#endif + case VariantType::Array: return visit.visit(content_.asArray); - case VALUE_IS_OBJECT: + case VariantType::Object: return visit.visit(content_.asObject); - case VALUE_IS_LINKED_STRING: - return visit.visit(JsonString(content_.asLinkedString)); - case VALUE_IS_OWNED_STRING: + case VariantType::LinkedString: + return visit.visit(JsonString(content_.asLinkedString, true)); + case VariantType::OwnedString: return visit.visit(JsonString(content_.asOwnedString->data, - content_.asOwnedString->length, - JsonString::Copied)); - case VALUE_IS_RAW_STRING: + content_.asOwnedString->length)); + case VariantType::RawString: return visit.visit(RawString(content_.asOwnedString->data, content_.asOwnedString->length)); - case VALUE_IS_SIGNED_INTEGER: - return visit.visit(content_.asSignedInteger); - case VALUE_IS_UNSIGNED_INTEGER: - return visit.visit(content_.asUnsignedInteger); - case VALUE_IS_BOOLEAN: + 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); @@ -2182,9 +2166,10 @@ class VariantData { } template static typename TVisitor::result_type accept(const VariantData* var, + const ResourceManager* resources, TVisitor& visit) { if (var != 0) - return var->accept(visit); + return var->accept(visit, resources); else return visit.visit(nullptr); } @@ -2197,17 +2182,43 @@ class VariantData { return nullptr; return var->addElement(resources); } - bool asBoolean() const { - switch (type()) { - case VALUE_IS_BOOLEAN: + template + bool addValue(const T& value, ResourceManager* resources) { + auto array = isNull() ? &toArray() : asArray(); + return detail::ArrayData::addValue(array, value, resources); + } + template + static bool addValue(VariantData* var, const 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 VALUE_IS_SIGNED_INTEGER: - case VALUE_IS_UNSIGNED_INTEGER: - return content_.asUnsignedInteger != 0; - case VALUE_IS_FLOAT: + case VariantType::Uint32: + case VariantType::Int32: + return content_.asUint32 != 0; + case VariantType::Float: return content_.asFloat != 0; - case VALUE_IS_NULL: +#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; } @@ -2225,40 +2236,70 @@ class VariantData { return const_cast(this)->asCollection(); } template - T asFloat() const { + T asFloat(const ResourceManager* resources) const { static_assert(is_floating_point::value, "T must be a floating point"); - switch (type()) { - case VALUE_IS_BOOLEAN: +#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 VALUE_IS_UNSIGNED_INTEGER: - return static_cast(content_.asUnsignedInteger); - case VALUE_IS_SIGNED_INTEGER: - return static_cast(content_.asSignedInteger); - case VALUE_IS_LINKED_STRING: - case VALUE_IS_OWNED_STRING: + 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 VALUE_IS_FLOAT: + 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 { + T asIntegral(const ResourceManager* resources) const { static_assert(is_integral::value, "T must be an integral type"); - switch (type()) { - case VALUE_IS_BOOLEAN: +#if ARDUINOJSON_USE_EXTENSIONS + auto extension = getExtension(resources); +#else + (void)resources; // silence warning +#endif + switch (type_) { + case VariantType::Boolean: return content_.asBoolean; - case VALUE_IS_UNSIGNED_INTEGER: - return convertNumber(content_.asUnsignedInteger); - case VALUE_IS_SIGNED_INTEGER: - return convertNumber(content_.asSignedInteger); - case VALUE_IS_LINKED_STRING: + 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 VALUE_IS_OWNED_STRING: + case VariantType::OwnedString: return parseNumber(content_.asOwnedString->data); - case VALUE_IS_FLOAT: + case VariantType::Float: return convertNumber(content_.asFloat); +#if ARDUINOJSON_USE_DOUBLE + case VariantType::Double: + return convertNumber(extension->asDouble); +#endif default: return 0; } @@ -2270,25 +2311,28 @@ class VariantData { return const_cast(this)->asObject(); } JsonString asRawString() const { - switch (type()) { - case VALUE_IS_RAW_STRING: + switch (type_) { + case VariantType::RawString: return JsonString(content_.asOwnedString->data, - content_.asOwnedString->length, JsonString::Copied); + content_.asOwnedString->length); default: return JsonString(); } } JsonString asString() const { - switch (type()) { - case VALUE_IS_LINKED_STRING: - return JsonString(content_.asLinkedString, JsonString::Linked); - case VALUE_IS_OWNED_STRING: + switch (type_) { + case VariantType::LinkedString: + return JsonString(content_.asLinkedString, true); + case VariantType::OwnedString: return JsonString(content_.asOwnedString->data, - content_.asOwnedString->length, JsonString::Copied); + content_.asOwnedString->length); 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); @@ -2325,30 +2369,41 @@ class VariantData { return obj->getOrAddMember(key, resources); } bool isArray() const { - return (flags_ & VALUE_IS_ARRAY) != 0; + return type_ == VariantType::Array; } bool isBoolean() const { - return type() == VALUE_IS_BOOLEAN; + return type_ == VariantType::Boolean; } bool isCollection() const { - return (flags_ & COLLECTION_MASK) != 0; + return type_ & VariantTypeBits::CollectionMask; } bool isFloat() const { - return (flags_ & NUMBER_BIT) != 0; + return type_ & VariantTypeBits::NumberBit; } template - bool isInteger() const { - switch (type()) { - case VALUE_IS_UNSIGNED_INTEGER: - return canConvertNumber(content_.asUnsignedInteger); - case VALUE_IS_SIGNED_INTEGER: - return canConvertNumber(content_.asSignedInteger); + 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() == VALUE_IS_NULL; + return type_ == VariantType::Null; } static bool isNull(const VariantData* var) { if (!var) @@ -2356,10 +2411,11 @@ class VariantData { return var->isNull(); } bool isObject() const { - return (flags_ & VALUE_IS_OBJECT) != 0; + return type_ == VariantType::Object; } bool isString() const { - return type() == VALUE_IS_LINKED_STRING || type() == VALUE_IS_OWNED_STRING; + return type_ == VariantType::LinkedString || + type_ == VariantType::OwnedString; } size_t nesting(const ResourceManager* resources) const { auto collection = asCollection(); @@ -2394,153 +2450,210 @@ class VariantData { return; var->removeMember(key, resources); } - void reset() { - flags_ = VALUE_IS_NULL; + void reset() { // TODO: remove + type_ = VariantType::Null; } void setBoolean(bool value) { - setType(VALUE_IS_BOOLEAN); + ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first + type_ = VariantType::Boolean; content_.asBoolean = value; } - void setBoolean(bool value, ResourceManager* resources) { - release(resources); - setBoolean(value); - } - void setFloat(JsonFloat value) { - setType(VALUE_IS_FLOAT); + template + enable_if_t setFloat(T value, ResourceManager*) { + ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first + type_ = VariantType::Float; content_.asFloat = value; - } - void setFloat(JsonFloat value, ResourceManager* resources) { - release(resources); - setFloat(value); + return true; } template - typename enable_if::value>::type setInteger(T value) { - setType(VALUE_IS_SIGNED_INTEGER); - content_.asSignedInteger = value; - } + enable_if_t setFloat(T value, ResourceManager*); template - typename enable_if::value>::type setInteger(T value) { - setType(VALUE_IS_UNSIGNED_INTEGER); - content_.asUnsignedInteger = static_cast(value); - } + enable_if_t::value, bool> setInteger(T value, + ResourceManager* resources); template - void setInteger(T value, ResourceManager* resources) { - release(resources); - setInteger(value); - } - void setNull() { - setType(VALUE_IS_NULL); - } - void setNull(ResourceManager* resources) { - release(resources); - setNull(); - } - static void setNull(VariantData* var, ResourceManager* resources) { - if (!var) - return; - var->setNull(resources); - } + 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); - setType(VALUE_IS_RAW_STRING); + type_ = VariantType::RawString; content_.asOwnedString = s; } template - void setRawString(SerializedValue value, ResourceManager* resources) { - release(resources); - auto dup = resources->saveString(adaptString(value.data(), value.size())); - if (dup) - setRawString(dup); - else - setNull(); - } + 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 - void setString(TAdaptedString value, ResourceManager* resources) { - setNull(resources); - if (value.isNull()) - return; - if (value.isLinked()) { - setLinkedString(value.data()); - return; - } - auto dup = resources->saveString(value); - if (dup) - setOwnedString(dup); + 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); - setType(VALUE_IS_LINKED_STRING); + type_ = VariantType::LinkedString; content_.asLinkedString = s; } void setOwnedString(StringNode* s) { + ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first ARDUINOJSON_ASSERT(s); - setType(VALUE_IS_OWNED_STRING); + type_ = VariantType::OwnedString; content_.asOwnedString = s; } size_t size(const ResourceManager* resources) const { - return isCollection() ? content_.asCollection.size(resources) : 0; + 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() { - setType(VALUE_IS_ARRAY); + ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first + type_ = VariantType::Array; new (&content_.asArray) ArrayData(); return content_.asArray; } - ArrayData& toArray(ResourceManager* resources) { - release(resources); - return toArray(); - } static ArrayData* toArray(VariantData* var, ResourceManager* resources) { if (!var) return 0; - return &var->toArray(resources); + var->clear(resources); + return &var->toArray(); } ObjectData& toObject() { - setType(VALUE_IS_OBJECT); + ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first + type_ = VariantType::Object; new (&content_.asObject) ObjectData(); return content_.asObject; } - ObjectData& toObject(ResourceManager* resources) { - release(resources); - return toObject(); - } static ObjectData* toObject(VariantData* var, ResourceManager* resources) { if (!var) return 0; - return &var->toObject(resources); + var->clear(resources); + return &var->toObject(); } - uint8_t type() const { - return flags_ & VALUE_MASK; + VariantType type() const { + return type_; } - private: - void release(ResourceManager* resources) { - if (flags_ & OWNED_VALUE_BIT) - resources->dereferenceString(content_.asOwnedString->data); - auto collection = asCollection(); - if (collection) - collection->clear(resources); - } - void setType(uint8_t t) { - flags_ &= OWNED_KEY_BIT; - flags_ |= t; + 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; @@ -2552,15 +2665,15 @@ template struct VariantTo {}; template <> struct VariantTo { - typedef JsonArray type; + using type = JsonArray; }; template <> struct VariantTo { - typedef JsonObject type; + using type = JsonObject; }; template <> struct VariantTo { - typedef JsonVariant type; + using type = JsonVariant; }; class VariantAttorney { public: @@ -2598,31 +2711,31 @@ CompareResult arithmeticCompare(const T& lhs, const T& rhs) { template CompareResult arithmeticCompare( const T1& lhs, const T2& rhs, - typename enable_if::value && is_integral::value && - sizeof(T1) < sizeof(T2)>::type* = 0) { + 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, - typename enable_if::value && is_integral::value && - sizeof(T2) < sizeof(T1)>::type* = 0) { + 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, - typename enable_if::value && is_integral::value && - is_signed::value == is_signed::value && - sizeof(T2) == sizeof(T1)>::type* = 0) { + 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, - typename enable_if::value && is_integral::value && - is_unsigned::value && is_signed::value && - sizeof(T2) == sizeof(T1)>::type* = 0) { + 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)); @@ -2630,9 +2743,9 @@ CompareResult arithmeticCompare( template CompareResult arithmeticCompare( const T1& lhs, const T2& rhs, - typename enable_if::value && is_integral::value && - is_signed::value && is_unsigned::value && - sizeof(T2) == sizeof(T1)>::type* = 0) { + 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); @@ -2640,35 +2753,31 @@ CompareResult arithmeticCompare( template CompareResult arithmeticCompare( const T1& lhs, const T2& rhs, - typename enable_if::value || - is_floating_point::value>::type* = 0) { + enable_if_t::value || is_floating_point::value>* = + 0) { return arithmeticCompare(static_cast(lhs), static_cast(rhs)); } template CompareResult arithmeticCompareNegateLeft( - JsonUInt, const T2&, - typename enable_if::value>::type* = 0) { + JsonUInt, const T2&, enable_if_t::value>* = 0) { return COMPARE_RESULT_LESS; } template CompareResult arithmeticCompareNegateLeft( - JsonUInt lhs, const T2& rhs, - typename enable_if::value>::type* = 0) { + 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, - typename enable_if::value>::type* = 0) { + const T1&, JsonUInt, enable_if_t::value>* = 0) { return COMPARE_RESULT_GREATER; } template CompareResult arithmeticCompareNegateRight( - const T1& lhs, JsonUInt rhs, - typename enable_if::value>::type* = 0) { + const T1& lhs, JsonUInt rhs, enable_if_t::value>* = 0) { if (lhs > 0) return COMPARE_RESULT_GREATER; return arithmeticCompare(static_cast(rhs), -lhs); @@ -2687,10 +2796,9 @@ CompareResult compare(JsonVariantConst lhs, struct VariantOperatorTag {}; template struct VariantOperators : VariantOperatorTag { - template - friend - typename enable_if::value && !is_array::value, T>::type - operator|(const TVariant& variant, const T& defaultValue) { + template ::value && !is_array::value, int> = 0> + friend T operator|(const TVariant& variant, const T& defaultValue) { if (variant.template is()) return variant.template as(); else @@ -2704,119 +2812,113 @@ struct VariantOperators : VariantOperatorTag { return defaultValue; } template - friend typename enable_if::value, JsonVariantConst>::type - operator|(const TVariant& variant, T defaultValue) { + friend enable_if_t::value, JsonVariantConst> operator|( + const TVariant& variant, const T& defaultValue) { if (variant) return variant; else return defaultValue; } template - friend bool operator==(T* lhs, TVariant rhs) { + friend bool operator==(T* lhs, const TVariant& rhs) { return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; } template - friend bool operator==(const T& lhs, TVariant rhs) { + friend bool operator==(const T& lhs, const TVariant& rhs) { return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; } template - friend bool operator==(TVariant lhs, T* rhs) { + friend bool operator==(const TVariant& lhs, T* rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; + } + template ::value, int> = 0> + friend bool operator==(const TVariant& lhs, const T& rhs) { return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; } template - friend - typename enable_if::value, bool>::type - operator==(TVariant lhs, const T& rhs) { - return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; - } - template - friend bool operator!=(T* lhs, TVariant rhs) { + friend bool operator!=(T* lhs, const TVariant& rhs) { return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; } template - friend bool operator!=(const T& lhs, TVariant rhs) { + friend bool operator!=(const T& lhs, const TVariant& rhs) { return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; } template - friend bool operator!=(TVariant lhs, T* rhs) { + friend bool operator!=(const TVariant& lhs, T* rhs) { + return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; + } + template ::value, int> = 0> + friend bool operator!=(TVariant lhs, const T& rhs) { return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; } template - friend - typename enable_if::value, bool>::type - operator!=(TVariant lhs, const T& rhs) { - return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; - } - template - friend bool operator<(T* lhs, TVariant rhs) { + friend bool operator<(T* lhs, const TVariant& rhs) { return compare(rhs, lhs) == COMPARE_RESULT_GREATER; } template - friend bool operator<(const T& lhs, TVariant rhs) { + friend bool operator<(const T& lhs, const TVariant& rhs) { return compare(rhs, lhs) == COMPARE_RESULT_GREATER; } template - friend bool operator<(TVariant lhs, T* rhs) { + friend bool operator<(const TVariant& lhs, T* rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_LESS; + } + template ::value, int> = 0> + friend bool operator<(TVariant lhs, const T& rhs) { return compare(lhs, rhs) == COMPARE_RESULT_LESS; } template - friend - typename enable_if::value, bool>::type - operator<(TVariant lhs, const T& rhs) { - return compare(lhs, rhs) == COMPARE_RESULT_LESS; - } - template - friend bool operator<=(T* lhs, TVariant rhs) { + friend bool operator<=(T* lhs, const TVariant& rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } template - friend bool operator<=(const T& lhs, TVariant rhs) { + friend bool operator<=(const T& lhs, const TVariant& rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } template - friend bool operator<=(TVariant lhs, T* rhs) { + friend bool operator<=(const TVariant& lhs, T* rhs) { + return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template ::value, int> = 0> + friend bool operator<=(TVariant lhs, const T& rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template - friend - typename enable_if::value, bool>::type - operator<=(TVariant lhs, const T& rhs) { - return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; - } - template - friend bool operator>(T* lhs, TVariant rhs) { + friend bool operator>(T* lhs, const TVariant& rhs) { return compare(rhs, lhs) == COMPARE_RESULT_LESS; } template - friend bool operator>(const T& lhs, TVariant rhs) { + friend bool operator>(const T& lhs, const TVariant& rhs) { return compare(rhs, lhs) == COMPARE_RESULT_LESS; } template - friend bool operator>(TVariant lhs, T* rhs) { + friend bool operator>(const TVariant& lhs, T* rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_GREATER; + } + template ::value, int> = 0> + friend bool operator>(TVariant lhs, const T& rhs) { return compare(lhs, rhs) == COMPARE_RESULT_GREATER; } template - friend - typename enable_if::value, bool>::type - operator>(TVariant lhs, const T& rhs) { - return compare(lhs, rhs) == COMPARE_RESULT_GREATER; - } - template - friend bool operator>=(T* lhs, TVariant rhs) { + friend bool operator>=(T* lhs, const TVariant& rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template - friend bool operator>=(const T& lhs, TVariant rhs) { + friend bool operator>=(const T& lhs, const TVariant& rhs) { return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template - friend bool operator>=(TVariant lhs, T* rhs) { + friend bool operator>=(const TVariant& lhs, T* rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } - template - friend - typename enable_if::value, bool>::type - operator>=(TVariant lhs, const T& rhs) { + template ::value, int> = 0> + friend bool operator>=(const TVariant& lhs, const T& rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } }; @@ -2827,6 +2929,11 @@ 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, @@ -2844,56 +2951,81 @@ class JsonVariantConst : public detail::VariantTag, size_t size() const { return detail::VariantData::size(data_, resources_); } - template - typename detail::enable_if::value && - !detail::is_same::value, - T>::type - as() const { + template ::value, int> = 0> + T as() const { return Converter::fromJson(*this); } - template - typename detail::enable_if::value && - !detail::is_same::value, - bool>::type - is() const { + template ::value, int> = 0> + detail::InvalidConversion as() const; + template ::value, int> = 0> + bool is() const { return Converter::checkJson(*this); } + template ::value, int> = 0> + bool is() const { + return false; + } template operator T() const { return as(); } - JsonVariantConst operator[](size_t index) const { + template ::value, int> = 0> + JsonVariantConst operator[](T index) const { return JsonVariantConst( - detail::VariantData::getElement(data_, index, resources_), resources_); + detail::VariantData::getElement(data_, size_t(index), resources_), + resources_); } - template - typename detail::enable_if::value, - JsonVariantConst>::type - operator[](const TString& key) const { + template ::value, int> = 0> + JsonVariantConst operator[](const TString& key) const { return JsonVariantConst(detail::VariantData::getMember( data_, detail::adaptString(key), resources_), resources_); } - template - typename detail::enable_if::value, - JsonVariantConst>::type - operator[](TChar* key) const { + template ::value && + !detail::is_const::value, + int> = 0> + JsonVariantConst operator[](TChar* key) const { return JsonVariantConst(detail::VariantData::getMember( data_, detail::adaptString(key), resources_), resources_); } - template - typename detail::enable_if::value, bool>::type - containsKey(const TString& key) const { + template ::value, int> = 0> + JsonVariantConst operator[](const TVariant& key) const { + if (key.template is()) + return operator[](key.template as()); + else + return operator[](key.template as()); + } + template ::value, int> = 0> + ARDUINOJSON_DEPRECATED("use var[key].is() instead") + bool containsKey(const TString& key) const { return detail::VariantData::getMember(getData(), detail::adaptString(key), resources_) != 0; } - template - typename detail::enable_if::value, bool>::type - containsKey(TChar* key) const { + template ::value && + !detail::is_const::value, + int> = 0> + ARDUINOJSON_DEPRECATED("use obj[\"key\"].is() instead") + bool containsKey(TChar* key) const { return detail::VariantData::getMember(getData(), detail::adaptString(key), resources_) != 0; } + template ::value, int> = 0> + ARDUINOJSON_DEPRECATED("use var[key].is() instead") + bool containsKey(const TVariant& key) const { + return containsKey(key.template as()); + } ARDUINOJSON_DEPRECATED("always returns zero") size_t memoryUsage() const { return 0; @@ -2921,7 +3053,7 @@ class VariantRefBase : public VariantTag { friend class VariantAttorney; public: void clear() const { - VariantData::setNull(getOrCreateData(), getResourceManager()); + VariantData::clear(getOrCreateData(), getResourceManager()); } bool isNull() const { return VariantData::isNull(getData()); @@ -2930,88 +3062,97 @@ class VariantRefBase : public VariantTag { return !getData(); } template - typename enable_if::value, T>::type as() - const { - return Converter::fromJson(getVariantConst()); - } - template - typename enable_if::value, T>::type as() const; - template ::value>::type> + T as() const; + template ::value, int> = 0> operator T() const { return as(); } + template ::value, int> = 0> + JsonArray to() const; + template ::value, int> = 0> + JsonObject to() const; + template ::value, int> = 0> + JsonVariant to() const; template - typename enable_if::value, JsonArray>::type to() const; + FORCE_INLINE bool is() const; template - typename enable_if::value, JsonObject>::type to() - const; - template - typename enable_if::value, JsonVariant>::type to() - const; - template - FORCE_INLINE - typename enable_if::value, bool>::type - is() const; - template - FORCE_INLINE - typename enable_if::value, bool>::type - is() const { - return Converter::checkJson(getVariantConst()); + bool set(const T& value) const { + using TypeForConverter = conditional_t::value, T, + remove_cv_t>>; + return doSet>(value); + } + template ::value, int> = 0> + bool set(T* value) const { + return doSet>(value); } - template - bool set(const T& value) const; - template - bool set(T* value) const; size_t size() const { return VariantData::size(getData(), getResourceManager()); } size_t nesting() const { return VariantData::nesting(getData(), getResourceManager()); } - template - typename enable_if::value, T>::type add() const { + template ::value, int> = 0> + T add() const { return add().template to(); } - template - typename enable_if::value, T>::type add() const; + template ::value, int> = 0> + T add() const; template bool add(const T& value) const { - return add().set(value); + return detail::VariantData::addValue(getOrCreateData(), value, + getResourceManager()); } - template + template ::value, int> = 0> bool add(T* value) const { - return add().set(value); + return detail::VariantData::addValue(getOrCreateData(), value, + getResourceManager()); } void remove(size_t index) const { VariantData::removeElement(getData(), index, getResourceManager()); } - template - typename enable_if::value>::type remove(TChar* key) const { + template ::value, int> = 0> + void remove(TChar* key) const { VariantData::removeMember(getData(), adaptString(key), getResourceManager()); } - template - typename enable_if::value>::type remove( - const TString& key) const { + template ::value, int> = 0> + void remove(const TString& key) const { VariantData::removeMember(getData(), adaptString(key), getResourceManager()); } + template ::value, int> = 0> + void 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 - typename enable_if::value, bool>::type containsKey( + template ::value, int> = 0> + ARDUINOJSON_DEPRECATED("use obj[key].is() instead") + bool containsKey(const TString& key) const; + template ::value, int> = 0> + ARDUINOJSON_DEPRECATED("use obj[\"key\"].is() instead") + bool containsKey(TChar* key) const; + template ::value, int> = 0> + ARDUINOJSON_DEPRECATED("use obj[key].is() instead") + bool containsKey(const TVariant& key) const; + template ::value, int> = 0> + FORCE_INLINE MemberProxy> operator[]( const TString& key) const; - template - typename enable_if::value, bool>::type containsKey( + template < + typename TChar, + enable_if_t::value && !is_const::value, int> = 0> + FORCE_INLINE MemberProxy> operator[]( TChar* key) const; - template - FORCE_INLINE typename enable_if::value, - MemberProxy>::type - operator[](const TString& key) const; - template - FORCE_INLINE typename enable_if::value, - MemberProxy>::type - operator[](TChar* key) const; + template ::value, int> = 0> + 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") @@ -3058,17 +3199,41 @@ class VariantRefBase : public VariantTag { 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(const T& value) const { + return doSet( + value, is_same::return_type, + bool>{}); + } + template + bool doSet(const T& value, false_type) const; + template + bool doSet(const T& value, true_type) const; ArduinoJson::JsonVariant getOrCreateVariant() const; }; template class ElementProxy : public VariantRefBase>, public VariantOperators> { friend class VariantAttorney; + friend class VariantRefBase>; + template + friend class MemberProxy; + template + friend class ElementProxy; 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; @@ -3084,6 +3249,8 @@ class ElementProxy : public VariantRefBase>, return *this; } private: + ElementProxy(const ElementProxy& src) // Error here? See https://arduinojson.org/v7/proxy-non-copyable/ + : upstream_(src.upstream_), index_(src.index_) {} ResourceManager* getResourceManager() const { return VariantAttorney::getResourceManager(upstream_); } @@ -3135,15 +3302,10 @@ struct Converter : private detail::VariantAttorney { static JsonVariant fromJson(JsonVariant src) { return src; } - static detail::InvalidConversion fromJson( - JsonVariantConst); static bool checkJson(JsonVariant src) { auto data = getData(src); return !!data; } - static bool checkJson(JsonVariantConst) { - return false; - } }; template <> struct Converter : private detail::VariantAttorney { @@ -3230,7 +3392,7 @@ class JsonArrayConst : public detail::VariantOperators { friend class JsonArray; friend class detail::VariantAttorney; public: - typedef JsonArrayConstIterator iterator; + using iterator = JsonArrayConstIterator; iterator begin() const { if (!data_) return iterator(); @@ -3239,13 +3401,24 @@ class JsonArrayConst : public detail::VariantOperators { iterator end() const { return iterator(); } - JsonArrayConst() : data_(0) {} + JsonArrayConst() : data_(0), resources_(0) {} JsonArrayConst(const detail::ArrayData* data, const detail::ResourceManager* resources) : data_(data), resources_(resources) {} - JsonVariantConst operator[](size_t index) const { + template ::value, int> = 0> + JsonVariantConst operator[](T index) const { return JsonVariantConst( - detail::ArrayData::getElement(data_, index, resources_), resources_); + detail::ArrayData::getElement(data_, size_t(index), resources_), + resources_); + } + template ::value, int> = 0> + 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_); @@ -3295,7 +3468,7 @@ class JsonObject; class JsonArray : public detail::VariantOperators { friend class detail::VariantAttorney; public: - typedef JsonArrayIterator iterator; + using iterator = JsonArrayIterator; JsonArray() : data_(0), resources_(0) {} JsonArray(detail::ArrayData* data, detail::ResourceManager* resources) : data_(data), resources_(resources) {} @@ -3307,24 +3480,25 @@ class JsonArray : public detail::VariantOperators { operator JsonArrayConst() const { return JsonArrayConst(data_, resources_); } - template - typename detail::enable_if::value, T>::type - add() const { + template ::value, int> = 0> + T add() const { return add().to(); } - template - typename detail::enable_if::value, T>::type - add() const { + template ::value, int> = 0> + JsonVariant add() const { return JsonVariant(detail::ArrayData::addElement(data_, resources_), resources_); } template bool add(const T& value) const { - return add().set(value); + return detail::ArrayData::addValue(data_, value, resources_); } - template + template ::value, int> = 0> bool add(T* value) const { - return add().set(value); + return detail::ArrayData::addValue(data_, value, resources_); } iterator begin() const { if (!data_) @@ -3350,11 +3524,27 @@ class JsonArray : public detail::VariantOperators { void remove(size_t index) const { detail::ArrayData::removeElement(data_, index, resources_); } + template ::value, int> = 0> + void remove(const TVariant& variant) const { + if (variant.template is()) + remove(variant.template as()); + } void clear() const { detail::ArrayData::clear(data_, resources_); } - detail::ElementProxy operator[](size_t index) const { - return {*this, index}; + template ::value, int> = 0> + detail::ElementProxy operator[](T index) const { + return {*this, size_t(index)}; + } + template ::value, int> = 0> + detail::ElementProxy operator[](const TVariant& variant) const { + if (variant.template is()) + return {*this, variant.template as()}; + else + return {*this, size_t(-1)}; } operator JsonVariantConst() const { return JsonVariantConst(collectionToVariant(data_), resources_); @@ -3401,42 +3591,42 @@ class JsonArray : public detail::VariantOperators { class JsonPair { public: JsonPair(detail::ObjectData::iterator iterator, - detail::ResourceManager* resources) - : iterator_(iterator), resources_(resources) {} + detail::ResourceManager* resources) { + if (!iterator.done()) { + key_ = iterator->asString(); + iterator.next(resources); + value_ = JsonVariant(iterator.data(), resources); + } + } JsonString key() const { - if (!iterator_.done()) - return JsonString(iterator_.key(), iterator_.ownsKey() - ? JsonString::Copied - : JsonString::Linked); - else - return JsonString(); + return key_; } JsonVariant value() { - return JsonVariant(iterator_.data(), resources_); + return value_; } private: - detail::ObjectData::iterator iterator_; - detail::ResourceManager* resources_; + JsonString key_; + JsonVariant value_; }; class JsonPairConst { public: JsonPairConst(detail::ObjectData::iterator iterator, - const detail::ResourceManager* resources) - : iterator_(iterator), resources_(resources) {} + const detail::ResourceManager* resources) { + if (!iterator.done()) { + key_ = iterator->asString(); + iterator.next(resources); + value_ = JsonVariantConst(iterator.data(), resources); + } + } JsonString key() const { - if (!iterator_.done()) - return JsonString(iterator_.key(), iterator_.ownsKey() - ? JsonString::Copied - : JsonString::Linked); - else - return JsonString(); + return key_; } JsonVariantConst value() const { - return JsonVariantConst(iterator_.data(), resources_); + return value_; } private: - detail::ObjectData::iterator iterator_; - const detail::ResourceManager* resources_; + JsonString key_; + JsonVariantConst value_; }; class JsonObjectIterator { friend class JsonObject; @@ -3458,7 +3648,8 @@ class JsonObjectIterator { return iterator_ != other.iterator_; } JsonObjectIterator& operator++() { - iterator_.next(resources_); + iterator_.next(resources_); // key + iterator_.next(resources_); // value return *this; } private: @@ -3485,7 +3676,8 @@ class JsonObjectConstIterator { return iterator_ != other.iterator_; } JsonObjectConstIterator& operator++() { - iterator_.next(resources_); + iterator_.next(resources_); // key + iterator_.next(resources_); // value return *this; } private: @@ -3496,8 +3688,8 @@ class JsonObjectConst : public detail::VariantOperators { friend class JsonObject; friend class detail::VariantAttorney; public: - typedef JsonObjectConstIterator iterator; - JsonObjectConst() : data_(0) {} + using iterator = JsonObjectConstIterator; + JsonObjectConst() : data_(0), resources_(0) {} JsonObjectConst(const detail::ObjectData* data, const detail::ResourceManager* resources) : data_(data), resources_(resources) {} @@ -3524,32 +3716,49 @@ class JsonObjectConst : public detail::VariantOperators { iterator end() const { return iterator(); } - template + template ::value, int> = 0> + ARDUINOJSON_DEPRECATED("use obj[key].is() instead") 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 - typename detail::enable_if::value, - JsonVariantConst>::type - operator[](const TString& key) const { + template ::value, int> = 0> + ARDUINOJSON_DEPRECATED("use obj[key].is() instead") + bool containsKey(const TVariant& key) const { + return containsKey(key.template as()); + } + template ::value, int> = 0> + JsonVariantConst operator[](const TString& key) const { return JsonVariantConst(detail::ObjectData::getMember( data_, detail::adaptString(key), resources_), resources_); } - template - typename detail::enable_if::value, - JsonVariantConst>::type - operator[](TChar* key) const { + template ::value && + !detail::is_const::value, + int> = 0> + JsonVariantConst operator[](TChar* key) const { return JsonVariantConst(detail::ObjectData::getMember( data_, detail::adaptString(key), resources_), resources_); } + template ::value, int> = 0> + 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; @@ -3579,16 +3788,19 @@ inline bool operator==(JsonObjectConst lhs, JsonObjectConst rhs) { } ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -template +template class MemberProxy - : public VariantRefBase>, - public VariantOperators> { + : public VariantRefBase>, + public VariantOperators> { friend class VariantAttorney; + friend class VariantRefBase>; + template + friend class MemberProxy; + template + friend class ElementProxy; public: - MemberProxy(TUpstream upstream, TStringRef key) + MemberProxy(TUpstream upstream, AdaptedString 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; @@ -3598,30 +3810,32 @@ class MemberProxy this->set(src); return *this; } - template + template ::value, int> = 0> MemberProxy& operator=(T* src) { this->set(src); return *this; } private: + MemberProxy(const MemberProxy& src) // Error here? See https://arduinojson.org/v7/proxy-non-copyable/ + : upstream_(src.upstream_), key_(src.key_) {} ResourceManager* getResourceManager() const { return VariantAttorney::getResourceManager(upstream_); } VariantData* getData() const { return VariantData::getMember( - VariantAttorney::getData(upstream_), adaptString(key_), + VariantAttorney::getData(upstream_), key_, VariantAttorney::getResourceManager(upstream_)); } VariantData* getOrCreateData() const { auto data = VariantAttorney::getOrCreateData(upstream_); if (!data) return nullptr; - return data->getOrAddMember(adaptString(key_), + return data->getOrAddMember(key_, VariantAttorney::getResourceManager(upstream_)); } private: TUpstream upstream_; - TStringRef key_; + AdaptedString key_; }; ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE @@ -3629,7 +3843,7 @@ class JsonArray; class JsonObject : public detail::VariantOperators { friend class detail::VariantAttorney; public: - typedef JsonObjectIterator iterator; + using iterator = JsonObjectIterator; JsonObject() : data_(0), resources_(0) {} JsonObject(detail::ObjectData* data, detail::ResourceManager* resource) : data_(data), resources_(resource) {} @@ -3677,43 +3891,68 @@ class JsonObject : public detail::VariantOperators { } return true; } - template - typename detail::enable_if::value, - detail::MemberProxy>::type - operator[](const TString& key) const { - return {*this, key}; + template ::value, int> = 0> + detail::MemberProxy> operator[]( + const TString& key) const { + return {*this, detail::adaptString(key)}; } - template - typename detail::enable_if::value, - detail::MemberProxy>::type - operator[](TChar* key) const { - return {*this, key}; + template ::value && + !detail::is_const::value, + int> = 0> + detail::MemberProxy> operator[]( + TChar* key) const { + return {*this, detail::adaptString(key)}; + } + template ::value, int> = 0> + detail::MemberProxy> operator[]( + const TVariant& key) const { + return {*this, detail::adaptString(key.template as())}; } FORCE_INLINE void remove(iterator it) const { detail::ObjectData::remove(data_, it.iterator_, resources_); } - template - FORCE_INLINE void remove(const TString& key) const { + template ::value, int> = 0> + void remove(const TString& key) const { detail::ObjectData::removeMember(data_, detail::adaptString(key), resources_); } + template ::value, int> = 0> + void 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 - typename detail::enable_if::value, bool>::type - containsKey(const TString& key) const { + template ::value, int> = 0> + ARDUINOJSON_DEPRECATED("use obj[key].is() instead") + bool containsKey(const TString& key) const { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } - template - typename detail::enable_if::value, bool>::type - containsKey(TChar* key) const { + template ::value && + !detail::is_const::value, + int> = 0> + ARDUINOJSON_DEPRECATED("use obj[\"key\"].is() instead") + bool containsKey(TChar* key) const { return detail::ObjectData::getMember(data_, detail::adaptString(key), resources_) != 0; } + template ::value, int> = 0> + ARDUINOJSON_DEPRECATED("use obj[key].is() instead") + bool containsKey(const TVariant& key) const { + return containsKey(key.template as()); + } template ARDUINOJSON_DEPRECATED("use obj[key].to() instead") JsonArray createNestedArray(TChar* key) const { @@ -3763,16 +4002,15 @@ class JsonDocument : public detail::VariantOperators { : JsonDocument(detail::DefaultAllocator::instance()) { swap(*this, src); } - template + template ::value || + detail::is_same::value || + detail::is_same::value || + detail::is_same::value || + detail::is_same::value, + int> = 0> JsonDocument(const T& src, - Allocator* alloc = detail::DefaultAllocator::instance(), - typename detail::enable_if< - detail::is_same::value || - detail::is_same::value || - detail::is_same::value || - detail::is_same::value || - detail::is_same::value || - detail::is_same::value>::type* = 0) + Allocator* alloc = detail::DefaultAllocator::instance()) : JsonDocument(alloc) { set(src); } @@ -3826,10 +4064,15 @@ class JsonDocument : public detail::VariantOperators { bool set(const JsonDocument& src) { return to().set(src.as()); } - template - typename detail::enable_if::value, - bool>::type - set(const T& src) { + template < + typename T, + detail::enable_if_t::value, int> = 0> + bool set(const T& src) { + return to().set(src); + } + template ::value, int> = 0> + bool set(TChar* src) { return to().set(src); } template @@ -3838,78 +4081,114 @@ class JsonDocument : public detail::VariantOperators { 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 + template ::value, int> = 0> + ARDUINOJSON_DEPRECATED("use doc[key].is() instead") bool containsKey(const TString& key) const { return data_.getMember(detail::adaptString(key), &resources_) != 0; } - template - typename detail::enable_if::value, - detail::MemberProxy>::type - operator[](const TString& key) { - return {*this, key}; + template ::value, int> = 0> + ARDUINOJSON_DEPRECATED("use doc[key].is() instead") + bool containsKey(const TVariant& key) const { + return containsKey(key.template as()); } - template - typename detail::enable_if::value, - detail::MemberProxy>::type - operator[](TChar* key) { - return {*this, key}; + template ::value, int> = 0> + detail::MemberProxy> operator[]( + const TString& key) { + return {*this, detail::adaptString(key)}; } - template - typename detail::enable_if::value, - JsonVariantConst>::type - operator[](const TString& key) const { + template ::value && + !detail::is_const::value, + int> = 0> + detail::MemberProxy> operator[]( + TChar* key) { + return {*this, detail::adaptString(key)}; + } + template ::value, int> = 0> + JsonVariantConst operator[](const TString& key) const { return JsonVariantConst( data_.getMember(detail::adaptString(key), &resources_), &resources_); } - template - typename detail::enable_if::value, - JsonVariantConst>::type - operator[](TChar* key) const { + template ::value && + !detail::is_const::value, + int> = 0> + JsonVariantConst operator[](TChar* key) const { return JsonVariantConst( data_.getMember(detail::adaptString(key), &resources_), &resources_); } - detail::ElementProxy operator[](size_t index) { - return {*this, index}; + template ::value, int> = 0> + detail::ElementProxy operator[](T index) { + return {*this, size_t(index)}; } JsonVariantConst operator[](size_t index) const { return JsonVariantConst(data_.getElement(index, &resources_), &resources_); } - template - typename detail::enable_if::value, T>::type - add() { + template ::value, int> = 0> + 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 ::value, int> = 0> + T add() { return add().to(); } - template - typename detail::enable_if::value, T>::type - add() { + template ::value, int> = 0> + JsonVariant add() { return JsonVariant(data_.addElement(&resources_), &resources_); } template bool add(const TValue& value) { - return add().set(value); + return data_.addValue(value, &resources_); } - template + template ::value, int> = 0> bool add(TChar* value) { - return add().set(value); + return data_.addValue(value, &resources_); } - void remove(size_t index) { - detail::VariantData::removeElement(getData(), index, getResourceManager()); + template ::value, int> = 0> + void remove(T index) { + detail::VariantData::removeElement(getData(), size_t(index), + getResourceManager()); } - template - typename detail::enable_if::value>::type remove( - TChar* key) { + template ::value && + !detail::is_const::value, + int> = 0> + void remove(TChar* key) { detail::VariantData::removeMember(getData(), detail::adaptString(key), getResourceManager()); } - template - typename detail::enable_if::value>::type remove( - const TString& key) { + template ::value, int> = 0> + void remove(const TString& key) { detail::VariantData::removeMember(getData(), detail::adaptString(key), getResourceManager()); } + template ::value, int> = 0> + void remove(const TVariant& key) { + if (key.template is()) + remove(key.template as()); + if (key.template is()) + remove(key.template as()); + } operator JsonVariant() { return getVariant(); } @@ -3985,7 +4264,7 @@ ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template struct VariantDataVisitor { - typedef TResult result_type; + using result_type = TResult; template TResult visit(const T&) { return TResult(); @@ -3993,7 +4272,7 @@ struct VariantDataVisitor { }; template struct JsonVariantVisitor { - typedef TResult result_type; + using result_type = TResult; template TResult visit(const T&) { return TResult(); @@ -4027,14 +4306,13 @@ typename TVisitor::result_type accept(JsonVariantConst variant, return visit.visit(nullptr); auto resources = VariantAttorney::getResourceManager(variant); VisitorAdapter adapter(visit, resources); - return data->accept(adapter); + return data->accept(adapter, resources); } struct ComparerBase : JsonVariantVisitor {}; template struct Comparer; template -struct Comparer::value>::type> - : ComparerBase { +struct Comparer::value>> : ComparerBase { T rhs; // TODO: store adapted string? explicit Comparer(T value) : rhs(value) {} CompareResult visit(JsonString lhs) { @@ -4055,24 +4333,23 @@ struct Comparer::value>::type> using ComparerBase::visit; }; template -struct Comparer::value || - is_floating_point::value>::type> +struct Comparer< + T, enable_if_t::value || is_floating_point::value>> : ComparerBase { T rhs; explicit Comparer(T value) : rhs(value) {} - CompareResult visit(JsonFloat lhs) { + template + enable_if_t::value || is_integral::value, + CompareResult> + visit(const U& lhs) { return arithmeticCompare(lhs, rhs); } - CompareResult visit(JsonInteger lhs) { - return arithmeticCompare(lhs, rhs); + template + enable_if_t::value && !is_integral::value, + CompareResult> + visit(const U& lhs) { + return ComparerBase::visit(lhs); } - CompareResult visit(JsonUInt lhs) { - return arithmeticCompare(lhs, rhs); - } - CompareResult visit(bool lhs) { - return visit(static_cast(lhs)); - } - using ComparerBase::visit; }; struct NullComparer : ComparerBase { CompareResult visit(nullptr_t) { @@ -4175,8 +4452,8 @@ struct VariantComparer : ComparerBase { } }; template -struct Comparer::value>::type> +struct Comparer< + T, enable_if_t::value>> : VariantComparer { explicit Comparer(const T& value) : VariantComparer(static_cast(value)) {} @@ -4195,6 +4472,13 @@ inline ArrayData::iterator ArrayData::at( } 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); @@ -4220,23 +4504,39 @@ inline VariantData* ArrayData::getElement( inline void ArrayData::removeElement(size_t index, ResourceManager* resources) { remove(at(index, resources), resources); } +template +inline bool ArrayData::addValue(const T& value, ResourceManager* resources) { + ARDUINOJSON_ASSERT(resources != nullptr); + auto slot = resources->allocVariant(); + if (!slot) + return false; + JsonVariant variant(slot.ptr(), resources); + if (!variant.set(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 typename detail::enable_if::value, bool>::type -copyArray(const T& src, JsonVariant dst) { +template ::value, int> = 0> +inline bool copyArray(const T& src, JsonVariant dst) { return dst.set(src); } -template -inline typename detail::enable_if< - !detail::is_base_of::value, bool>::type -copyArray(T (&src)[N], const TDestination& dst) { +template ::value, int> = 0> +inline bool copyArray(T (&src)[N], const TDestination& dst) { return copyArray(src, N, dst); } -template -inline typename detail::enable_if< - !detail::is_base_of::value, bool>::type -copyArray(const T* src, size_t len, const TDestination& dst) { +template ::value, int> = 0> +inline 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()); @@ -4255,9 +4555,8 @@ template inline bool copyArray(const T* src, size_t len, JsonDocument& dst) { return copyArray(src, len, dst.to()); } -template -inline typename detail::enable_if::value, size_t>::type -copyArray(JsonVariantConst src, T& dst) { +template ::value, int> = 0> +inline size_t copyArray(JsonVariantConst src, T& dst) { dst = src.as(); return 1; } @@ -4283,12 +4582,12 @@ inline size_t copyArray(JsonVariantConst src, char (&dst)[N]) { dst[len] = 0; return 1; } -template -inline typename detail::enable_if< - detail::is_array::value && - detail::is_base_of::value, - size_t>::type -copyArray(const TSource& src, T& dst) { +template < + typename TSource, typename T, + detail::enable_if_t::value && + detail::is_base_of::value, + int> = 0> +inline size_t copyArray(const TSource& src, T& dst) { return copyArray(src.template as(), dst); } ARDUINOJSON_END_PUBLIC_NAMESPACE @@ -4329,75 +4628,70 @@ inline T* addPadding(T* p) { size_t address = addPadding(reinterpret_cast(p)); return reinterpret_cast(address); } -inline CollectionIterator::CollectionIterator(VariantSlot* slot, SlotId slotId) +inline CollectionIterator::CollectionIterator(VariantData* slot, SlotId slotId) : slot_(slot), currentId_(slotId) { nextId_ = slot_ ? slot_->next() : NULL_SLOT; } -inline const char* CollectionIterator::key() const { - ARDUINOJSON_ASSERT(slot_ != nullptr); - return slot_->key(); -} -inline void CollectionIterator::setKey(const char* s) { - ARDUINOJSON_ASSERT(slot_ != nullptr); - ARDUINOJSON_ASSERT(s != nullptr); - return slot_->setKey(s); -} -inline void CollectionIterator::setKey(StringNode* s) { - ARDUINOJSON_ASSERT(slot_ != nullptr); - ARDUINOJSON_ASSERT(s != nullptr); - return slot_->setKey(s); -} -inline bool CollectionIterator::ownsKey() const { - ARDUINOJSON_ASSERT(slot_ != nullptr); - return slot_->ownsKey(); -} inline void CollectionIterator::next(const ResourceManager* resources) { ARDUINOJSON_ASSERT(currentId_ != NULL_SLOT); - slot_ = resources->getSlot(nextId_); + slot_ = resources->getVariant(nextId_); currentId_ = nextId_; if (slot_) nextId_ = slot_->next(); } -inline CollectionData::iterator CollectionData::addSlot( - ResourceManager* resources) { - auto slot = resources->allocSlot(); - if (!slot) - return {}; +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->getSlot(tail_); + auto tail = resources->getVariant(tail_); tail->setNext(slot.id()); tail_ = slot.id(); } else { head_ = slot.id(); tail_ = slot.id(); } - return iterator(slot, 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->getSlot(next); + auto slot = resources->getVariant(next); next = slot->next(); - releaseSlot(SlotWithId(slot, currId), resources); + resources->freeVariant({slot, currId}); } head_ = NULL_SLOT; tail_ = NULL_SLOT; } -inline SlotWithId CollectionData::getPreviousSlot( - VariantSlot* target, const ResourceManager* resources) const { - auto prev = SlotWithId(); +inline Slot CollectionData::getPreviousSlot( + VariantData* target, const ResourceManager* resources) const { + auto prev = Slot(); auto currentId = head_; while (currentId != NULL_SLOT) { - auto currentSlot = resources->getSlot(currentId); + auto currentSlot = resources->getVariant(currentId); if (currentSlot == target) - return prev; - prev = SlotWithId(currentSlot, currentId); + break; + prev = Slot(currentSlot, currentId); currentId = currentSlot->next(); } - return SlotWithId(); + return prev; } -inline void CollectionData::remove(iterator it, ResourceManager* resources) { +inline void CollectionData::removeOne(iterator it, ResourceManager* resources) { if (it.done()) return; auto curr = it.slot_; @@ -4409,7 +4703,18 @@ inline void CollectionData::remove(iterator it, ResourceManager* resources) { head_ = next; if (next == NULL_SLOT) tail_ = prev.id(); - releaseSlot({it.slot_, it.currentId_}, resources); + 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; @@ -4426,82 +4731,53 @@ inline size_t CollectionData::size(const ResourceManager* resources) const { count++; return count; } -inline void CollectionData::releaseSlot(SlotWithId slot, - ResourceManager* resources) { - if (slot->ownsKey()) - resources->dereferenceString(slot->key()); - slot->data()->setNull(resources); - resources->freeSlot(slot); -} -inline void VariantPool::create(SlotCount cap, Allocator* allocator) { - ARDUINOJSON_ASSERT(cap > 0); - slots_ = - reinterpret_cast(allocator->allocate(slotsToBytes(cap))); - capacity_ = slots_ ? cap : 0; - usage_ = 0; -} -inline void VariantPool::destroy(Allocator* allocator) { - if (slots_) - allocator->deallocate(slots_); - slots_ = nullptr; - capacity_ = 0; - usage_ = 0; -} -inline void VariantPool::shrinkToFit(Allocator* allocator) { - auto newSlots = reinterpret_cast( - allocator->reallocate(slots_, slotsToBytes(usage_))); - if (newSlots) { - slots_ = newSlots; - capacity_ = usage_; +inline Slot ResourceManager::allocVariant() { + auto p = variantPools_.allocSlot(allocator_); + if (!p) { + overflowed_ = true; + return {}; } + return {new (&p->variant) VariantData, p.id()}; } -inline SlotWithId VariantPool::allocSlot() { - if (!slots_) +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 {}; - if (usage_ >= capacity_) - return {}; - auto index = usage_++; - auto slot = &slots_[index]; - return {new (slot) VariantSlot, SlotId(index)}; + } + return {&p->extension, p.id()}; } -inline VariantSlot* VariantPool::getSlot(SlotId id) const { - ARDUINOJSON_ASSERT(id < usage_); - return &slots_[id]; +inline void ResourceManager::freeExtension(SlotId id) { + auto p = getExtension(id); + variantPools_.freeSlot({reinterpret_cast(p), id}); } -inline SlotCount VariantPool::usage() const { - return usage_; -} -inline void VariantPool::clear() { - usage_ = 0; -} -inline SlotCount VariantPool::bytesToSlots(size_t n) { - return static_cast(n / sizeof(VariantSlot)); -} -inline size_t VariantPool::slotsToBytes(SlotCount n) { - return n * sizeof(VariantSlot); -} -inline SlotWithId VariantPoolList::allocFromFreeList() { - ARDUINOJSON_ASSERT(freeList_ != NULL_SLOT); - auto id = freeList_; - auto slot = getSlot(freeList_); - freeList_ = slot->next(); - return {new (slot) VariantSlot, id}; -} -inline void VariantPoolList::freeSlot(SlotWithId slot) { - slot->setNext(freeList_); - freeList_ = slot.id(); +inline VariantExtension* ResourceManager::getExtension(SlotId id) const { + return &variantPools_.getSlot(id)->extension; } +#endif template inline VariantData* ObjectData::getMember( TAdaptedString key, const ResourceManager* resources) const { - return findKey(key, resources).data(); + 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 it = findKey(key, resources); - if (!it.done()) - return it.data(); + auto data = getMember(key, resources); + if (data) + return data; return addMember(key, resources); } template @@ -4509,9 +4785,11 @@ 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 (stringEquals(key, adaptString(it.key()))) + if (isKey && stringEquals(key, adaptString(it->asString()))) return it; + isKey = !isKey; } return iterator(); } @@ -4520,6 +4798,23 @@ 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) { @@ -4540,68 +4835,72 @@ class EscapeSequence { } } private: - static const char* escapeTable(bool excludeSolidus) { - return &"//\"\"\\\\b\bf\fn\nr\rt\t"[excludeSolidus ? 2 : 0]; + static const char* escapeTable(bool isSerializing) { + return &"//''\"\"\\\\b\bf\fn\nr\rt\t"[isSerializing ? 4 : 0]; } }; -template struct FloatParts { uint32_t integral; uint32_t decimal; int16_t exponent; int8_t decimalPlaces; - FloatParts(TFloat value) { - uint32_t maxDecimalPart = sizeof(TFloat) >= 8 ? 1000000000 : 1000000; - decimalPlaces = sizeof(TFloat) >= 8 ? 9 : 6; - exponent = normalize(value); - integral = uint32_t(value); - for (uint32_t tmp = integral; tmp >= 10; tmp /= 10) { - maxDecimalPart /= 10; - decimalPlaces--; - } - TFloat remainder = (value - TFloat(integral)) * TFloat(maxDecimalPart); - decimal = uint32_t(remainder); - remainder = remainder - TFloat(decimal); - decimal += uint32_t(remainder * 2); - if (decimal >= maxDecimalPart) { - decimal = 0; - integral++; - if (exponent && integral >= 10) { - exponent++; - integral = 1; - } - } - while (decimal % 10 == 0 && decimalPlaces > 0) { - decimal /= 10; - decimalPlaces--; - } - } - static int16_t normalize(TFloat& value) { - typedef FloatTraits traits; - int16_t powersOf10 = 0; - int8_t index = sizeof(TFloat) == 8 ? 8 : 5; - int bit = 1 << index; - if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) { - for (; index >= 0; index--) { - if (value >= traits::positiveBinaryPowersOfTen()[index]) { - value *= traits::negativeBinaryPowersOfTen()[index]; - powersOf10 = int16_t(powersOf10 + bit); - } - bit >>= 1; - } - } - if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) { - for (; index >= 0; index--) { - if (value < traits::negativeBinaryPowersOfTen()[index] * 10) { - value *= traits::positiveBinaryPowersOfTen()[index]; - powersOf10 = int16_t(powersOf10 - bit); - } - bit >>= 1; - } - } - return powersOf10; - } }; +template +inline int16_t normalize(TFloat& value) { + using traits = FloatTraits; + 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: @@ -4660,6 +4959,9 @@ class TextFormatter { } 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 @@ -4677,7 +4979,7 @@ class TextFormatter { value = -value; } #endif - FloatParts parts(value); + auto parts = decomposeFloat(value, decimalPlaces); writeInteger(parts.integral); if (parts.decimalPlaces) writeDecimals(parts.decimal, parts.decimalPlaces); @@ -4687,8 +4989,8 @@ class TextFormatter { } } template - typename enable_if::value>::type writeInteger(T value) { - typedef typename make_unsigned::type unsigned_type; + enable_if_t::value> writeInteger(T value) { + using unsigned_type = make_unsigned_t; unsigned_type unsigned_value; if (value < 0) { writeRaw('-'); @@ -4699,7 +5001,7 @@ class TextFormatter { writeInteger(unsigned_value); } template - typename enable_if::value>::type writeInteger(T value) { + enable_if_t::value> writeInteger(T value) { char buffer[22]; char* end = buffer + sizeof(buffer); char* begin = end; @@ -4752,9 +5054,10 @@ class DummyWriter { template