diff --git a/screenshot/Alarm_Screen_Raster.svg b/DOKU/Alarm_Screen_Raster.svg similarity index 100% rename from screenshot/Alarm_Screen_Raster.svg rename to DOKU/Alarm_Screen_Raster.svg diff --git a/screenshot/Config_Screen_Raster.svg b/DOKU/Config_Screen_Raster.svg similarity index 100% rename from screenshot/Config_Screen_Raster.svg rename to DOKU/Config_Screen_Raster.svg diff --git a/screenshot/Main_Screen_Raster.svg b/DOKU/Main_Screen_Raster.svg similarity index 100% rename from screenshot/Main_Screen_Raster.svg rename to DOKU/Main_Screen_Raster.svg diff --git a/README.md b/README.md index 75a0e18..4a94eff 100644 --- a/README.md +++ b/README.md @@ -103,3 +103,5 @@ Zum Zeitpunkt der Veröffentlichung des Blogartikels gab es ein vollständiges S ![Konfigurationswebsite](screenshot/website_3.png?raw=true "Radio") ![Konfigurationswebsite](screenshot/website_4.png?raw=true "WLAN") + +![Info Tab](screenshot/info_tab.png?raw=true "Info Tab") diff --git a/radiowecker/ArduinoJson.h b/radiowecker/ArduinoJson.h index d9d1c39..f0ca223 100644 --- a/radiowecker/ArduinoJson.h +++ b/radiowecker/ArduinoJson.h @@ -1,5 +1,5 @@ // ArduinoJson - https://arduinojson.org -// Copyright © 2014-2023, Benoit BLANCHON +// Copyright © 2014-2024, Benoit BLANCHON // MIT License #pragma once @@ -56,26 +56,58 @@ #ifndef ARDUINOJSON_USE_DOUBLE # define ARDUINOJSON_USE_DOUBLE 1 #endif -#ifndef ARDUINOJSON_USE_LONG_LONG -# if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ >= 4 || \ - defined(_MSC_VER) -# define ARDUINOJSON_USE_LONG_LONG 1 +#ifndef ARDUINOJSON_SIZEOF_POINTER +# if defined(__SIZEOF_POINTER__) +# define ARDUINOJSON_SIZEOF_POINTER __SIZEOF_POINTER__ +# elif defined(_WIN64) && _WIN64 +# define ARDUINOJSON_SIZEOF_POINTER 8 // 64 bits +# else +# define ARDUINOJSON_SIZEOF_POINTER 4 // assume 32 bits otherwise # endif #endif #ifndef ARDUINOJSON_USE_LONG_LONG -# define ARDUINOJSON_USE_LONG_LONG 0 +# if ARDUINOJSON_SIZEOF_POINTER >= 4 // 32 & 64 bits systems +# define ARDUINOJSON_USE_LONG_LONG 1 +# else +# define ARDUINOJSON_USE_LONG_LONG 0 +# endif #endif #ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT # define ARDUINOJSON_DEFAULT_NESTING_LIMIT 10 #endif -#ifndef ARDUINOJSON_SLOT_OFFSET_SIZE -# if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ <= 2 -# define ARDUINOJSON_SLOT_OFFSET_SIZE 1 -# elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ >= 8 || \ - defined(_WIN64) && _WIN64 -# define ARDUINOJSON_SLOT_OFFSET_SIZE 4 +#ifndef ARDUINOJSON_SLOT_ID_SIZE +# if ARDUINOJSON_SIZEOF_POINTER <= 2 +# define ARDUINOJSON_SLOT_ID_SIZE 1 +# elif ARDUINOJSON_SIZEOF_POINTER == 4 +# define ARDUINOJSON_SLOT_ID_SIZE 2 +# else +# define ARDUINOJSON_SLOT_ID_SIZE 4 +# endif +#endif +#ifndef ARDUINOJSON_POOL_CAPACITY +# if ARDUINOJSON_SIZEOF_POINTER <= 2 +# define ARDUINOJSON_POOL_CAPACITY 16 // 128 bytes +# elif ARDUINOJSON_SIZEOF_POINTER == 4 +# define ARDUINOJSON_POOL_CAPACITY 64 // 1024 bytes +# else +# define ARDUINOJSON_POOL_CAPACITY 128 // 3072 bytes +# endif +#endif +#ifndef ARDUINOJSON_INITIAL_POOL_COUNT +# define ARDUINOJSON_INITIAL_POOL_COUNT 4 +#endif +#ifndef ARDUINOJSON_AUTO_SHRINK +# if ARDUINOJSON_SIZEOF_POINTER <= 2 +# define ARDUINOJSON_AUTO_SHRINK 0 +# else +# define ARDUINOJSON_AUTO_SHRINK 1 +# endif +#endif +#ifndef ARDUINOJSON_STRING_LENGTH_SIZE +# if ARDUINOJSON_SIZEOF_POINTER <= 2 +# define ARDUINOJSON_STRING_LENGTH_SIZE 1 // up to 255 characters # else -# define ARDUINOJSON_SLOT_OFFSET_SIZE 2 +# define ARDUINOJSON_STRING_LENGTH_SIZE 2 // up to 65535 characters # endif #endif #ifdef ARDUINO @@ -147,9 +179,6 @@ #ifndef ARDUINOJSON_TAB # define ARDUINOJSON_TAB " " #endif -#ifndef ARDUINOJSON_ENABLE_STRING_DEDUPLICATION -# define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 1 -#endif #ifndef ARDUINOJSON_STRING_BUFFER_SIZE # define ARDUINOJSON_STRING_BUFFER_SIZE 32 #endif @@ -177,8 +206,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 @@ -197,22 +230,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 "6.21.3" -#define ARDUINOJSON_VERSION_MAJOR 6 -#define ARDUINOJSON_VERSION_MINOR 21 -#define ARDUINOJSON_VERSION_REVISION 3 -#define ARDUINOJSON_VERSION_MACRO V6213 +#define ARDUINOJSON_VERSION "7.1.0" +#define ARDUINOJSON_VERSION_MAJOR 7 +#define ARDUINOJSON_VERSION_MINOR 1 +#define ARDUINOJSON_VERSION_REVISION 0 +#define ARDUINOJSON_VERSION_MACRO V710 #ifndef ARDUINOJSON_VERSION_NAMESPACE -# define ARDUINOJSON_VERSION_NAMESPACE \ - ARDUINOJSON_CONCAT4( \ - ARDUINOJSON_VERSION_MACRO, \ - ARDUINOJSON_BIN2ALPHA( \ - ARDUINOJSON_ENABLE_PROGMEM, ARDUINOJSON_USE_LONG_LONG, \ - ARDUINOJSON_USE_DOUBLE, ARDUINOJSON_ENABLE_STRING_DEDUPLICATION), \ - ARDUINOJSON_BIN2ALPHA( \ - ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \ - ARDUINOJSON_ENABLE_COMMENTS, ARDUINOJSON_DECODE_UNICODE), \ - ARDUINOJSON_SLOT_OFFSET_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 { \ @@ -234,50 +267,42 @@ struct Converter; ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template -class InvalidConversion; // Error here? See https://arduinojson.org/v6/invalid-conversion/ -template -struct ConverterNeedsWriteableRef; +class InvalidConversion; // Error here? See https://arduinojson.org/v7/invalid-conversion/ ARDUINOJSON_END_PRIVATE_NAMESPACE #include #include -ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -#if ARDUINOJSON_ENABLE_ALIGNMENT -inline bool isAligned(size_t value) { - const size_t mask = sizeof(void*) - 1; - size_t addr = value; - return (addr & mask) == 0; -} -inline size_t addPadding(size_t bytes) { - const size_t mask = sizeof(void*) - 1; - return (bytes + mask) & ~mask; -} -template -struct AddPadding { - static const size_t mask = sizeof(void*) - 1; - static const size_t value = (bytes + mask) & ~mask; +#include +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class Allocator { + public: + virtual void* allocate(size_t size) = 0; + virtual void deallocate(void* ptr) = 0; + virtual void* reallocate(void* ptr, size_t new_size) = 0; + protected: + ~Allocator() = default; }; -#else -inline bool isAligned(size_t) { - return true; -} -inline size_t addPadding(size_t bytes) { - return bytes; -} -template -struct AddPadding { - static const size_t value = bytes; +namespace detail { +class DefaultAllocator : public Allocator { + public: + void* allocate(size_t size) override { + return malloc(size); + } + void deallocate(void* ptr) override { + free(ptr); + } + void* reallocate(void* ptr, size_t new_size) override { + return realloc(ptr, new_size); + } + static Allocator* instance() { + static DefaultAllocator allocator; + return &allocator; + } + private: + DefaultAllocator() = default; + ~DefaultAllocator() = default; }; -#endif -template -inline bool isAligned(T* ptr) { - return isAligned(reinterpret_cast(ptr)); -} -template -inline T* addPadding(T* p) { - size_t address = addPadding(reinterpret_cast(p)); - return reinterpret_cast(address); -} -ARDUINOJSON_END_PRIVATE_NAMESPACE +} // namespace detail +ARDUINOJSON_END_PUBLIC_NAMESPACE #if ARDUINOJSON_DEBUG #include # define ARDUINOJSON_ASSERT(X) assert(X) @@ -285,16 +310,22 @@ ARDUINOJSON_END_PRIVATE_NAMESPACE # define ARDUINOJSON_ASSERT(X) ((void)0) #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -template Y)> -struct Max {}; -template -struct Max { - static const size_t value = X; +template +struct uint_; +template <> +struct uint_<8> { + typedef uint8_t type; }; -template -struct Max { - static const size_t value = Y; +template <> +struct uint_<16> { + typedef uint16_t type; +}; +template <> +struct uint_<32> { + typedef uint32_t type; }; +template +using uint_t = typename uint_::type; template struct conditional { typedef TrueType type; @@ -303,12 +334,30 @@ template struct conditional { typedef FalseType type; }; +template +using conditional_t = + typename conditional::type; template struct enable_if {}; template struct enable_if { typedef T type; }; +template +using enable_if_t = typename enable_if::type; +template +struct function_traits; +template +struct function_traits { + using return_type = ReturnType; + using arg1_type = Arg1; +}; +template +struct function_traits { + using return_type = ReturnType; + using arg1_type = Arg1; + using arg2_type = Arg2; +}; template struct integral_constant { static const T value = v; @@ -329,6 +378,8 @@ template struct remove_reference { typedef T type; }; +template +using remove_reference_t = typename remove_reference::type; template class is_base_of { protected: // <- to avoid GCC's "all member functions in class are private" @@ -336,8 +387,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(); @@ -401,29 +452,30 @@ template struct remove_cv { typedef T type; }; +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; }; @@ -433,22 +485,22 @@ 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; @@ -477,10 +529,8 @@ 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; @@ -489,16 +539,139 @@ template struct remove_const { typedef T type; }; +template +using remove_const_t = typename remove_const::type; +template +struct make_void { + using type = void; +}; +template +using void_t = typename make_void::type; ARDUINOJSON_END_PRIVATE_NAMESPACE -#include +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4310) +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct numeric_limits; +template +struct numeric_limits::value>> { + static constexpr T lowest() { + return 0; + } + static constexpr T highest() { + return T(-1); + } +}; +template +struct numeric_limits< + T, enable_if_t::value && is_signed::value>> { + static constexpr T lowest() { + return T(T(1) << (sizeof(T) * 8 - 1)); + } + static constexpr T highest() { + return T(~lowest()); + } +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#ifdef _MSC_VER +# pragma warning(pop) +#endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -namespace StringStoragePolicy { -struct Link {}; -struct Copy {}; -struct LinkOrCopy { - bool link; +struct StringNode { + using references_type = uint_t; + using length_type = uint_t; + struct StringNode* next; + references_type references; + length_type length; + char data[1]; + static constexpr size_t maxLength = numeric_limits::highest(); + static constexpr size_t sizeForLength(size_t n) { + return n + 1 + offsetof(StringNode, data); + } + static StringNode* create(size_t length, Allocator* allocator) { + if (length > maxLength) + return nullptr; + auto size = sizeForLength(length); + if (size < length) // integer overflow + return nullptr; // (not testable on 64-bit) + auto node = reinterpret_cast(allocator->allocate(size)); + if (node) { + node->length = length_type(length); + node->references = 1; + } + return node; + } + static StringNode* resize(StringNode* node, size_t length, + Allocator* allocator) { + ARDUINOJSON_ASSERT(node != nullptr); + StringNode* newNode; + if (length <= maxLength) + newNode = reinterpret_cast( + allocator->reallocate(node, sizeForLength(length))); + else + newNode = nullptr; + if (newNode) + newNode->length = length_type(length); + else + allocator->deallocate(node); + return newNode; + } + static void destroy(StringNode* node, Allocator* allocator) { + allocator->deallocate(node); + } }; -} // namespace StringStoragePolicy +constexpr size_t sizeofString(size_t n) { + return StringNode::sizeForLength(n); +} +using nullptr_t = decltype(nullptr); +template +T&& forward(remove_reference_t& t) noexcept { + return static_cast(t); +} +template +remove_reference_t&& move(T&& t) { + return static_cast&&>(t); +} +template +void swap_(T& a, T& b) { + T tmp = move(a); + a = move(b); + b = move(tmp); +} +ARDUINOJSON_END_PRIVATE_NAMESPACE +#include +#ifdef _MSC_VER // Visual Studio +# define FORCE_INLINE // __forceinline causes C4714 when returning std::string +# ifndef ARDUINOJSON_DEPRECATED +# define ARDUINOJSON_DEPRECATED(msg) __declspec(deprecated(msg)) +# endif +#elif defined(__GNUC__) // GCC or Clang +# define FORCE_INLINE __attribute__((always_inline)) +# ifndef ARDUINOJSON_DEPRECATED +# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +# define ARDUINOJSON_DEPRECATED(msg) __attribute__((deprecated(msg))) +# else +# define ARDUINOJSON_DEPRECATED(msg) __attribute__((deprecated)) +# endif +# endif +#else // Other compilers +# define FORCE_INLINE +# ifndef ARDUINOJSON_DEPRECATED +# define ARDUINOJSON_DEPRECATED(msg) +# endif +#endif +#if defined(__has_attribute) +# if __has_attribute(no_sanitize) +# define ARDUINOJSON_NO_SANITIZE(check) __attribute__((no_sanitize(check))) +# else +# define ARDUINOJSON_NO_SANITIZE(check) +# endif +#else +# define ARDUINOJSON_NO_SANITIZE(check) +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template struct StringAdapter; template @@ -526,7 +699,7 @@ class ZeroTerminatedRamString { bool isNull() const { return !str_; } - size_t size() const { + FORCE_INLINE size_t size() const { return str_ ? ::strlen(str_) : 0; } char operator[](size_t i) const { @@ -547,21 +720,21 @@ class ZeroTerminatedRamString { ZeroTerminatedRamString b) { return stringCompare(a, b) == 0; } - StringStoragePolicy::Copy storagePolicy() const { - return StringStoragePolicy::Copy(); + bool isLinked() const { + return false; } protected: const char* str_; }; template -struct StringAdapter::value>::type> { +struct StringAdapter::value>> { typedef ZeroTerminatedRamString AdaptedString; static AdaptedString adapt(const TChar* p) { return AdaptedString(reinterpret_cast(p)); } }; template -struct StringAdapter::value>::type> { +struct StringAdapter::value>> { typedef ZeroTerminatedRamString AdaptedString; static AdaptedString adapt(const TChar* p) { return AdaptedString(reinterpret_cast(p)); @@ -570,8 +743,8 @@ struct StringAdapter::value>::type> { class StaticStringAdapter : public ZeroTerminatedRamString { public: StaticStringAdapter(const char* str) : ZeroTerminatedRamString(str) {} - StringStoragePolicy::Link storagePolicy() const { - return StringStoragePolicy::Link(); + bool isLinked() const { + return true; } }; template <> @@ -599,16 +772,15 @@ class SizedRamString { const char* data() const { return str_; } - StringStoragePolicy::Copy storagePolicy() const { - return StringStoragePolicy::Copy(); + bool isLinked() const { + return false; } protected: const char* str_; size_t size_; }; template -struct SizedStringAdapter::value>::type> { +struct SizedStringAdapter::value>> { typedef SizedRamString AdaptedString; static AdaptedString adapt(const TChar* p, size_t n) { return AdaptedString(reinterpret_cast(p), n); @@ -673,9 +845,8 @@ class JsonStringAdapter : public SizedRamString { public: JsonStringAdapter(const JsonString& s) : SizedRamString(s.c_str(), s.size()), linked_(s.isLinked()) {} - StringStoragePolicy::LinkOrCopy storagePolicy() const { - StringStoragePolicy::LinkOrCopy policy = {linked_}; - return policy; + bool isLinked() const { + return linked_; } private: bool linked_; @@ -691,30 +862,24 @@ 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 @@ -729,32 +894,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> { + enable_if_t<(string_traits::has_cstr || string_traits::has_data) && + (string_traits::has_length || string_traits::has_size)>> { typedef SizedRamString AdaptedString; static AdaptedString adapt(const T& s) { return AdaptedString(get_data(s), get_size(s)); } private: template - static 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(); } }; @@ -893,6 +1055,9 @@ class FlashString { ARDUINOJSON_ASSERT(i <= size_); return static_cast(pgm_read_byte(str_ + i)); } + const char* data() const { + return nullptr; + } size_t size() const { return size_; } @@ -922,8 +1087,8 @@ class FlashString { ARDUINOJSON_ASSERT(s.size() <= n); ::memcpy_P(p, s.str_, n); } - StringStoragePolicy::Copy storagePolicy() const { - return StringStoragePolicy::Copy(); + bool isLinked() const { + return false; } private: const char* str_; @@ -947,8 +1112,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()); @@ -966,14 +1130,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()); @@ -988,8 +1150,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); } @@ -1000,474 +1161,358 @@ static void stringGetChars(TAdaptedString s, char* p, size_t n) { p[i] = s[i]; } } -template -struct int_t; -template <> -struct int_t<8> { - typedef int8_t type; -}; -template <> -struct int_t<16> { - typedef int16_t type; -}; -template <> -struct int_t<32> { - typedef int32_t type; -}; -ARDUINOJSON_END_PRIVATE_NAMESPACE -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4310) -#endif -ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -template -struct numeric_limits; -template -struct numeric_limits::value>::type> { - static T lowest() { - return 0; +class VariantSlot; +class VariantPool; +class StringPool { + public: + StringPool() = default; + StringPool(const StringPool&) = delete; + void operator=(StringPool&& src) = delete; + ~StringPool() { + ARDUINOJSON_ASSERT(strings_ == nullptr); + } + friend void swap(StringPool& a, StringPool& b) { + swap_(a.strings_, b.strings_); + } + void clear(Allocator* allocator) { + while (strings_) { + auto node = strings_; + strings_ = node->next; + StringNode::destroy(node, allocator); + } } - static T highest() { - return T(-1); + size_t size() const { + size_t total = 0; + for (auto node = strings_; node; node = node->next) + total += sizeofString(node->length); + return total; } -}; -template -struct numeric_limits< - T, typename enable_if::value && is_signed::value>::type> { - static T lowest() { - return T(T(1) << (sizeof(T) * 8 - 1)); + template + StringNode* add(TAdaptedString str, Allocator* allocator) { + ARDUINOJSON_ASSERT(str.isNull() == false); + auto node = get(str); + if (node) { + node->references++; + return node; + } + size_t n = str.size(); + node = StringNode::create(n, allocator); + if (!node) + return nullptr; + stringGetChars(str, node->data, n); + node->data[n] = 0; // force NUL terminator + add(node); + return node; + } + void add(StringNode* node) { + ARDUINOJSON_ASSERT(node != nullptr); + node->next = strings_; + strings_ = node; } - static T highest() { - return T(~lowest()); + template + StringNode* get(const TAdaptedString& str) const { + for (auto node = strings_; node; node = node->next) { + if (stringEquals(str, adaptString(node->data, node->length))) + return node; + } + return nullptr; + } + void dereference(const char* s, Allocator* allocator) { + StringNode* prev = nullptr; + for (auto node = strings_; node; node = node->next) { + if (node->data == s) { + if (--node->references == 0) { + if (prev) + prev->next = node->next; + else + strings_ = node->next; + StringNode::destroy(node, allocator); + } + return; + } + prev = node; + } } + private: + StringNode* strings_ = nullptr; }; -ARDUINOJSON_END_PRIVATE_NAMESPACE -#ifdef _MSC_VER -# pragma warning(pop) -#endif -ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -class MemoryPool; -class VariantData; class VariantSlot; -class CollectionData { - VariantSlot* head_; - VariantSlot* tail_; +using SlotId = uint_t; +using SlotCount = SlotId; +const SlotId NULL_SLOT = SlotId(-1); +class SlotWithId { public: - VariantData* addElement(MemoryPool* pool); - VariantData* getElement(size_t index) const; - VariantData* getOrAddElement(size_t index, MemoryPool* pool); - void removeElement(size_t index); - template - VariantData* addMember(TAdaptedString key, MemoryPool* pool); - template - VariantData* getMember(TAdaptedString key) const; - template - VariantData* getOrAddMember(TAdaptedString key, MemoryPool* pool); - template - void removeMember(TAdaptedString key) { - removeSlot(getSlot(key)); + SlotWithId() : slot_(nullptr), id_(NULL_SLOT) {} + SlotWithId(VariantSlot* slot, SlotId id) : slot_(slot), id_(id) { + ARDUINOJSON_ASSERT((slot == nullptr) == (id == NULL_SLOT)); } - template - bool containsKey(const TAdaptedString& key) const; + 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(); - size_t memoryUsage() const; - size_t size() const; - VariantSlot* addSlot(MemoryPool*); - void removeSlot(VariantSlot* slot); - bool copyFrom(const CollectionData& src, MemoryPool* pool); - VariantSlot* head() const { - return head_; + void 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_; + } } - void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance); private: - VariantSlot* getSlot(size_t index) const; - template - VariantSlot* getSlot(TAdaptedString key) const; - VariantSlot* getPreviousSlot(VariantSlot*) const; + 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); }; -inline const VariantData* collectionToVariant( - const CollectionData* collection) { - const void* data = collection; // prevent warning cast-align - return reinterpret_cast(data); -} -inline VariantData* collectionToVariant(CollectionData* collection) { - void* data = collection; // prevent warning cast-align - return reinterpret_cast(data); -} -ARDUINOJSON_END_PRIVATE_NAMESPACE -ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE -#if ARDUINOJSON_USE_DOUBLE -typedef double JsonFloat; -#else -typedef float JsonFloat; -#endif -#if ARDUINOJSON_USE_LONG_LONG -typedef int64_t JsonInteger; -typedef uint64_t JsonUInt; -#else -typedef long JsonInteger; -typedef unsigned long JsonUInt; -#endif -ARDUINOJSON_END_PUBLIC_NAMESPACE -#define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T) \ - static_assert(sizeof(T) <= sizeof(ArduinoJson::JsonInteger), \ - "To use 64-bit integers with ArduinoJson, you must set " \ - "ARDUINOJSON_USE_LONG_LONG to 1. See " \ - "https://arduinojson.org/v6/api/config/use_long_long/"); -ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -enum { - VALUE_MASK = 0x7F, - OWNED_VALUE_BIT = 0x01, - VALUE_IS_NULL = 0, - VALUE_IS_LINKED_RAW = 0x02, - VALUE_IS_OWNED_RAW = 0x03, - VALUE_IS_LINKED_STRING = 0x04, - VALUE_IS_OWNED_STRING = 0x05, - VALUE_IS_BOOLEAN = 0x06, - NUMBER_BIT = 0x08, - VALUE_IS_UNSIGNED_INTEGER = 0x08, - VALUE_IS_SIGNED_INTEGER = 0x0A, - VALUE_IS_FLOAT = 0x0C, - COLLECTION_MASK = 0x60, - VALUE_IS_OBJECT = 0x20, - VALUE_IS_ARRAY = 0x40, - OWNED_KEY_BIT = 0x80 -}; -struct RawData { - const char* data; - size_t size; -}; -union VariantContent { - JsonFloat asFloat; - bool asBoolean; - JsonUInt asUnsignedInteger; - JsonInteger asSignedInteger; - CollectionData asCollection; - struct { - const char* data; - size_t size; - } asString; -}; -typedef int_t::type VariantSlotDiff; -class VariantSlot { - VariantContent content_; - uint8_t flags_; - VariantSlotDiff next_; - const char* key_; - public: - VariantData* data() { - return reinterpret_cast(&content_); - } - const VariantData* data() const { - return reinterpret_cast(&content_); - } - VariantSlot* next() { - return next_ ? this + next_ : 0; - } - const VariantSlot* next() const { - return const_cast(this)->next(); - } - VariantSlot* next(size_t distance) { - VariantSlot* slot = this; - while (distance--) { - if (!slot->next_) - return 0; - slot += slot->next_; - } - return slot; - } - const VariantSlot* next(size_t distance) const { - return const_cast(this)->next(distance); - } - void setNext(VariantSlot* slot) { - ARDUINOJSON_ASSERT(!slot || slot - this >= - numeric_limits::lowest()); - ARDUINOJSON_ASSERT(!slot || slot - this <= - numeric_limits::highest()); - next_ = VariantSlotDiff(slot ? slot - this : 0); - } - void setNextNotNull(VariantSlot* slot) { - ARDUINOJSON_ASSERT(slot != 0); - ARDUINOJSON_ASSERT(slot - this >= - numeric_limits::lowest()); - ARDUINOJSON_ASSERT(slot - this <= - numeric_limits::highest()); - next_ = VariantSlotDiff(slot - this); - } - void setKey(JsonString k) { - ARDUINOJSON_ASSERT(k); - if (k.isLinked()) - flags_ &= VALUE_MASK; - else - flags_ |= OWNED_KEY_BIT; - key_ = k.c_str(); - } - const char* key() const { - return key_; - } - bool ownsKey() const { - return (flags_ & OWNED_KEY_BIT) != 0; - } - void clear() { - next_ = 0; - flags_ = 0; - key_ = 0; - } - void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) { - if (flags_ & OWNED_KEY_BIT) - key_ += stringDistance; - if (flags_ & OWNED_VALUE_BIT) - content_.asString.data += stringDistance; - if (flags_ & COLLECTION_MASK) - content_.asCollection.movePointers(stringDistance, variantDistance); - } -}; -ARDUINOJSON_END_PRIVATE_NAMESPACE -#define JSON_STRING_SIZE(SIZE) (SIZE + 1) -#define JSON_ARRAY_SIZE(NUMBER_OF_ELEMENTS) \ - ((NUMBER_OF_ELEMENTS) * sizeof(ArduinoJson::detail::VariantSlot)) -#define JSON_OBJECT_SIZE(NUMBER_OF_ELEMENTS) \ - ((NUMBER_OF_ELEMENTS) * sizeof(ArduinoJson::detail::VariantSlot)) -ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -class MemoryPool { +class VariantSlot; +class VariantPool; +class ResourceManager { public: - MemoryPool(char* buf, size_t capa) - : begin_(buf), - left_(buf), - right_(buf ? buf + capa : 0), - end_(buf ? buf + capa : 0), - overflowed_(false) { - ARDUINOJSON_ASSERT(isAligned(begin_)); - ARDUINOJSON_ASSERT(isAligned(right_)); - ARDUINOJSON_ASSERT(isAligned(end_)); - } - void* buffer() { - return begin_; // NOLINT(clang-analyzer-unix.Malloc) - } - size_t capacity() const { - return size_t(end_ - begin_); + 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 size_t(left_ - begin_ + end_ - right_); + return VariantPool::slotsToBytes(variantPools_.usage()) + + stringPool_.size(); } bool overflowed() const { return overflowed_; } - VariantSlot* allocVariant() { - return allocRight(); + SlotWithId allocSlot() { + auto p = variantPools_.allocSlot(allocator_); + if (!p) + overflowed_ = true; + return p; + } + void freeSlot(SlotWithId slot); + VariantSlot* getSlot(SlotId id) const { + return variantPools_.getSlot(id); } template - const char* saveString(TAdaptedString str) { + StringNode* saveString(TAdaptedString str) { if (str.isNull()) return 0; -#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION - const char* existingCopy = findString(str); - if (existingCopy) - return existingCopy; -#endif - size_t n = str.size(); - char* newCopy = allocString(n + 1); - if (newCopy) { - stringGetChars(str, newCopy, n); - newCopy[n] = 0; // force null-terminator - } - return newCopy; + auto node = stringPool_.add(str, allocator_); + if (!node) + overflowed_ = true; + return node; } - void getFreeZone(char** zoneStart, size_t* zoneSize) const { - *zoneStart = left_; - *zoneSize = size_t(right_ - left_); + void saveString(StringNode* node) { + stringPool_.add(node); } - const char* saveStringFromFreeZone(size_t len) { -#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION - const char* dup = findString(adaptString(left_, len)); - if (dup) - return dup; -#endif - const char* str = left_; - left_ += len; - *left_++ = 0; - checkInvariants(); - return str; + template + StringNode* getString(const TAdaptedString& str) const { + return stringPool_.get(str); } - void markAsOverflowed() { - overflowed_ = true; + StringNode* createString(size_t length) { + auto node = StringNode::create(length, allocator_); + if (!node) + overflowed_ = true; + return node; } - void clear() { - left_ = begin_; - right_ = end_; - overflowed_ = false; + StringNode* resizeString(StringNode* node, size_t length) { + node = StringNode::resize(node, length, allocator_); + if (!node) + overflowed_ = true; + return node; } - bool canAlloc(size_t bytes) const { - return left_ + bytes <= right_; + void destroyString(StringNode* node) { + StringNode::destroy(node, allocator_); } - bool owns(void* p) const { - return begin_ <= p && p < end_; + void dereferenceString(const char* s) { + stringPool_.dereference(s, allocator_); } - void* operator new(size_t, void* p) { - return p; + void clear() { + variantPools_.clear(allocator_); + overflowed_ = false; + stringPool_.clear(allocator_); } - ptrdiff_t squash() { - char* new_right = addPadding(left_); - if (new_right >= right_) - return 0; - size_t right_size = static_cast(end_ - right_); - memmove(new_right, right_, right_size); - ptrdiff_t bytes_reclaimed = right_ - new_right; - right_ = new_right; - end_ = new_right + right_size; - return bytes_reclaimed; - } - void movePointers(ptrdiff_t offset) { - begin_ += offset; - left_ += offset; - right_ += offset; - end_ += offset; + void shrinkToFit() { + variantPools_.shrinkToFit(allocator_); } private: - void checkInvariants() { - ARDUINOJSON_ASSERT(begin_ <= left_); - ARDUINOJSON_ASSERT(left_ <= right_); - ARDUINOJSON_ASSERT(right_ <= end_); - ARDUINOJSON_ASSERT(isAligned(right_)); - } -#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION - template - const char* findString(const TAdaptedString& str) const { - size_t n = str.size(); - for (char* next = begin_; next + n < left_; ++next) { - if (next[n] == '\0' && stringEquals(str, adaptString(next, n))) - return next; - while (*next) - ++next; - } - return 0; - } -#endif - char* allocString(size_t n) { - if (!canAlloc(n)) { - overflowed_ = true; - return 0; - } - char* s = left_; - left_ += n; - checkInvariants(); - return s; - } - template - T* allocRight() { - return reinterpret_cast(allocRight(sizeof(T))); - } - void* allocRight(size_t bytes) { - if (!canAlloc(bytes)) { - overflowed_ = true; - return 0; - } - right_ -= bytes; - return right_; - } - char *begin_, *left_, *right_, *end_; + Allocator* allocator_; bool overflowed_; + StringPool stringPool_; + VariantPoolList variantPools_; }; -template -bool storeString(MemoryPool* pool, TAdaptedString str, - StringStoragePolicy::Copy, TCallback callback) { - const char* copy = pool->saveString(str); - JsonString storedString(copy, str.size(), JsonString::Copied); - callback(storedString); - return copy != 0; -} -template -bool storeString(MemoryPool*, TAdaptedString str, StringStoragePolicy::Link, - TCallback callback) { - JsonString storedString(str.data(), str.size(), JsonString::Linked); - callback(storedString); - return !str.isNull(); -} -template -bool storeString(MemoryPool* pool, TAdaptedString str, - StringStoragePolicy::LinkOrCopy policy, TCallback callback) { - if (policy.link) - return storeString(pool, str, StringStoragePolicy::Link(), callback); - else - return storeString(pool, str, StringStoragePolicy::Copy(), callback); -} -template -bool storeString(MemoryPool* pool, TAdaptedString str, TCallback callback) { - return storeString(pool, str, str.storagePolicy(), callback); -} template struct IsString : false_type {}; template -struct IsString< - T, typename make_void::AdaptedString>::type> +struct IsString::AdaptedString>> : true_type {}; ARDUINOJSON_END_PRIVATE_NAMESPACE -#ifdef _MSC_VER // Visual Studio -# define FORCE_INLINE // __forceinline causes C4714 when returning std::string -# define NO_INLINE __declspec(noinline) -#elif defined(__GNUC__) // GCC or Clang -# define FORCE_INLINE __attribute__((always_inline)) -# define NO_INLINE __attribute__((noinline)) -#else // Other compilers -# define FORCE_INLINE -# define NO_INLINE -#endif -#if defined(__has_attribute) -# if __has_attribute(no_sanitize) -# define ARDUINOJSON_NO_SANITIZE(check) __attribute__((no_sanitize(check))) -# else -# define ARDUINOJSON_NO_SANITIZE(check) -# endif -#else -# define ARDUINOJSON_NO_SANITIZE(check) -#endif -ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE -class JsonArray; -class JsonObject; -class JsonVariant; -ARDUINOJSON_END_PUBLIC_NAMESPACE -ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -template -struct VariantTo {}; -template <> -struct VariantTo { - typedef JsonArray type; -}; -template <> -struct VariantTo { - typedef JsonObject type; -}; -template <> -struct VariantTo { - typedef JsonVariant type; -}; -class VariantAttorney { - template - struct ResultOfGetData { - protected: // <- to avoid GCC's "all member functions in class are private" - static int probe(const VariantData*); - static char probe(VariantData*); - static TClient& client; - public: - typedef typename conditional::type type; - }; - public: - template - FORCE_INLINE static MemoryPool* getPool(TClient& client) { - return client.getPool(); - } - template - FORCE_INLINE static typename ResultOfGetData::type getData( - TClient& client) { - return client.getData(); - } - template - FORCE_INLINE static VariantData* getOrCreateData(TClient& client) { - return client.getOrCreateData(); - } -}; -ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE template class SerializedValue { @@ -1502,6 +1547,7 @@ class SerializedValue { TChar* data_; size_t size_; }; +using RawString = SerializedValue; template inline SerializedValue serialized(T str) { return SerializedValue(str); @@ -1522,7 +1568,6 @@ ARDUINOJSON_END_PUBLIC_NAMESPACE # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wconversion" #endif -#include ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE #ifndef isnan template @@ -1644,16 +1689,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() { @@ -1709,30 +1754,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() { @@ -1753,75 +1798,83 @@ inline TFloat make_float(TFloat m, TExponent e) { } return m; } +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +#if ARDUINOJSON_USE_DOUBLE +typedef double JsonFloat; +#else +typedef float JsonFloat; +#endif +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template -typename enable_if::value && is_unsigned::value && - is_integral::value && sizeof(TOut) <= sizeof(TIn), - bool>::type +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(); @@ -1837,364 +1890,731 @@ ARDUINOJSON_END_PRIVATE_NAMESPACE # pragma GCC diagnostic pop #endif ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -class VariantData { - VariantContent content_; // must be first to allow cast from array to variant - uint8_t flags_; +class VariantData; +class VariantSlot; +class CollectionIterator { + friend class CollectionData; public: - VariantData() : flags_(VALUE_IS_NULL) {} - void operator=(const VariantData& src) { - content_ = src.content_; - flags_ = uint8_t((flags_ & OWNED_KEY_BIT) | (src.flags_ & ~OWNED_KEY_BIT)); - } - template - typename TVisitor::result_type accept(TVisitor& visitor) const { - switch (type()) { - case VALUE_IS_FLOAT: - return visitor.visitFloat(content_.asFloat); - case VALUE_IS_ARRAY: - return visitor.visitArray(content_.asCollection); - case VALUE_IS_OBJECT: - return visitor.visitObject(content_.asCollection); - case VALUE_IS_LINKED_STRING: - case VALUE_IS_OWNED_STRING: - return visitor.visitString(content_.asString.data, - content_.asString.size); - case VALUE_IS_OWNED_RAW: - case VALUE_IS_LINKED_RAW: - return visitor.visitRawJson(content_.asString.data, - content_.asString.size); - case VALUE_IS_SIGNED_INTEGER: - return visitor.visitSignedInteger(content_.asSignedInteger); - case VALUE_IS_UNSIGNED_INTEGER: - return visitor.visitUnsignedInteger(content_.asUnsignedInteger); - case VALUE_IS_BOOLEAN: - return visitor.visitBoolean(content_.asBoolean != 0); - default: - return visitor.visitNull(); - } - } - template - T asIntegral() const; - template - T asFloat() const; - JsonString asString() const; - bool asBoolean() const; - CollectionData* asArray() { - return isArray() ? &content_.asCollection : 0; - } - const CollectionData* asArray() const { - return const_cast(this)->asArray(); - } - const CollectionData* asCollection() const { - return isCollection() ? &content_.asCollection : 0; - } - CollectionData* asObject() { - return isObject() ? &content_.asCollection : 0; - } - const CollectionData* asObject() const { - return const_cast(this)->asObject(); + CollectionIterator() : slot_(nullptr), currentId_(NULL_SLOT) {} + void next(const ResourceManager* resources); + bool done() const { + return slot_ == nullptr; } - bool copyFrom(const VariantData& src, MemoryPool* pool); - bool isArray() const { - return (flags_ & VALUE_IS_ARRAY) != 0; + bool operator==(const CollectionIterator& other) const { + return slot_ == other.slot_; } - bool isBoolean() const { - return type() == VALUE_IS_BOOLEAN; + bool operator!=(const CollectionIterator& other) const { + return slot_ != other.slot_; } - bool isCollection() const { - return (flags_ & COLLECTION_MASK) != 0; + VariantData* operator->() { + ARDUINOJSON_ASSERT(slot_ != nullptr); + return data(); } - template - bool isInteger() const { - switch (type()) { - case VALUE_IS_UNSIGNED_INTEGER: - return canConvertNumber(content_.asUnsignedInteger); - case VALUE_IS_SIGNED_INTEGER: - return canConvertNumber(content_.asSignedInteger); - default: - return false; - } + VariantData& operator*() { + ARDUINOJSON_ASSERT(slot_ != nullptr); + return *data(); } - bool isFloat() const { - return (flags_ & NUMBER_BIT) != 0; + const VariantData& operator*() const { + ARDUINOJSON_ASSERT(slot_ != nullptr); + return *data(); } - bool isString() const { - return type() == VALUE_IS_LINKED_STRING || type() == VALUE_IS_OWNED_STRING; + const char* key() const; + bool ownsKey() const; + void setKey(StringNode*); + void setKey(const char*); + VariantData* data() { + return reinterpret_cast(slot_); } - bool isObject() const { - return (flags_ & VALUE_IS_OBJECT) != 0; + const VariantData* data() const { + return reinterpret_cast(slot_); } - bool isNull() const { - return type() == VALUE_IS_NULL; + private: + CollectionIterator(VariantSlot* slot, SlotId slotId); + VariantSlot* slot_; + SlotId currentId_, nextId_; +}; +class CollectionData { + SlotId head_ = NULL_SLOT; + SlotId tail_ = NULL_SLOT; + public: + static void* operator new(size_t, void* p) noexcept { + return p; } - bool isEnclosed() const { - return !isFloat(); + static void operator delete(void*, void*) noexcept {} + using iterator = CollectionIterator; + iterator createIterator(const ResourceManager* resources) const { + return iterator(resources->getSlot(head_), head_); } - void remove(size_t index) { - if (isArray()) - content_.asCollection.removeElement(index); + size_t size(const ResourceManager*) const; + size_t nesting(const ResourceManager*) const; + void clear(ResourceManager* resources); + static void clear(CollectionData* collection, ResourceManager* resources) { + if (!collection) + return; + collection->clear(resources); } - template - void remove(TAdaptedString key) { - if (isObject()) - content_.asCollection.removeMember(key); + void remove(iterator it, ResourceManager* resources); + static void remove(CollectionData* collection, iterator it, + ResourceManager* resources) { + if (collection) + return collection->remove(it, resources); } - void setBoolean(bool value) { - setType(VALUE_IS_BOOLEAN); - content_.asBoolean = value; + SlotId head() const { + return head_; } - void setFloat(JsonFloat value) { - setType(VALUE_IS_FLOAT); - content_.asFloat = value; + void addSlot(SlotWithId slot, ResourceManager* resources); + protected: + iterator addSlot(ResourceManager*); + private: + SlotWithId getPreviousSlot(VariantSlot*, const ResourceManager*) const; +}; +inline const VariantData* collectionToVariant( + const CollectionData* collection) { + const void* data = collection; // prevent warning cast-align + return reinterpret_cast(data); +} +inline VariantData* collectionToVariant(CollectionData* collection) { + void* data = collection; // prevent warning cast-align + return reinterpret_cast(data); +} +class ArrayData : public CollectionData { + public: + VariantData* addElement(ResourceManager* resources) { + return addSlot(resources).data(); } - void setLinkedRaw(SerializedValue value) { - if (value.data()) { - setType(VALUE_IS_LINKED_RAW); - content_.asString.data = value.data(); - content_.asString.size = value.size(); - } else { - setType(VALUE_IS_NULL); - } + static VariantData* addElement(ArrayData* array, ResourceManager* resources) { + if (!array) + return nullptr; + return array->addElement(resources); } template - bool storeOwnedRaw(SerializedValue value, MemoryPool* pool) { - const char* dup = pool->saveString(adaptString(value.data(), value.size())); - if (dup) { - setType(VALUE_IS_OWNED_RAW); - content_.asString.data = dup; - content_.asString.size = value.size(); - return true; - } else { - setType(VALUE_IS_NULL); + bool addValue(T&& value, ResourceManager* resources); + template + static bool addValue(ArrayData* array, T&& value, + ResourceManager* resources) { + if (!array) return false; + return array->addValue(value, resources); + } + VariantData* getOrAddElement(size_t index, ResourceManager* resources); + VariantData* getElement(size_t index, const ResourceManager* resources) const; + static VariantData* getElement(const ArrayData* array, size_t index, + const ResourceManager* resources) { + if (!array) + return nullptr; + return array->getElement(index, resources); + } + void removeElement(size_t index, ResourceManager* resources); + static void removeElement(ArrayData* array, size_t index, + ResourceManager* resources) { + if (!array) + return; + array->removeElement(index, resources); + } + bool copyFrom(const ArrayData& src, ResourceManager* resources); + static bool copy(ArrayData* dst, const ArrayData* src, + ResourceManager* resources) { + if (!dst || !src) + return false; + return dst->copyFrom(*src, resources); + } + private: + iterator at(size_t index, const ResourceManager* resources) const; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +#if ARDUINOJSON_USE_LONG_LONG +typedef int64_t JsonInteger; +typedef uint64_t JsonUInt; +#else +typedef long JsonInteger; +typedef unsigned long JsonUInt; +#endif +ARDUINOJSON_END_PUBLIC_NAMESPACE +#define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T) \ + static_assert(sizeof(T) <= sizeof(ArduinoJson::JsonInteger), \ + "To use 64-bit integers with ArduinoJson, you must set " \ + "ARDUINOJSON_USE_LONG_LONG to 1. See " \ + "https://arduinojson.org/v7/api/config/use_long_long/"); +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +class ObjectData : public CollectionData { + public: + VariantData* addMember(StringNode* key, ResourceManager* resources) { + ARDUINOJSON_ASSERT(key != nullptr); + auto it = addSlot(resources); + if (it.done()) + return nullptr; + it.setKey(key); + return it.data(); + } + template + VariantData* addMember(TAdaptedString key, ResourceManager* resources) { + ARDUINOJSON_ASSERT(!key.isNull()); + if (key.isLinked()) { + auto it = addSlot(resources); + if (!it.done()) + it.setKey(key.data()); + return it.data(); + } else { + auto storedKey = resources->saveString(key); + if (!storedKey) + return nullptr; + auto it = addSlot(resources); + if (!it.done()) + it.setKey(storedKey); + return it.data(); + } + } + template + VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources); + template + VariantData* getMember(TAdaptedString key, + const ResourceManager* resources) const; + template + static VariantData* getMember(const ObjectData* object, TAdaptedString key, + const ResourceManager* resources) { + if (!object) + return nullptr; + return object->getMember(key, resources); + } + template + void removeMember(TAdaptedString key, ResourceManager* resources); + template + static void removeMember(ObjectData* obj, TAdaptedString key, + ResourceManager* resources) { + if (!obj) + return; + obj->removeMember(key, resources); + } + private: + template + iterator findKey(TAdaptedString key, const ResourceManager* resources) const; +}; +enum { + VALUE_MASK = 0x7F, + OWNED_VALUE_BIT = 0x01, + VALUE_IS_NULL = 0, + VALUE_IS_RAW_STRING = 0x03, + VALUE_IS_LINKED_STRING = 0x04, + VALUE_IS_OWNED_STRING = 0x05, + VALUE_IS_BOOLEAN = 0x06, + NUMBER_BIT = 0x08, + VALUE_IS_UNSIGNED_INTEGER = 0x08, + VALUE_IS_SIGNED_INTEGER = 0x0A, + VALUE_IS_FLOAT = 0x0C, + COLLECTION_MASK = 0x60, + VALUE_IS_OBJECT = 0x20, + VALUE_IS_ARRAY = 0x40, + OWNED_KEY_BIT = 0x80 +}; +union VariantContent { + VariantContent() {} + JsonFloat asFloat; + bool asBoolean; + JsonUInt asUnsignedInteger; + JsonInteger asSignedInteger; + ArrayData asArray; + ObjectData asObject; + CollectionData asCollection; + const char* asLinkedString; + struct StringNode* asOwnedString; +}; +struct StringNode; +class VariantSlot { + VariantContent content_; + uint8_t flags_; + SlotId next_; + const char* key_; + public: + static void* operator new(size_t, void* p) noexcept { + return p; + } + static void operator delete(void*, void*) noexcept {} + VariantSlot() : flags_(0), next_(NULL_SLOT), key_(0) {} + VariantData* data() { + return reinterpret_cast(&content_); + } + const VariantData* data() const { + return reinterpret_cast(&content_); + } + SlotId next() const { + return next_; + } + void setNext(SlotId slot) { + next_ = slot; + } + void setKey(const char* k) { + ARDUINOJSON_ASSERT(k); + flags_ &= VALUE_MASK; + key_ = k; + } + void setKey(StringNode* k) { + ARDUINOJSON_ASSERT(k); + flags_ |= OWNED_KEY_BIT; + key_ = k->data; + } + const char* key() const { + return key_; + } + bool ownsKey() const { + return (flags_ & OWNED_KEY_BIT) != 0; + } +}; +inline VariantData* slotData(VariantSlot* slot) { + return reinterpret_cast(slot); +} +constexpr size_t sizeofArray(size_t n) { + return n * sizeof(VariantSlot); +} +constexpr size_t sizeofObject(size_t n) { + return n * sizeof(VariantSlot); +} +template +T parseNumber(const char* s); +class VariantData { + VariantContent content_; // must be first to allow cast from array to variant + uint8_t flags_; + public: + VariantData() : flags_(VALUE_IS_NULL) {} + template + typename TVisitor::result_type accept(TVisitor& visit) const { + switch (type()) { + case VALUE_IS_FLOAT: + return visit.visit(content_.asFloat); + case VALUE_IS_ARRAY: + return visit.visit(content_.asArray); + case VALUE_IS_OBJECT: + return visit.visit(content_.asObject); + case VALUE_IS_LINKED_STRING: + return visit.visit(JsonString(content_.asLinkedString)); + case VALUE_IS_OWNED_STRING: + return visit.visit(JsonString(content_.asOwnedString->data, + content_.asOwnedString->length, + JsonString::Copied)); + case VALUE_IS_RAW_STRING: + return visit.visit(RawString(content_.asOwnedString->data, + content_.asOwnedString->length)); + case VALUE_IS_SIGNED_INTEGER: + return visit.visit(content_.asSignedInteger); + case VALUE_IS_UNSIGNED_INTEGER: + return visit.visit(content_.asUnsignedInteger); + case VALUE_IS_BOOLEAN: + return visit.visit(content_.asBoolean != 0); + default: + return visit.visit(nullptr); } } + template + static typename TVisitor::result_type accept(const VariantData* var, + TVisitor& visit) { + if (var != 0) + return var->accept(visit); + else + return visit.visit(nullptr); + } + VariantData* addElement(ResourceManager* resources) { + auto array = isNull() ? &toArray() : asArray(); + return detail::ArrayData::addElement(array, resources); + } + static VariantData* addElement(VariantData* var, ResourceManager* resources) { + if (!var) + return nullptr; + return var->addElement(resources); + } template - typename enable_if::value>::type setInteger(T value) { - setType(VALUE_IS_UNSIGNED_INTEGER); - content_.asUnsignedInteger = static_cast(value); + bool addValue(T&& value, ResourceManager* resources) { + auto array = isNull() ? &toArray() : asArray(); + return detail::ArrayData::addValue(array, detail::forward(value), + resources); } template - typename enable_if::value>::type setInteger(T value) { - setType(VALUE_IS_SIGNED_INTEGER); - content_.asSignedInteger = value; + static bool addValue(VariantData* var, T&& value, + ResourceManager* resources) { + if (!var) + return false; + return var->addValue(value, resources); } - void setNull() { - setType(VALUE_IS_NULL); + bool asBoolean() const { + switch (type()) { + case VALUE_IS_BOOLEAN: + return content_.asBoolean; + case VALUE_IS_SIGNED_INTEGER: + case VALUE_IS_UNSIGNED_INTEGER: + return content_.asUnsignedInteger != 0; + case VALUE_IS_FLOAT: + return content_.asFloat != 0; + case VALUE_IS_NULL: + return false; + default: + return true; + } } - void setString(JsonString s) { - ARDUINOJSON_ASSERT(s); - if (s.isLinked()) - setType(VALUE_IS_LINKED_STRING); - else - setType(VALUE_IS_OWNED_STRING); - content_.asString.data = s.c_str(); - content_.asString.size = s.size(); + ArrayData* asArray() { + return isArray() ? &content_.asArray : 0; } - CollectionData& toArray() { - setType(VALUE_IS_ARRAY); - content_.asCollection.clear(); - return content_.asCollection; + const ArrayData* asArray() const { + return const_cast(this)->asArray(); } - CollectionData& toObject() { - setType(VALUE_IS_OBJECT); - content_.asCollection.clear(); - return content_.asCollection; + CollectionData* asCollection() { + return isCollection() ? &content_.asCollection : 0; } - size_t memoryUsage() const { + const CollectionData* asCollection() const { + return const_cast(this)->asCollection(); + } + template + T asFloat() const { + static_assert(is_floating_point::value, "T must be a floating point"); switch (type()) { + case VALUE_IS_BOOLEAN: + return static_cast(content_.asBoolean); + case VALUE_IS_UNSIGNED_INTEGER: + return static_cast(content_.asUnsignedInteger); + case VALUE_IS_SIGNED_INTEGER: + return static_cast(content_.asSignedInteger); + case VALUE_IS_LINKED_STRING: case VALUE_IS_OWNED_STRING: - case VALUE_IS_OWNED_RAW: - return content_.asString.size + 1; - case VALUE_IS_OBJECT: - case VALUE_IS_ARRAY: - return content_.asCollection.memoryUsage(); + return parseNumber(content_.asOwnedString->data); + case VALUE_IS_FLOAT: + return static_cast(content_.asFloat); default: return 0; } } - size_t size() const { - return isCollection() ? content_.asCollection.size() : 0; + template + T asIntegral() const { + static_assert(is_integral::value, "T must be an integral type"); + switch (type()) { + case VALUE_IS_BOOLEAN: + return content_.asBoolean; + case VALUE_IS_UNSIGNED_INTEGER: + return convertNumber(content_.asUnsignedInteger); + case VALUE_IS_SIGNED_INTEGER: + return convertNumber(content_.asSignedInteger); + case VALUE_IS_LINKED_STRING: + return parseNumber(content_.asLinkedString); + case VALUE_IS_OWNED_STRING: + return parseNumber(content_.asOwnedString->data); + case VALUE_IS_FLOAT: + return convertNumber(content_.asFloat); + default: + return 0; + } } - VariantData* addElement(MemoryPool* pool) { - if (isNull()) - toArray(); - if (!isArray()) - return 0; - return content_.asCollection.addElement(pool); + ObjectData* asObject() { + return isObject() ? &content_.asObject : 0; } - VariantData* getElement(size_t index) const { - const CollectionData* col = asArray(); - return col ? col->getElement(index) : 0; + const ObjectData* asObject() const { + return const_cast(this)->asObject(); } - VariantData* getOrAddElement(size_t index, MemoryPool* pool) { - if (isNull()) - toArray(); - if (!isArray()) - return 0; - return content_.asCollection.getOrAddElement(index, pool); + JsonString asRawString() const { + switch (type()) { + case VALUE_IS_RAW_STRING: + return JsonString(content_.asOwnedString->data, + content_.asOwnedString->length, JsonString::Copied); + default: + return JsonString(); + } + } + JsonString asString() const { + switch (type()) { + case VALUE_IS_LINKED_STRING: + return JsonString(content_.asLinkedString, JsonString::Linked); + case VALUE_IS_OWNED_STRING: + return JsonString(content_.asOwnedString->data, + content_.asOwnedString->length, JsonString::Copied); + default: + return JsonString(); + } + } + VariantData* getElement(size_t index, + const ResourceManager* resources) const { + return ArrayData::getElement(asArray(), index, resources); + } + static VariantData* getElement(const VariantData* var, size_t index, + const ResourceManager* resources) { + return var != 0 ? var->getElement(index, resources) : 0; } template - VariantData* getMember(TAdaptedString key) const { - const CollectionData* col = asObject(); - return col ? col->getMember(key) : 0; + VariantData* getMember(TAdaptedString key, + const ResourceManager* resources) const { + return ObjectData::getMember(asObject(), key, resources); } template - VariantData* getOrAddMember(TAdaptedString key, MemoryPool* pool) { - if (isNull()) - toObject(); - if (!isObject()) + static VariantData* getMember(const VariantData* var, TAdaptedString key, + const ResourceManager* resources) { + if (!var) return 0; - return content_.asCollection.getOrAddMember(key, pool); + return var->getMember(key, resources); } - void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) { - if (flags_ & OWNED_VALUE_BIT) - content_.asString.data += stringDistance; - if (flags_ & COLLECTION_MASK) - content_.asCollection.movePointers(stringDistance, variantDistance); + VariantData* getOrAddElement(size_t index, ResourceManager* resources) { + auto array = isNull() ? &toArray() : asArray(); + if (!array) + return nullptr; + return array->getOrAddElement(index, resources); } - uint8_t type() const { - return flags_ & VALUE_MASK; + template + VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources) { + if (key.isNull()) + return nullptr; + auto obj = isNull() ? &toObject() : asObject(); + if (!obj) + return nullptr; + return obj->getOrAddMember(key, resources); + } + bool isArray() const { + return (flags_ & VALUE_IS_ARRAY) != 0; + } + bool isBoolean() const { + return type() == VALUE_IS_BOOLEAN; + } + bool isCollection() const { + return (flags_ & COLLECTION_MASK) != 0; + } + bool isFloat() const { + return (flags_ & NUMBER_BIT) != 0; + } + template + bool isInteger() const { + switch (type()) { + case VALUE_IS_UNSIGNED_INTEGER: + return canConvertNumber(content_.asUnsignedInteger); + case VALUE_IS_SIGNED_INTEGER: + return canConvertNumber(content_.asSignedInteger); + default: + return false; + } + } + bool isNull() const { + return type() == VALUE_IS_NULL; + } + static bool isNull(const VariantData* var) { + if (!var) + return true; + return var->isNull(); + } + bool isObject() const { + return (flags_ & VALUE_IS_OBJECT) != 0; + } + bool isString() const { + return type() == VALUE_IS_LINKED_STRING || type() == VALUE_IS_OWNED_STRING; + } + size_t nesting(const ResourceManager* resources) const { + auto collection = asCollection(); + if (collection) + return collection->nesting(resources); + else + return 0; + } + static size_t nesting(const VariantData* var, + const ResourceManager* resources) { + if (!var) + return 0; + return var->nesting(resources); + } + void removeElement(size_t index, ResourceManager* resources) { + ArrayData::removeElement(asArray(), index, resources); + } + static void removeElement(VariantData* var, size_t index, + ResourceManager* resources) { + if (!var) + return; + var->removeElement(index, resources); + } + template + void removeMember(TAdaptedString key, ResourceManager* resources) { + ObjectData::removeMember(asObject(), key, resources); } template - inline bool setString(TAdaptedString value, MemoryPool* pool) { - if (value.isNull()) { + static void removeMember(VariantData* var, TAdaptedString key, + ResourceManager* resources) { + if (!var) + return; + var->removeMember(key, resources); + } + void reset() { + flags_ = VALUE_IS_NULL; + } + void setBoolean(bool value) { + setType(VALUE_IS_BOOLEAN); + content_.asBoolean = value; + } + void setBoolean(bool value, ResourceManager* resources) { + release(resources); + setBoolean(value); + } + void setFloat(JsonFloat value) { + setType(VALUE_IS_FLOAT); + content_.asFloat = value; + } + void setFloat(JsonFloat value, ResourceManager* resources) { + release(resources); + setFloat(value); + } + template + enable_if_t::value> setInteger(T value) { + setType(VALUE_IS_SIGNED_INTEGER); + content_.asSignedInteger = value; + } + template + enable_if_t::value> setInteger(T value) { + setType(VALUE_IS_UNSIGNED_INTEGER); + content_.asUnsignedInteger = static_cast(value); + } + template + void setInteger(T value, ResourceManager* resources) { + release(resources); + setInteger(value); + } + void setNull() { + setType(VALUE_IS_NULL); + } + void setNull(ResourceManager* resources) { + release(resources); + setNull(); + } + static void setNull(VariantData* var, ResourceManager* resources) { + if (!var) + return; + var->setNull(resources); + } + void setRawString(StringNode* s) { + ARDUINOJSON_ASSERT(s); + setType(VALUE_IS_RAW_STRING); + content_.asOwnedString = s; + } + template + void setRawString(SerializedValue value, ResourceManager* resources) { + release(resources); + auto dup = resources->saveString(adaptString(value.data(), value.size())); + if (dup) + setRawString(dup); + else setNull(); - return true; - } - return storeString(pool, value, VariantStringSetter(this)); } - private: - void setType(uint8_t t) { - flags_ &= OWNED_KEY_BIT; - flags_ |= t; + template + static void setRawString(VariantData* var, SerializedValue value, + ResourceManager* resources) { + if (!var) + return; + var->setRawString(value, resources); } - struct VariantStringSetter { - VariantStringSetter(VariantData* instance) : instance_(instance) {} - template - void operator()(TStoredString s) { - if (s) - instance_->setString(s); - else - instance_->setNull(); + template + void setString(TAdaptedString value, ResourceManager* resources) { + setNull(resources); + if (value.isNull()) + return; + if (value.isLinked()) { + setLinkedString(value.data()); + return; } - VariantData* instance_; - }; -}; -template -struct Visitor { - typedef TResult result_type; - TResult visitArray(const CollectionData&) { - return TResult(); + auto dup = resources->saveString(value); + if (dup) + setOwnedString(dup); } - TResult visitBoolean(bool) { - return TResult(); + template + static void setString(VariantData* var, TAdaptedString value, + ResourceManager* resources) { + if (!var) + return; + var->setString(value, resources); } - TResult visitFloat(JsonFloat) { - return TResult(); + void setLinkedString(const char* s) { + ARDUINOJSON_ASSERT(s); + setType(VALUE_IS_LINKED_STRING); + content_.asLinkedString = s; } - TResult visitSignedInteger(JsonInteger) { - return TResult(); + void setOwnedString(StringNode* s) { + ARDUINOJSON_ASSERT(s); + setType(VALUE_IS_OWNED_STRING); + content_.asOwnedString = s; } - TResult visitNull() { - return TResult(); + size_t size(const ResourceManager* resources) const { + return isCollection() ? content_.asCollection.size(resources) : 0; } - TResult visitObject(const CollectionData&) { - return TResult(); + static size_t size(const VariantData* var, const ResourceManager* resources) { + return var != 0 ? var->size(resources) : 0; + } + ArrayData& toArray() { + setType(VALUE_IS_ARRAY); + new (&content_.asArray) ArrayData(); + return content_.asArray; + } + ArrayData& toArray(ResourceManager* resources) { + release(resources); + return toArray(); + } + static ArrayData* toArray(VariantData* var, ResourceManager* resources) { + if (!var) + return 0; + return &var->toArray(resources); + } + ObjectData& toObject() { + setType(VALUE_IS_OBJECT); + new (&content_.asObject) ObjectData(); + return content_.asObject; + } + ObjectData& toObject(ResourceManager* resources) { + release(resources); + return toObject(); + } + static ObjectData* toObject(VariantData* var, ResourceManager* resources) { + if (!var) + return 0; + return &var->toObject(resources); } - TResult visitUnsignedInteger(JsonUInt) { - return TResult(); + uint8_t type() const { + return flags_ & VALUE_MASK; } - TResult visitRawJson(const char*, size_t) { - return TResult(); + private: + void release(ResourceManager* resources) { + if (flags_ & OWNED_VALUE_BIT) + resources->dereferenceString(content_.asOwnedString->data); + auto collection = asCollection(); + if (collection) + collection->clear(resources); } - TResult visitString(const char*, size_t) { - return TResult(); + void setType(uint8_t t) { + flags_ &= OWNED_KEY_BIT; + flags_ |= t; } }; -template -inline typename TVisitor::result_type variantAccept(const VariantData* var, - TVisitor& visitor) { - if (var != 0) - return var->accept(visitor); - else - return visitor.visitNull(); -} -inline bool variantCopyFrom(VariantData* dst, const VariantData* src, - MemoryPool* pool) { - if (!dst) - return false; - if (!src) { - dst->setNull(); - return true; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class JsonArray; +class JsonObject; +class JsonVariant; +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct VariantTo {}; +template <> +struct VariantTo { + typedef JsonArray type; +}; +template <> +struct VariantTo { + typedef JsonObject type; +}; +template <> +struct VariantTo { + typedef JsonVariant type; +}; +class VariantAttorney { + public: + template + static auto getResourceManager(TClient& client) + -> decltype(client.getResourceManager()) { + return client.getResourceManager(); } - return dst->copyFrom(*src, pool); -} -inline void variantSetNull(VariantData* var) { - if (!var) - return; - var->setNull(); -} -template -inline bool variantSetString(VariantData* var, TAdaptedString value, - MemoryPool* pool) { - return var != 0 ? var->setString(value, pool) : 0; -} -inline size_t variantSize(const VariantData* var) { - return var != 0 ? var->size() : 0; -} -inline CollectionData* variantToArray(VariantData* var) { - if (!var) - return 0; - return &var->toArray(); -} -inline CollectionData* variantToObject(VariantData* var) { - if (!var) - return 0; - return &var->toObject(); -} -inline VariantData* variantGetElement(const VariantData* var, size_t index) { - return var != 0 ? var->getElement(index) : 0; -} -inline NO_INLINE VariantData* variantAddElement(VariantData* var, - MemoryPool* pool) { - return var != 0 ? var->addElement(pool) : 0; -} -inline NO_INLINE VariantData* variantGetOrAddElement(VariantData* var, - size_t index, - MemoryPool* pool) { - return var != 0 ? var->getOrAddElement(index, pool) : 0; -} -template -VariantData* variantGetMember(const VariantData* var, TAdaptedString key) { - if (!var) - return 0; - return var->getMember(key); -} -template -VariantData* variantGetOrAddMember(VariantData* var, TAdaptedString key, - MemoryPool* pool) { - if (!var) - return 0; - return var->getOrAddMember(key, pool); -} -inline bool variantIsNull(const VariantData* var) { - return var == 0 || var->isNull(); -} -inline size_t variantNesting(const VariantData* var) { - if (!var) - return 0; - const CollectionData* collection = var->asCollection(); - if (!collection) - return 0; - size_t maxChildNesting = 0; - for (const VariantSlot* s = collection->head(); s; s = s->next()) { - size_t childNesting = variantNesting(s->data()); - if (childNesting > maxChildNesting) - maxChildNesting = childNesting; + template + static auto getData(TClient& client) -> decltype(client.getData()) { + return client.getData(); } - return maxChildNesting + 1; -} + template + static VariantData* getOrCreateData(TClient& client) { + return client.getOrCreateData(); + } +}; enum CompareResult { COMPARE_RESULT_DIFFER = 0, COMPARE_RESULT_EQUAL = 1, @@ -2215,31 +2635,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)); @@ -2247,9 +2667,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); @@ -2257,35 +2677,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); @@ -2305,9 +2721,8 @@ struct VariantOperatorTag {}; template struct VariantOperators : VariantOperatorTag { template - friend - typename enable_if::value && !is_array::value, T>::type - operator|(const TVariant& variant, const T& defaultValue) { + friend enable_if_t::value && !is_array::value, T> operator|( + const TVariant& variant, const T& defaultValue) { if (variant.template is()) return variant.template as(); else @@ -2321,8 +2736,8 @@ 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, T defaultValue) { if (variant) return variant; else @@ -2341,9 +2756,8 @@ struct VariantOperators : VariantOperatorTag { return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; } template - friend - typename enable_if::value, bool>::type - operator==(TVariant lhs, const T& rhs) { + friend enable_if_t::value, bool> + operator==(TVariant lhs, const T& rhs) { return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; } template @@ -2359,9 +2773,8 @@ struct VariantOperators : VariantOperatorTag { return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; } template - friend - typename enable_if::value, bool>::type - operator!=(TVariant lhs, const T& rhs) { + friend enable_if_t::value, bool> + operator!=(TVariant lhs, const T& rhs) { return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; } template @@ -2377,9 +2790,8 @@ struct VariantOperators : VariantOperatorTag { return compare(lhs, rhs) == COMPARE_RESULT_LESS; } template - friend - typename enable_if::value, bool>::type - operator<(TVariant lhs, const T& rhs) { + friend enable_if_t::value, bool> operator<( + TVariant lhs, const T& rhs) { return compare(lhs, rhs) == COMPARE_RESULT_LESS; } template @@ -2395,9 +2807,8 @@ struct VariantOperators : VariantOperatorTag { return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template - friend - typename enable_if::value, bool>::type - operator<=(TVariant lhs, const T& rhs) { + friend enable_if_t::value, bool> + operator<=(TVariant lhs, const T& rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; } template @@ -2413,9 +2824,8 @@ struct VariantOperators : VariantOperatorTag { return compare(lhs, rhs) == COMPARE_RESULT_GREATER; } template - friend - typename enable_if::value, bool>::type - operator>(TVariant lhs, const T& rhs) { + friend enable_if_t::value, bool> operator>( + TVariant lhs, const T& rhs) { return compare(lhs, rhs) == COMPARE_RESULT_GREATER; } template @@ -2431,9 +2841,8 @@ struct VariantOperators : VariantOperatorTag { return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } template - friend - typename enable_if::value, bool>::type - operator>=(TVariant lhs, const T& rhs) { + friend enable_if_t::value, bool> + operator>=(TVariant lhs, const T& rhs) { return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; } }; @@ -2444,76 +2853,108 @@ 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_(0) {} - explicit JsonVariantConst(const detail::VariantData* data) : data_(data) {} - FORCE_INLINE bool isNull() const { - using namespace detail; - return variantIsNull(data_); + JsonVariantConst() : data_(nullptr), resources_(nullptr) {} + explicit JsonVariantConst(const detail::VariantData* data, + const detail::ResourceManager* resources) + : data_(data), resources_(resources) {} + bool isNull() const { + return detail::VariantData::isNull(data_); } - FORCE_INLINE bool isUnbound() const { + bool isUnbound() const { return !data_; } - FORCE_INLINE size_t memoryUsage() const { - return data_ ? data_->memoryUsage() : 0; - } - FORCE_INLINE size_t nesting() const { - return variantNesting(data_); + size_t nesting() const { + return detail::VariantData::nesting(data_, resources_); } size_t size() const { - return variantSize(data_); + return detail::VariantData::size(data_, resources_); } - template - FORCE_INLINE typename detail::enable_if::value && - !detail::is_same::value, - T>::type - as() const { + template ::value, bool> = true> + T as() const { return Converter::fromJson(*this); } + template ::value, bool> = true> + detail::InvalidConversion as() const; template - FORCE_INLINE typename detail::enable_if::value && - !detail::is_same::value, - bool>::type - is() const { + detail::enable_if_t::value, bool> is() const { return Converter::checkJson(*this); } template - FORCE_INLINE operator T() const { + detail::enable_if_t::value, bool> is() const { + return false; + } + template + operator T() const { return as(); } - FORCE_INLINE JsonVariantConst operator[](size_t index) const { - return JsonVariantConst(variantGetElement(data_, index)); + template + detail::enable_if_t::value, JsonVariantConst> + operator[](T index) const { + return JsonVariantConst( + detail::VariantData::getElement(data_, size_t(index), resources_), + resources_); } template - FORCE_INLINE typename detail::enable_if::value, - JsonVariantConst>::type + detail::enable_if_t::value, JsonVariantConst> operator[](const TString& key) const { - return JsonVariantConst(variantGetMember(data_, detail::adaptString(key))); + return JsonVariantConst(detail::VariantData::getMember( + data_, detail::adaptString(key), resources_), + resources_); } template - FORCE_INLINE typename detail::enable_if::value, - JsonVariantConst>::type + detail::enable_if_t::value, JsonVariantConst> operator[](TChar* key) const { - return JsonVariantConst(variantGetMember(data_, detail::adaptString(key))); + return JsonVariantConst(detail::VariantData::getMember( + data_, detail::adaptString(key), resources_), + resources_); + } + template + detail::enable_if_t::value, JsonVariantConst> + operator[](const TVariant& key) const { + if (key.template is()) + return operator[](key.template as()); + else + return operator[](key.template as()); } template - FORCE_INLINE - typename detail::enable_if::value, bool>::type - containsKey(const TString& key) const { - return variantGetMember(getData(), detail::adaptString(key)) != 0; + detail::enable_if_t::value, bool> containsKey( + const TString& key) const { + return detail::VariantData::getMember(getData(), detail::adaptString(key), + resources_) != 0; } template - FORCE_INLINE - typename detail::enable_if::value, bool>::type - containsKey(TChar* key) const { - return variantGetMember(getData(), detail::adaptString(key)) != 0; + detail::enable_if_t::value, bool> containsKey( + TChar* key) const { + return detail::VariantData::getMember(getData(), detail::adaptString(key), + resources_) != 0; + } + template + detail::enable_if_t::value, bool> containsKey( + const TVariant& key) const { + return containsKey(key.template as()); + } + ARDUINOJSON_DEPRECATED("always returns zero") + size_t memoryUsage() const { + return 0; } protected: const detail::VariantData* getData() const { return data_; } + const detail::ResourceManager* getResourceManager() const { + return resources_; + } private: const detail::VariantData* data_; + const detail::ResourceManager* resources_; }; class JsonVariant; ARDUINOJSON_END_PUBLIC_NAMESPACE @@ -2526,137 +2967,130 @@ template class VariantRefBase : public VariantTag { friend class VariantAttorney; public: - FORCE_INLINE void clear() const { - variantSetNull(getData()); + void clear() const { + VariantData::setNull(getOrCreateData(), getResourceManager()); } - FORCE_INLINE bool isNull() const { - return variantIsNull(getData()); + bool isNull() const { + return VariantData::isNull(getData()); } - FORCE_INLINE bool isUnbound() const { + bool isUnbound() const { return !getData(); } template - FORCE_INLINE - typename enable_if::value, T>::type - as() const { - return Converter::fromJson(getVariantConst()); - } - template - FORCE_INLINE typename enable_if::value, T>::type - as() const { - return Converter::fromJson(getVariant()); - } - template - FORCE_INLINE operator T() const { + T as() const; + template ::value>> + operator T() const { return as(); } template - typename enable_if::value, JsonArray>::type to() const; - template - typename enable_if::value, JsonObject>::type to() - const; + enable_if_t::value, JsonArray> to() const; template - typename enable_if::value, JsonVariant>::type to() - const; + enable_if_t::value, JsonObject> to() const; template - FORCE_INLINE - typename enable_if::value, bool>::type - is() const { - return Converter::checkJson(getVariant()); - } + enable_if_t::value, JsonVariant> to() const; template - FORCE_INLINE typename enable_if::value && - !is_same::value && - !is_same::value, - bool>::type - is() const { - return Converter::checkJson(getVariantConst()); - } - FORCE_INLINE void shallowCopy(ArduinoJson::JsonVariantConst target) { - VariantData* data = getOrCreateData(); - if (!data) - return; - const VariantData* targetData = VariantAttorney::getData(target); - if (targetData) - *data = *targetData; - else - data->setNull(); - } + FORCE_INLINE bool is() const; template - FORCE_INLINE bool set(const T& value) const { - Converter::toJson(value, getOrCreateVariant()); - MemoryPool* pool = getPool(); - return pool && !pool->overflowed(); + bool set(const T& value) const { + return doSet>>(value); } template - FORCE_INLINE bool set(T* value) const { - Converter::toJson(value, getOrCreateVariant()); - MemoryPool* pool = getPool(); - return pool && !pool->overflowed(); + bool set(T* value) const { + return doSet>(value); } - FORCE_INLINE size_t size() const { - return variantSize(getData()); + size_t size() const { + return VariantData::size(getData(), getResourceManager()); } - FORCE_INLINE size_t memoryUsage() const { - VariantData* data = getData(); - return data ? data->memoryUsage() : 0; + size_t nesting() const { + return VariantData::nesting(getData(), getResourceManager()); } - FORCE_INLINE size_t nesting() const { - return variantNesting(getData()); + template + enable_if_t::value, T> add() const { + return add().template to(); } - FORCE_INLINE JsonVariant add() const; template - FORCE_INLINE bool add(const T& value) const { - return add().set(value); + enable_if_t::value, T> add() const; + template + bool add(const T& value) const { + return detail::VariantData::addValue(getOrCreateData(), value, + getResourceManager()); } template - FORCE_INLINE bool add(T* value) const { - return add().set(value); + bool add(T* value) const { + return detail::VariantData::addValue(getOrCreateData(), value, + getResourceManager()); } - FORCE_INLINE void remove(size_t index) const { - VariantData* data = getData(); - if (data) - data->remove(index); + void remove(size_t index) const { + VariantData::removeElement(getData(), index, getResourceManager()); } template - FORCE_INLINE typename enable_if::value>::type remove( - TChar* key) const { - VariantData* data = getData(); - if (data) - data->remove(adaptString(key)); + enable_if_t::value> remove(TChar* key) const { + VariantData::removeMember(getData(), adaptString(key), + getResourceManager()); } template - FORCE_INLINE typename enable_if::value>::type remove( - const TString& key) const { - VariantData* data = getData(); - if (data) - data->remove(adaptString(key)); + enable_if_t::value> remove(const TString& key) const { + VariantData::removeMember(getData(), adaptString(key), + getResourceManager()); + } + template + enable_if_t::value> remove(const TVariant& key) const { + if (key.template is()) + remove(key.template as()); + else + remove(key.template as()); } - FORCE_INLINE JsonArray createNestedArray() const; - FORCE_INLINE JsonObject createNestedObject() const; - FORCE_INLINE ElementProxy operator[](size_t index) const; + ElementProxy operator[](size_t index) const; template - FORCE_INLINE typename enable_if::value, bool>::type - containsKey(const TString& key) const; + enable_if_t::value, bool> containsKey( + const TString& key) const; template - FORCE_INLINE typename enable_if::value, bool>::type - containsKey(TChar* key) const; + enable_if_t::value, bool> containsKey(TChar* key) const; + template + enable_if_t::value, bool> containsKey( + const TVariant& key) const; template - FORCE_INLINE typename enable_if::value, - MemberProxy>::type - operator[](const TString& key) const; + FORCE_INLINE + enable_if_t::value, MemberProxy> + operator[](const TString& key) const; template - FORCE_INLINE typename enable_if::value, - MemberProxy>::type - operator[](TChar* key) const; - template - FORCE_INLINE JsonArray createNestedArray(const TString& key) const; + FORCE_INLINE + enable_if_t::value, MemberProxy> + operator[](TChar* key) const; + template + enable_if_t::value, JsonVariantConst> operator[]( + const TVariant& key) const { + if (key.template is()) + return operator[](key.template as()); + else + return operator[](key.template as()); + } + ARDUINOJSON_DEPRECATED("use add() instead") + JsonVariant add() const; + ARDUINOJSON_DEPRECATED("use add() instead") + JsonArray createNestedArray() const; template - FORCE_INLINE JsonArray createNestedArray(TChar* key) const; + ARDUINOJSON_DEPRECATED("use var[key].to() instead") + JsonArray createNestedArray(TChar* key) const; template - JsonObject createNestedObject(const TString& key) const; + ARDUINOJSON_DEPRECATED("use var[key].to() instead") + JsonArray createNestedArray(const TString& key) const; + ARDUINOJSON_DEPRECATED("use add() instead") + JsonObject createNestedObject() const; template + ARDUINOJSON_DEPRECATED("use var[key].to() instead") JsonObject createNestedObject(TChar* key) const; + template + ARDUINOJSON_DEPRECATED("use var[key].to() instead") + JsonObject createNestedObject(const TString& key) const; + ARDUINOJSON_DEPRECATED("always returns zero") + size_t memoryUsage() const { + return 0; + } + ARDUINOJSON_DEPRECATED("performs a deep copy") + void shallowCopy(JsonVariantConst src) const { + set(src); + } private: TDerived& derived() { return static_cast(*this); @@ -2664,21 +3098,42 @@ class VariantRefBase : public VariantTag { const TDerived& derived() const { return static_cast(*this); } - FORCE_INLINE MemoryPool* getPool() const { - return VariantAttorney::getPool(derived()); + ResourceManager* getResourceManager() const { + return VariantAttorney::getResourceManager(derived()); } - FORCE_INLINE VariantData* getData() const { + VariantData* getData() const { return VariantAttorney::getData(derived()); } - FORCE_INLINE VariantData* getOrCreateData() const { + VariantData* getOrCreateData() const { return VariantAttorney::getOrCreateData(derived()); } - private: FORCE_INLINE ArduinoJson::JsonVariant getVariant() const; FORCE_INLINE ArduinoJson::JsonVariantConst getVariantConst() const { - return ArduinoJson::JsonVariantConst(getData()); + 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(); } - FORCE_INLINE ArduinoJson::JsonVariant getOrCreateVariant() const; + template + bool doSet(T&& value) const { + return doSet( + detail::forward(value), + is_same::return_type, + bool>{}); + } + template + bool doSet(T&& value, false_type) const; + template + bool doSet(T&& value, true_type) const; + ArduinoJson::JsonVariant getOrCreateVariant() const; }; template class ElementProxy : public VariantRefBase>, @@ -2689,30 +3144,35 @@ class ElementProxy : public VariantRefBase>, : upstream_(upstream), index_(index) {} ElementProxy(const ElementProxy& src) : upstream_(src.upstream_), index_(src.index_) {} - FORCE_INLINE ElementProxy& operator=(const ElementProxy& src) { + ElementProxy& operator=(const ElementProxy& src) { this->set(src); return *this; } template - FORCE_INLINE ElementProxy& operator=(const T& src) { + ElementProxy& operator=(const T& src) { this->set(src); return *this; } template - FORCE_INLINE ElementProxy& operator=(T* src) { + ElementProxy& operator=(T* src) { this->set(src); return *this; } private: - FORCE_INLINE MemoryPool* getPool() const { - return VariantAttorney::getPool(upstream_); + ResourceManager* getResourceManager() const { + return VariantAttorney::getResourceManager(upstream_); } FORCE_INLINE VariantData* getData() const { - return variantGetElement(VariantAttorney::getData(upstream_), index_); + return VariantData::getElement( + VariantAttorney::getData(upstream_), index_, + VariantAttorney::getResourceManager(upstream_)); } - FORCE_INLINE VariantData* getOrCreateData() const { - return variantGetOrAddElement(VariantAttorney::getOrCreateData(upstream_), - index_, VariantAttorney::getPool(upstream_)); + VariantData* getOrCreateData() const { + auto data = VariantAttorney::getOrCreateData(upstream_); + if (!data) + return nullptr; + return data->getOrAddElement( + index_, VariantAttorney::getResourceManager(upstream_)); } TUpstream upstream_; size_t index_; @@ -2723,169 +3183,117 @@ class JsonVariant : public detail::VariantRefBase, public detail::VariantOperators { friend class detail::VariantAttorney; public: - JsonVariant() : data_(0), pool_(0) {} - JsonVariant(detail::MemoryPool* pool, detail::VariantData* data) - : data_(data), pool_(pool) {} + JsonVariant() : data_(0), resources_(0) {} + JsonVariant(detail::VariantData* data, detail::ResourceManager* resources) + : data_(data), resources_(resources) {} private: - FORCE_INLINE detail::MemoryPool* getPool() const { - return pool_; + detail::ResourceManager* getResourceManager() const { + return resources_; } - FORCE_INLINE detail::VariantData* getData() const { + detail::VariantData* getData() const { return data_; } - FORCE_INLINE detail::VariantData* getOrCreateData() const { + detail::VariantData* getOrCreateData() const { return data_; } detail::VariantData* data_; - detail::MemoryPool* pool_; + detail::ResourceManager* resources_; }; +namespace detail { +bool copyVariant(JsonVariant dst, JsonVariantConst src); +} template <> struct Converter : private detail::VariantAttorney { - static void toJson(JsonVariant src, JsonVariant dst) { - detail::variantCopyFrom(getData(dst), getData(src), getPool(dst)); + static void toJson(JsonVariantConst src, JsonVariant dst) { + copyVariant(dst, src); } static JsonVariant fromJson(JsonVariant src) { return src; } - static detail::InvalidConversion fromJson( - JsonVariantConst); static bool checkJson(JsonVariant src) { auto data = getData(src); return !!data; } - static bool checkJson(JsonVariantConst) { - return false; - } }; template <> struct Converter : private detail::VariantAttorney { static void toJson(JsonVariantConst src, JsonVariant dst) { - variantCopyFrom(getData(dst), getData(src), getPool(dst)); + copyVariant(dst, src); } static JsonVariantConst fromJson(JsonVariantConst src) { - return JsonVariantConst(getData(src)); + return JsonVariantConst(getData(src), getResourceManager(src)); } static bool checkJson(JsonVariantConst src) { auto data = getData(src); return !!data; } }; -ARDUINOJSON_END_PUBLIC_NAMESPACE -ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -struct SlotKeySetter { - SlotKeySetter(VariantSlot* instance) : instance_(instance) {} - template - void operator()(TStoredString s) { - if (!s) - return; - ARDUINOJSON_ASSERT(instance_ != 0); - instance_->setKey(s); - } - VariantSlot* instance_; -}; -template -inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool* pool) { - if (!var) - return false; - return storeString(pool, key, SlotKeySetter(var)); -} -inline size_t slotSize(const VariantSlot* var) { - size_t n = 0; - while (var) { - n++; - var = var->next(); - } - return n; -} -inline VariantData* slotData(VariantSlot* slot) { - return reinterpret_cast(slot); -} -ARDUINOJSON_END_PRIVATE_NAMESPACE -ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE -class VariantPtr { +template +class Ptr { public: - VariantPtr(detail::MemoryPool* pool, detail::VariantData* data) - : variant_(pool, data) {} - JsonVariant* operator->() { - return &variant_; + Ptr(T value) : value_(value) {} + T* operator->() { + return &value_; } - JsonVariant& operator*() { - return variant_; + T& operator*() { + return value_; } private: - JsonVariant variant_; + T value_; }; class JsonArrayIterator { friend class JsonArray; public: - JsonArrayIterator() : slot_(0) {} - explicit JsonArrayIterator(detail::MemoryPool* pool, - detail::VariantSlot* slot) - : pool_(pool), slot_(slot) {} - JsonVariant operator*() const { - return JsonVariant(pool_, slot_->data()); + JsonArrayIterator() {} + explicit JsonArrayIterator(detail::ArrayData::iterator iterator, + detail::ResourceManager* resources) + : iterator_(iterator), resources_(resources) {} + JsonVariant operator*() { + return JsonVariant(iterator_.data(), resources_); } - VariantPtr operator->() { - return VariantPtr(pool_, slot_->data()); + Ptr operator->() { + return operator*(); } bool operator==(const JsonArrayIterator& other) const { - return slot_ == other.slot_; + return iterator_ == other.iterator_; } bool operator!=(const JsonArrayIterator& other) const { - return slot_ != other.slot_; + return iterator_ != other.iterator_; } JsonArrayIterator& operator++() { - slot_ = slot_->next(); + iterator_.next(resources_); return *this; } - JsonArrayIterator& operator+=(size_t distance) { - slot_ = slot_->next(distance); - return *this; - } - private: - detail::MemoryPool* pool_; - detail::VariantSlot* slot_; -}; -class VariantConstPtr { - public: - VariantConstPtr(const detail::VariantData* data) : variant_(data) {} - JsonVariantConst* operator->() { - return &variant_; - } - JsonVariantConst& operator*() { - return variant_; - } private: - JsonVariantConst variant_; + detail::ArrayData::iterator iterator_; + detail::ResourceManager* resources_; }; class JsonArrayConstIterator { friend class JsonArray; public: - JsonArrayConstIterator() : slot_(0) {} - explicit JsonArrayConstIterator(const detail::VariantSlot* slot) - : slot_(slot) {} + JsonArrayConstIterator() {} + explicit JsonArrayConstIterator(detail::ArrayData::iterator iterator, + const detail::ResourceManager* resources) + : iterator_(iterator), resources_(resources) {} JsonVariantConst operator*() const { - return JsonVariantConst(slot_->data()); + return JsonVariantConst(iterator_.data(), resources_); } - VariantConstPtr operator->() { - return VariantConstPtr(slot_->data()); + Ptr operator->() { + return operator*(); } bool operator==(const JsonArrayConstIterator& other) const { - return slot_ == other.slot_; + return iterator_ == other.iterator_; } bool operator!=(const JsonArrayConstIterator& other) const { - return slot_ != other.slot_; + return iterator_ != other.iterator_; } JsonArrayConstIterator& operator++() { - slot_ = slot_->next(); - return *this; - } - JsonArrayConstIterator& operator+=(size_t distance) { - slot_ = slot_->next(distance); + iterator_.next(resources_); return *this; } private: - const detail::VariantSlot* slot_; + detail::ArrayData::iterator iterator_; + const detail::ResourceManager* resources_; }; class JsonObject; class JsonArrayConst : public detail::VariantOperators { @@ -2893,410 +3301,391 @@ class JsonArrayConst : public detail::VariantOperators { friend class detail::VariantAttorney; public: typedef JsonArrayConstIterator iterator; - FORCE_INLINE iterator begin() const { + iterator begin() const { if (!data_) return iterator(); - return iterator(data_->head()); + return iterator(data_->createIterator(resources_), resources_); } - FORCE_INLINE iterator end() const { + iterator end() const { return iterator(); } - FORCE_INLINE JsonArrayConst() : data_(0) {} - FORCE_INLINE JsonArrayConst(const detail::CollectionData* data) - : data_(data) {} - FORCE_INLINE bool operator==(JsonArrayConst rhs) const { - if (data_ == rhs.data_) - return true; - if (!data_ || !rhs.data_) - return false; - iterator it1 = begin(); - iterator it2 = rhs.begin(); - for (;;) { - bool end1 = it1 == end(); - bool end2 = it2 == rhs.end(); - if (end1 && end2) - return true; - if (end1 || end2) - return false; - if (*it1 != *it2) - return false; - ++it1; - ++it2; - } - } - FORCE_INLINE JsonVariantConst operator[](size_t index) const { - return JsonVariantConst(data_ ? data_->getElement(index) : 0); + JsonArrayConst() : data_(0), resources_(0) {} + JsonArrayConst(const detail::ArrayData* data, + const detail::ResourceManager* resources) + : data_(data), resources_(resources) {} + template + detail::enable_if_t::value, JsonVariantConst> + operator[](T index) const { + return JsonVariantConst( + detail::ArrayData::getElement(data_, size_t(index), resources_), + resources_); + } + template + detail::enable_if_t::value, JsonVariantConst> + operator[](const TVariant& variant) const { + if (variant.template is()) + return operator[](variant.template as()); + else + return JsonVariantConst(); } operator JsonVariantConst() const { - return JsonVariantConst(collectionToVariant(data_)); + return JsonVariantConst(getData(), resources_); } - FORCE_INLINE bool isNull() const { + bool isNull() const { return data_ == 0; } - FORCE_INLINE operator bool() const { + operator bool() const { return data_ != 0; } - FORCE_INLINE size_t memoryUsage() const { - return data_ ? data_->memoryUsage() : 0; + size_t nesting() const { + return detail::VariantData::nesting(getData(), resources_); } - FORCE_INLINE size_t nesting() const { - return variantNesting(collectionToVariant(data_)); + size_t size() const { + return data_ ? data_->size(resources_) : 0; } - FORCE_INLINE size_t size() const { - return data_ ? data_->size() : 0; + ARDUINOJSON_DEPRECATED("always returns zero") + size_t memoryUsage() const { + return 0; } private: const detail::VariantData* getData() const { return collectionToVariant(data_); } - const detail::CollectionData* data_; + const detail::ArrayData* data_; + const detail::ResourceManager* resources_; }; -template <> -struct Converter : private detail::VariantAttorney { - static void toJson(JsonVariantConst src, JsonVariant dst) { - variantCopyFrom(getData(dst), getData(src), getPool(dst)); - } - static JsonArrayConst fromJson(JsonVariantConst src) { - auto data = getData(src); - return data ? data->asArray() : 0; - } - static bool checkJson(JsonVariantConst src) { - auto data = getData(src); - return data && data->isArray(); +inline bool operator==(JsonArrayConst lhs, JsonArrayConst rhs) { + if (!lhs && !rhs) + return true; + if (!lhs || !rhs) + return false; + auto a = lhs.begin(); + auto b = rhs.begin(); + for (;;) { + if (a == b) // same pointer or both null + return true; + if (a == lhs.end() || b == rhs.end()) + return false; + if (*a != *b) + return false; + ++a; + ++b; } -}; +} class JsonObject; class JsonArray : public detail::VariantOperators { friend class detail::VariantAttorney; public: typedef JsonArrayIterator iterator; - FORCE_INLINE JsonArray() : data_(0), pool_(0) {} - FORCE_INLINE JsonArray(detail::MemoryPool* pool, detail::CollectionData* data) - : data_(data), pool_(pool) {} + JsonArray() : data_(0), resources_(0) {} + JsonArray(detail::ArrayData* data, detail::ResourceManager* resources) + : data_(data), resources_(resources) {} operator JsonVariant() { void* data = data_; // prevent warning cast-align - return JsonVariant(pool_, reinterpret_cast(data)); + return JsonVariant(reinterpret_cast(data), + resources_); } operator JsonArrayConst() const { - return JsonArrayConst(data_); + return JsonArrayConst(data_, resources_); } - JsonVariant add() const { - if (!data_) - return JsonVariant(); - return JsonVariant(pool_, data_->addElement(pool_)); + template + detail::enable_if_t::value, T> add() const { + return add().to(); + } + template + detail::enable_if_t::value, T> add() const { + return JsonVariant(detail::ArrayData::addElement(data_, resources_), + resources_); } template - FORCE_INLINE bool add(const T& value) const { - return add().set(value); + bool add(const T& value) const { + return detail::ArrayData::addValue(data_, value, resources_); } template - FORCE_INLINE bool add(T* value) const { - return add().set(value); + bool add(T* value) const { + return detail::ArrayData::addValue(data_, value, resources_); } - FORCE_INLINE iterator begin() const { + iterator begin() const { if (!data_) return iterator(); - return iterator(pool_, data_->head()); + return iterator(data_->createIterator(resources_), resources_); } - FORCE_INLINE iterator end() const { + iterator end() const { return iterator(); } - FORCE_INLINE bool set(JsonArrayConst src) const { - if (!data_ || !src.data_) + bool set(JsonArrayConst src) const { + if (!data_) return false; - return data_->copyFrom(*src.data_, pool_); + clear(); + for (auto element : src) { + if (!add(element)) + return false; + } + return true; } - FORCE_INLINE bool operator==(JsonArray rhs) const { - return JsonArrayConst(data_) == JsonArrayConst(rhs.data_); + void remove(iterator it) const { + detail::ArrayData::remove(data_, it.iterator_, resources_); } - FORCE_INLINE void remove(iterator it) const { - if (!data_) - return; - data_->removeSlot(it.slot_); + void remove(size_t index) const { + detail::ArrayData::removeElement(data_, index, resources_); } - FORCE_INLINE void remove(size_t index) const { - if (!data_) - return; - data_->removeElement(index); + template + detail::enable_if_t::value> remove( + TVariant variant) const { + if (variant.template is()) + remove(variant.template as()); } void clear() const { - if (!data_) - return; - data_->clear(); - } - FORCE_INLINE detail::ElementProxy operator[](size_t index) const { - return {*this, index}; + detail::ArrayData::clear(data_, resources_); } - FORCE_INLINE JsonObject createNestedObject() const; - FORCE_INLINE JsonArray createNestedArray() const { - return add().to(); + template + detail::enable_if_t::value, + detail::ElementProxy> + operator[](T index) const { + return {*this, size_t(index)}; + } + template + detail::enable_if_t::value, + detail::ElementProxy> + operator[](const TVariant& variant) const { + if (variant.template is()) + return operator[](variant.template as()); + else + return {*this, size_t(-1)}; } operator JsonVariantConst() const { - return JsonVariantConst(collectionToVariant(data_)); + return JsonVariantConst(collectionToVariant(data_), resources_); } - FORCE_INLINE bool isNull() const { + bool isNull() const { return data_ == 0; } - FORCE_INLINE operator bool() const { + operator bool() const { return data_ != 0; } - FORCE_INLINE size_t memoryUsage() const { - return data_ ? data_->memoryUsage() : 0; - } - FORCE_INLINE size_t nesting() const { - return variantNesting(collectionToVariant(data_)); - } - FORCE_INLINE size_t size() const { - return data_ ? data_->size() : 0; + size_t nesting() const { + return detail::VariantData::nesting(collectionToVariant(data_), resources_); } - private: - detail::MemoryPool* getPool() const { - return pool_; + size_t size() const { + return data_ ? data_->size(resources_) : 0; } - detail::VariantData* getData() const { - return collectionToVariant(data_); + ARDUINOJSON_DEPRECATED("use add() instead") + JsonVariant add() const { + return add(); } - detail::VariantData* getOrCreateData() const { - return collectionToVariant(data_); + ARDUINOJSON_DEPRECATED("use add() instead") + JsonArray createNestedArray() const { + return add(); } - detail::CollectionData* data_; - detail::MemoryPool* pool_; -}; -template <> -struct Converter : private detail::VariantAttorney { - static void toJson(JsonVariantConst src, JsonVariant dst) { - variantCopyFrom(getData(dst), getData(src), getPool(dst)); + ARDUINOJSON_DEPRECATED("use add() instead") + JsonObject createNestedObject() const; + ARDUINOJSON_DEPRECATED("always returns zero") + size_t memoryUsage() const { + return 0; } - static JsonArray fromJson(JsonVariant src) { - auto data = getData(src); - auto pool = getPool(src); - return JsonArray(pool, data != 0 ? data->asArray() : 0); + private: + detail::ResourceManager* getResourceManager() const { + return resources_; } - static detail::InvalidConversion fromJson( - JsonVariantConst); - static bool checkJson(JsonVariantConst) { - return false; + detail::VariantData* getData() const { + return collectionToVariant(data_); } - static bool checkJson(JsonVariant src) { - auto data = getData(src); - return data && data->isArray(); + detail::VariantData* getOrCreateData() const { + return collectionToVariant(data_); } + detail::ArrayData* data_; + detail::ResourceManager* resources_; }; class JsonPair { public: - JsonPair(detail::MemoryPool* pool, detail::VariantSlot* slot) { - if (slot) { - key_ = JsonString(slot->key(), slot->ownsKey() ? JsonString::Copied - : JsonString::Linked); - value_ = JsonVariant(pool, slot->data()); - } - } + JsonPair(detail::ObjectData::iterator iterator, + detail::ResourceManager* resources) + : iterator_(iterator), resources_(resources) {} JsonString key() const { - return key_; + if (!iterator_.done()) + return JsonString(iterator_.key(), iterator_.ownsKey() + ? JsonString::Copied + : JsonString::Linked); + else + return JsonString(); } - JsonVariant value() const { - return value_; + JsonVariant value() { + return JsonVariant(iterator_.data(), resources_); } private: - JsonString key_; - JsonVariant value_; + detail::ObjectData::iterator iterator_; + detail::ResourceManager* resources_; }; class JsonPairConst { public: - JsonPairConst(const detail::VariantSlot* slot) { - if (slot) { - key_ = JsonString(slot->key(), slot->ownsKey() ? JsonString::Copied - : JsonString::Linked); - value_ = JsonVariantConst(slot->data()); - } - } + JsonPairConst(detail::ObjectData::iterator iterator, + const detail::ResourceManager* resources) + : iterator_(iterator), resources_(resources) {} JsonString key() const { - return key_; + if (!iterator_.done()) + return JsonString(iterator_.key(), iterator_.ownsKey() + ? JsonString::Copied + : JsonString::Linked); + else + return JsonString(); } JsonVariantConst value() const { - return value_; - } - private: - JsonString key_; - JsonVariantConst value_; -}; -class JsonPairPtr { - public: - JsonPairPtr(detail::MemoryPool* pool, detail::VariantSlot* slot) - : pair_(pool, slot) {} - const JsonPair* operator->() const { - return &pair_; - } - const JsonPair& operator*() const { - return pair_; + return JsonVariantConst(iterator_.data(), resources_); } private: - JsonPair pair_; + detail::ObjectData::iterator iterator_; + const detail::ResourceManager* resources_; }; class JsonObjectIterator { friend class JsonObject; public: - JsonObjectIterator() : slot_(0) {} - explicit JsonObjectIterator(detail::MemoryPool* pool, - detail::VariantSlot* slot) - : pool_(pool), slot_(slot) {} + JsonObjectIterator() {} + explicit JsonObjectIterator(detail::ObjectData::iterator iterator, + detail::ResourceManager* resources) + : iterator_(iterator), resources_(resources) {} JsonPair operator*() const { - return JsonPair(pool_, slot_); + return JsonPair(iterator_, resources_); } - JsonPairPtr operator->() { - return JsonPairPtr(pool_, slot_); + Ptr operator->() { + return operator*(); } bool operator==(const JsonObjectIterator& other) const { - return slot_ == other.slot_; + return iterator_ == other.iterator_; } bool operator!=(const JsonObjectIterator& other) const { - return slot_ != other.slot_; + return iterator_ != other.iterator_; } JsonObjectIterator& operator++() { - slot_ = slot_->next(); + iterator_.next(resources_); return *this; } - JsonObjectIterator& operator+=(size_t distance) { - slot_ = slot_->next(distance); - return *this; - } - private: - detail::MemoryPool* pool_; - detail::VariantSlot* slot_; -}; -class JsonPairConstPtr { - public: - JsonPairConstPtr(const detail::VariantSlot* slot) : pair_(slot) {} - const JsonPairConst* operator->() const { - return &pair_; - } - const JsonPairConst& operator*() const { - return pair_; - } private: - JsonPairConst pair_; + detail::ObjectData::iterator iterator_; + detail::ResourceManager* resources_; }; class JsonObjectConstIterator { friend class JsonObject; public: - JsonObjectConstIterator() : slot_(0) {} - explicit JsonObjectConstIterator(const detail::VariantSlot* slot) - : slot_(slot) {} + JsonObjectConstIterator() {} + explicit JsonObjectConstIterator(detail::ObjectData::iterator iterator, + const detail::ResourceManager* resources) + : iterator_(iterator), resources_(resources) {} JsonPairConst operator*() const { - return JsonPairConst(slot_); + return JsonPairConst(iterator_, resources_); } - JsonPairConstPtr operator->() { - return JsonPairConstPtr(slot_); + Ptr operator->() { + return operator*(); } bool operator==(const JsonObjectConstIterator& other) const { - return slot_ == other.slot_; + return iterator_ == other.iterator_; } bool operator!=(const JsonObjectConstIterator& other) const { - return slot_ != other.slot_; + return iterator_ != other.iterator_; } JsonObjectConstIterator& operator++() { - slot_ = slot_->next(); - return *this; - } - JsonObjectConstIterator& operator+=(size_t distance) { - slot_ = slot_->next(distance); + iterator_.next(resources_); return *this; } private: - const detail::VariantSlot* slot_; + detail::ObjectData::iterator iterator_; + const detail::ResourceManager* resources_; }; class JsonObjectConst : public detail::VariantOperators { friend class JsonObject; friend class detail::VariantAttorney; public: typedef JsonObjectConstIterator iterator; - JsonObjectConst() : data_(0) {} - JsonObjectConst(const detail::CollectionData* data) : data_(data) {} + JsonObjectConst() : data_(0), resources_(0) {} + JsonObjectConst(const detail::ObjectData* data, + const detail::ResourceManager* resources) + : data_(data), resources_(resources) {} operator JsonVariantConst() const { - return JsonVariantConst(collectionToVariant(data_)); + return JsonVariantConst(getData(), resources_); } - FORCE_INLINE bool isNull() const { + bool isNull() const { return data_ == 0; } - FORCE_INLINE operator bool() const { + operator bool() const { return data_ != 0; } - FORCE_INLINE size_t memoryUsage() const { - return data_ ? data_->memoryUsage() : 0; - } - FORCE_INLINE size_t nesting() const { - return variantNesting(collectionToVariant(data_)); + size_t nesting() const { + return detail::VariantData::nesting(getData(), resources_); } - FORCE_INLINE size_t size() const { - return data_ ? data_->size() : 0; + size_t size() const { + return data_ ? data_->size(resources_) : 0; } - FORCE_INLINE iterator begin() const { + iterator begin() const { if (!data_) return iterator(); - return iterator(data_->head()); + return iterator(data_->createIterator(resources_), resources_); } - FORCE_INLINE iterator end() const { + iterator end() const { return iterator(); } template - FORCE_INLINE bool containsKey(const TString& key) const { - return getMember(detail::adaptString(key)) != 0; + detail::enable_if_t::value, bool> containsKey( + const TString& key) const { + return detail::ObjectData::getMember(data_, detail::adaptString(key), + resources_) != 0; } template - FORCE_INLINE bool containsKey(TChar* key) const { - return getMember(detail::adaptString(key)) != 0; + bool containsKey(TChar* key) const { + return detail::ObjectData::getMember(data_, detail::adaptString(key), + resources_) != 0; + } + template + detail::enable_if_t::value, bool> containsKey( + const TVariant& key) const { + return containsKey(key.template as()); } template - FORCE_INLINE typename detail::enable_if::value, - JsonVariantConst>::type + detail::enable_if_t::value, JsonVariantConst> operator[](const TString& key) const { - return JsonVariantConst(getMember(detail::adaptString(key))); + return JsonVariantConst(detail::ObjectData::getMember( + data_, detail::adaptString(key), resources_), + resources_); } template - FORCE_INLINE typename detail::enable_if::value, - JsonVariantConst>::type + detail::enable_if_t::value, JsonVariantConst> operator[](TChar* key) const { - return JsonVariantConst(getMember(detail::adaptString(key))); + return JsonVariantConst(detail::ObjectData::getMember( + data_, detail::adaptString(key), resources_), + resources_); + } + template + detail::enable_if_t::value, JsonVariantConst> + operator[](const TVariant& key) const { + if (key.template is()) + return operator[](key.template as()); + else + return JsonVariantConst(); } - FORCE_INLINE bool operator==(JsonObjectConst rhs) const { - if (data_ == rhs.data_) - return true; - if (!data_ || !rhs.data_) - return false; - size_t count = 0; - for (iterator it = begin(); it != end(); ++it) { - if (it->value() != rhs[it->key()]) - return false; - count++; - } - return count == rhs.size(); + ARDUINOJSON_DEPRECATED("always returns zero") + size_t memoryUsage() const { + return 0; } private: const detail::VariantData* getData() const { return collectionToVariant(data_); } - template - const detail::VariantData* getMember(TAdaptedString key) const { - if (!data_) - return 0; - return data_->getMember(key); - } - const detail::CollectionData* data_; + const detail::ObjectData* data_; + const detail::ResourceManager* resources_; }; -template <> -struct Converter : private detail::VariantAttorney { - static void toJson(JsonVariantConst src, JsonVariant dst) { - variantCopyFrom(getData(dst), getData(src), getPool(dst)); - } - static JsonObjectConst fromJson(JsonVariantConst src) { - auto data = getData(src); - return data != 0 ? data->asObject() : 0; - } - static bool checkJson(JsonVariantConst src) { - auto data = getData(src); - return data && data->isObject(); +inline bool operator==(JsonObjectConst lhs, JsonObjectConst rhs) { + if (!lhs && !rhs) // both are null + return true; + if (!lhs || !rhs) // only one is null + return false; + size_t count = 0; + for (auto kvp : lhs) { + auto rhsValue = rhs[kvp.key()]; + if (rhsValue.isUnbound()) + return false; + if (kvp.value() != rhsValue) + return false; + count++; } -}; + return count == rhs.size(); +} ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template @@ -3305,36 +3694,39 @@ class MemberProxy public VariantOperators> { friend class VariantAttorney; public: - FORCE_INLINE MemberProxy(TUpstream upstream, TStringRef key) + MemberProxy(TUpstream upstream, TStringRef key) : upstream_(upstream), key_(key) {} MemberProxy(const MemberProxy& src) : upstream_(src.upstream_), key_(src.key_) {} - FORCE_INLINE MemberProxy& operator=(const MemberProxy& src) { + MemberProxy& operator=(const MemberProxy& src) { this->set(src); return *this; } template - FORCE_INLINE MemberProxy& operator=(const T& src) { + MemberProxy& operator=(const T& src) { this->set(src); return *this; } template - FORCE_INLINE MemberProxy& operator=(T* src) { + MemberProxy& operator=(T* src) { this->set(src); return *this; } private: - FORCE_INLINE MemoryPool* getPool() const { - return VariantAttorney::getPool(upstream_); + ResourceManager* getResourceManager() const { + return VariantAttorney::getResourceManager(upstream_); } - FORCE_INLINE VariantData* getData() const { - return variantGetMember(VariantAttorney::getData(upstream_), - adaptString(key_)); + VariantData* getData() const { + return VariantData::getMember( + VariantAttorney::getData(upstream_), adaptString(key_), + VariantAttorney::getResourceManager(upstream_)); } - FORCE_INLINE VariantData* getOrCreateData() const { - return variantGetOrAddMember(VariantAttorney::getOrCreateData(upstream_), - adaptString(key_), - VariantAttorney::getPool(upstream_)); + VariantData* getOrCreateData() const { + auto data = VariantAttorney::getOrCreateData(upstream_); + if (!data) + return nullptr; + return data->getOrAddMember(adaptString(key_), + VariantAttorney::getResourceManager(upstream_)); } private: TUpstream upstream_; @@ -3347,109 +3739,138 @@ class JsonObject : public detail::VariantOperators { friend class detail::VariantAttorney; public: typedef JsonObjectIterator iterator; - FORCE_INLINE JsonObject() : data_(0), pool_(0) {} - FORCE_INLINE JsonObject(detail::MemoryPool* buf, detail::CollectionData* data) - : data_(data), pool_(buf) {} + JsonObject() : data_(0), resources_(0) {} + JsonObject(detail::ObjectData* data, detail::ResourceManager* resource) + : data_(data), resources_(resource) {} operator JsonVariant() const { void* data = data_; // prevent warning cast-align - return JsonVariant(pool_, reinterpret_cast(data)); + return JsonVariant(reinterpret_cast(data), + resources_); } operator JsonObjectConst() const { - return JsonObjectConst(data_); + return JsonObjectConst(data_, resources_); } operator JsonVariantConst() const { - return JsonVariantConst(collectionToVariant(data_)); + return JsonVariantConst(collectionToVariant(data_), resources_); } - FORCE_INLINE bool isNull() const { + bool isNull() const { return data_ == 0; } - FORCE_INLINE operator bool() const { + operator bool() const { return data_ != 0; } - FORCE_INLINE size_t memoryUsage() const { - return data_ ? data_->memoryUsage() : 0; - } - FORCE_INLINE size_t nesting() const { - return variantNesting(collectionToVariant(data_)); + size_t nesting() const { + return detail::VariantData::nesting(collectionToVariant(data_), resources_); } - FORCE_INLINE size_t size() const { - return data_ ? data_->size() : 0; + size_t size() const { + return data_ ? data_->size(resources_) : 0; } - FORCE_INLINE iterator begin() const { + iterator begin() const { if (!data_) return iterator(); - return iterator(pool_, data_->head()); + return iterator(data_->createIterator(resources_), resources_); } - FORCE_INLINE iterator end() const { + iterator end() const { return iterator(); } void clear() const { - if (!data_) - return; - data_->clear(); + detail::ObjectData::clear(data_, resources_); } - FORCE_INLINE bool set(JsonObjectConst src) { + bool set(JsonObjectConst src) { if (!data_ || !src.data_) return false; - return data_->copyFrom(*src.data_, pool_); - } - FORCE_INLINE bool operator==(JsonObject rhs) const { - return JsonObjectConst(data_) == JsonObjectConst(rhs.data_); + clear(); + for (auto kvp : src) { + if (!operator[](kvp.key()).set(kvp.value())) + return false; + } + return true; } template - FORCE_INLINE - typename detail::enable_if::value, - detail::MemberProxy>::type - operator[](const TString& key) const { + detail::enable_if_t::value, + detail::MemberProxy> + operator[](const TString& key) const { return {*this, key}; } template - FORCE_INLINE - typename detail::enable_if::value, - detail::MemberProxy>::type - operator[](TChar* key) const { + detail::enable_if_t::value, + detail::MemberProxy> + operator[](TChar* key) const { return {*this, key}; } + template + detail::enable_if_t::value, + detail::MemberProxy> + operator[](const TVariant& key) const { + if (key.template is()) + return {*this, key.template as()}; + else + return {*this, nullptr}; + } FORCE_INLINE void remove(iterator it) const { - if (!data_) - return; - data_->removeSlot(it.slot_); + detail::ObjectData::remove(data_, it.iterator_, resources_); } template - FORCE_INLINE void remove(const TString& key) const { - removeMember(detail::adaptString(key)); + detail::enable_if_t::value> remove( + const TString& key) const { + detail::ObjectData::removeMember(data_, detail::adaptString(key), + resources_); + } + template + detail::enable_if_t::value> remove( + const TVariant& key) const { + if (key.template is()) + remove(key.template as()); } template FORCE_INLINE void remove(TChar* key) const { - removeMember(detail::adaptString(key)); + detail::ObjectData::removeMember(data_, detail::adaptString(key), + resources_); } template - FORCE_INLINE - typename detail::enable_if::value, bool>::type - containsKey(const TString& key) const { - return getMember(detail::adaptString(key)) != 0; + detail::enable_if_t::value, bool> containsKey( + const TString& key) const { + return detail::ObjectData::getMember(data_, detail::adaptString(key), + resources_) != 0; } template - FORCE_INLINE - typename detail::enable_if::value, bool>::type - containsKey(TChar* key) const { - return getMember(detail::adaptString(key)) != 0; + detail::enable_if_t::value, bool> containsKey( + TChar* key) const { + return detail::ObjectData::getMember(data_, detail::adaptString(key), + resources_) != 0; + } + template + detail::enable_if_t::value, bool> containsKey( + const TVariant& key) const { + return containsKey(key.template as()); } - template - FORCE_INLINE JsonArray createNestedArray(const TString& key) const; template - FORCE_INLINE JsonArray createNestedArray(TChar* key) const; + ARDUINOJSON_DEPRECATED("use obj[key].to() instead") + JsonArray createNestedArray(TChar* key) const { + return operator[](key).template to(); + } template - JsonObject createNestedObject(const TString& key) const { - return operator[](key).template to(); + ARDUINOJSON_DEPRECATED("use obj[key].to() instead") + JsonArray createNestedArray(const TString& key) const { + return operator[](key).template to(); } template - JsonObject createNestedObject(TChar* key) const { + ARDUINOJSON_DEPRECATED("use obj[key].to() instead") + JsonObject createNestedObject(TChar* key) { + return operator[](key).template to(); + } + template + ARDUINOJSON_DEPRECATED("use obj[key].to() instead") + JsonObject createNestedObject(const TString& key) { return operator[](key).template to(); } + ARDUINOJSON_DEPRECATED("always returns zero") + size_t memoryUsage() const { + return 0; + } private: - detail::MemoryPool* getPool() const { - return pool_; + detail::ResourceManager* getResourceManager() const { + return resources_; } detail::VariantData* getData() const { return detail::collectionToVariant(data_); @@ -3457,46 +3878,47 @@ class JsonObject : public detail::VariantOperators { detail::VariantData* getOrCreateData() const { return detail::collectionToVariant(data_); } - template - inline detail::VariantData* getMember(TAdaptedString key) const { - if (!data_) - return 0; - return data_->getMember(key); + detail::ObjectData* data_; + detail::ResourceManager* resources_; +}; +class JsonDocument : public detail::VariantOperators { + friend class detail::VariantAttorney; + public: + explicit JsonDocument(Allocator* alloc = detail::DefaultAllocator::instance()) + : resources_(alloc) {} + JsonDocument(const JsonDocument& src) : JsonDocument(src.allocator()) { + set(src); } - template - void removeMember(TAdaptedString key) const { - if (!data_) - return; - data_->removeMember(key); + JsonDocument(JsonDocument&& src) + : JsonDocument(detail::DefaultAllocator::instance()) { + swap(*this, src); } - detail::CollectionData* data_; - detail::MemoryPool* pool_; -}; -template <> -struct Converter : private detail::VariantAttorney { - static void toJson(JsonVariantConst src, JsonVariant dst) { - variantCopyFrom(getData(dst), getData(src), getPool(dst)); + template + JsonDocument( + const T& src, Allocator* alloc = detail::DefaultAllocator::instance(), + detail::enable_if_t::value || + detail::is_same::value || + detail::is_same::value || + detail::is_same::value || + detail::is_same::value>* = 0) + : JsonDocument(alloc) { + set(src); } - static JsonObject fromJson(JsonVariant src) { - auto data = getData(src); - auto pool = getPool(src); - return JsonObject(pool, data != 0 ? data->asObject() : 0); + JsonDocument& operator=(JsonDocument src) { + swap(*this, src); + return *this; } - static detail::InvalidConversion fromJson( - JsonVariantConst); - static bool checkJson(JsonVariantConst) { - return false; + template + JsonDocument& operator=(const T& src) { + set(src); + return *this; } - static bool checkJson(JsonVariant src) { - auto data = getData(src); - return data && data->isObject(); + Allocator* allocator() const { + return resources_.allocator(); + } + void shrinkToFit() { + resources_.shrinkToFit(); } -}; -class JsonDocument : public detail::VariantOperators { - friend class detail::VariantAttorney; - public: - JsonDocument(const JsonDocument&) = delete; - JsonDocument& operator=(const JsonDocument&) = delete; template T as() { return getVariant().template as(); @@ -3506,8 +3928,8 @@ class JsonDocument : public detail::VariantOperators { return getVariant().template as(); } void clear() { - pool_.clear(); - data_.setNull(); + resources_.clear(); + data_.reset(); } template bool is() { @@ -3520,28 +3942,21 @@ class JsonDocument : public detail::VariantOperators { bool isNull() const { return getVariant().isNull(); } - size_t memoryUsage() const { - return pool_.size(); - } bool overflowed() const { - return pool_.overflowed(); + return resources_.overflowed(); } size_t nesting() const { - return variantNesting(&data_); - } - size_t capacity() const { - return pool_.capacity(); + return data_.nesting(&resources_); } size_t size() const { - return data_.size(); + return data_.size(&resources_); } bool set(const JsonDocument& src) { return to().set(src.as()); } template - typename detail::enable_if::value, - bool>::type - set(const T& src) { + detail::enable_if_t::value, bool> set( + const T& src) { return to().set(src); } template @@ -3549,118 +3964,157 @@ class JsonDocument : public detail::VariantOperators { clear(); return getVariant().template to(); } - JsonArray createNestedArray() { - return add().to(); - } - template - JsonArray createNestedArray(TChar* key) { - return operator[](key).template to(); - } - template - JsonArray createNestedArray(const TString& key) { - return operator[](key).template to(); - } - JsonObject createNestedObject() { - return add().to(); - } - template - JsonObject createNestedObject(TChar* key) { - return operator[](key).template to(); - } - template - JsonObject createNestedObject(const TString& key) { - return operator[](key).template to(); - } template bool containsKey(TChar* key) const { - return data_.getMember(detail::adaptString(key)) != 0; + return data_.getMember(detail::adaptString(key), &resources_) != 0; } template - bool containsKey(const TString& key) const { - return data_.getMember(detail::adaptString(key)) != 0; + detail::enable_if_t::value, bool> containsKey( + const TString& key) const { + return data_.getMember(detail::adaptString(key), &resources_) != 0; + } + template + detail::enable_if_t::value, bool> containsKey( + const TVariant& key) const { + return containsKey(key.template as()); } template - FORCE_INLINE typename detail::enable_if< - detail::IsString::value, - detail::MemberProxy>::type + detail::enable_if_t::value, + detail::MemberProxy> operator[](const TString& key) { return {*this, key}; } template - FORCE_INLINE typename detail::enable_if< - detail::IsString::value, - detail::MemberProxy>::type + detail::enable_if_t::value, + detail::MemberProxy> operator[](TChar* key) { return {*this, key}; } template - FORCE_INLINE typename detail::enable_if::value, - JsonVariantConst>::type + detail::enable_if_t::value, JsonVariantConst> operator[](const TString& key) const { - return JsonVariantConst(data_.getMember(detail::adaptString(key))); + return JsonVariantConst( + data_.getMember(detail::adaptString(key), &resources_), &resources_); } template - FORCE_INLINE typename detail::enable_if::value, - JsonVariantConst>::type + detail::enable_if_t::value, JsonVariantConst> operator[](TChar* key) const { - return JsonVariantConst(data_.getMember(detail::adaptString(key))); + return JsonVariantConst( + data_.getMember(detail::adaptString(key), &resources_), &resources_); } - FORCE_INLINE detail::ElementProxy operator[](size_t index) { - return {*this, index}; + template + detail::enable_if_t::value, + detail::ElementProxy> + operator[](T index) { + return {*this, size_t(index)}; + } + JsonVariantConst operator[](size_t index) const { + return JsonVariantConst(data_.getElement(index, &resources_), &resources_); + } + template + detail::enable_if_t::value, JsonVariantConst> + operator[](const TVariant& key) const { + if (key.template is()) + return operator[](key.template as()); + if (key.template is()) + return operator[](key.template as()); + return {}; } - FORCE_INLINE JsonVariantConst operator[](size_t index) const { - return JsonVariantConst(data_.getElement(index)); + template + detail::enable_if_t::value, T> add() { + return add().to(); } - FORCE_INLINE JsonVariant add() { - return JsonVariant(&pool_, data_.addElement(&pool_)); + template + detail::enable_if_t::value, T> add() { + return JsonVariant(data_.addElement(&resources_), &resources_); } template - FORCE_INLINE bool add(const TValue& value) { - return add().set(value); + bool add(const TValue& value) { + return data_.addValue(value, &resources_); } template - FORCE_INLINE bool add(TChar* value) { - return add().set(value); + bool add(TChar* value) { + return data_.addValue(value, &resources_); } - FORCE_INLINE void remove(size_t index) { - data_.remove(index); + template + detail::enable_if_t::value> remove(T index) { + detail::VariantData::removeElement(getData(), size_t(index), + getResourceManager()); } template - FORCE_INLINE typename detail::enable_if::value>::type - remove(TChar* key) { - data_.remove(detail::adaptString(key)); + detail::enable_if_t::value> remove(TChar* key) { + detail::VariantData::removeMember(getData(), detail::adaptString(key), + getResourceManager()); } template - FORCE_INLINE - typename detail::enable_if::value>::type - remove(const TString& key) { - data_.remove(detail::adaptString(key)); + detail::enable_if_t::value> remove( + const TString& key) { + detail::VariantData::removeMember(getData(), detail::adaptString(key), + getResourceManager()); + } + template + detail::enable_if_t::value> remove( + const TVariant& key) { + if (key.template is()) + remove(key.template as()); + if (key.template is()) + remove(key.template as()); } - FORCE_INLINE operator JsonVariant() { + operator JsonVariant() { return getVariant(); } - FORCE_INLINE operator JsonVariantConst() const { + operator JsonVariantConst() const { return getVariant(); } - protected: - JsonDocument() : pool_(0, 0) {} - JsonDocument(detail::MemoryPool pool) : pool_(pool) {} - JsonDocument(char* buf, size_t capa) : pool_(buf, capa) {} - ~JsonDocument() {} - void replacePool(detail::MemoryPool pool) { - pool_ = pool; + friend void swap(JsonDocument& a, JsonDocument& b) { + swap(a.resources_, b.resources_); + swap_(a.data_, b.data_); + } + ARDUINOJSON_DEPRECATED("use add() instead") + JsonVariant add() { + return add(); + } + ARDUINOJSON_DEPRECATED("use add() instead") + JsonArray createNestedArray() { + return add(); + } + template + ARDUINOJSON_DEPRECATED("use doc[key].to() instead") + JsonArray createNestedArray(TChar* key) { + return operator[](key).template to(); + } + template + ARDUINOJSON_DEPRECATED("use doc[key].to() instead") + JsonArray createNestedArray(const TString& key) { + return operator[](key).template to(); + } + ARDUINOJSON_DEPRECATED("use add() instead") + JsonObject createNestedObject() { + return add(); + } + template + ARDUINOJSON_DEPRECATED("use doc[key].to() instead") + JsonObject createNestedObject(TChar* key) { + return operator[](key).template to(); } + template + ARDUINOJSON_DEPRECATED("use doc[key].to() instead") + JsonObject createNestedObject(const TString& key) { + return operator[](key).template to(); + } + ARDUINOJSON_DEPRECATED("always returns zero") + size_t memoryUsage() const { + return 0; + } + private: JsonVariant getVariant() { - return JsonVariant(&pool_, &data_); + return JsonVariant(&data_, &resources_); } JsonVariantConst getVariant() const { - return JsonVariantConst(&data_); + return JsonVariantConst(&data_, &resources_); } - detail::MemoryPool pool_; - detail::VariantData data_; - protected: - detail::MemoryPool* getPool() { - return &pool_; + detail::ResourceManager* getResourceManager() { + return &resources_; } detail::VariantData* getData() { return &data_; @@ -3671,214 +4125,284 @@ class JsonDocument : public detail::VariantOperators { detail::VariantData* getOrCreateData() { return &data_; } + detail::ResourceManager resources_; + detail::VariantData data_; }; inline void convertToJson(const JsonDocument& src, JsonVariant dst) { dst.set(src.as()); } -template -class AllocatorOwner { - public: - AllocatorOwner() {} - AllocatorOwner(TAllocator a) : allocator_(a) {} - void* allocate(size_t size) { - return allocator_.allocate(size); - } - void deallocate(void* ptr) { - if (ptr) - allocator_.deallocate(ptr); - } - void* reallocate(void* ptr, size_t new_size) { - return allocator_.reallocate(ptr, new_size); +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct VariantDataVisitor { + typedef TResult result_type; + template + TResult visit(const T&) { + return TResult(); } - TAllocator& allocator() { - return allocator_; +}; +template +struct JsonVariantVisitor { + typedef TResult result_type; + template + TResult visit(const T&) { + return TResult(); } - private: - TAllocator allocator_; }; -template -class BasicJsonDocument : AllocatorOwner, public JsonDocument { +template +class VisitorAdapter { public: - explicit BasicJsonDocument(size_t capa, TAllocator alloc = TAllocator()) - : AllocatorOwner(alloc), JsonDocument(allocPool(capa)) {} - BasicJsonDocument(const BasicJsonDocument& src) - : AllocatorOwner(src), JsonDocument() { - copyAssignFrom(src); + using result_type = typename TVisitor::result_type; + VisitorAdapter(TVisitor& visitor, const ResourceManager* resources) + : visitor_(&visitor), resources_(resources) {} + result_type visit(const ArrayData& value) { + return visitor_->visit(JsonArrayConst(&value, resources_)); } - BasicJsonDocument(BasicJsonDocument&& src) : AllocatorOwner(src) { - moveAssignFrom(src); - } - BasicJsonDocument(const JsonDocument& src) { - copyAssignFrom(src); + result_type visit(const ObjectData& value) { + return visitor_->visit(JsonObjectConst(&value, resources_)); } template - BasicJsonDocument(const T& src, - typename detail::enable_if< - detail::is_same::value || - detail::is_same::value || - detail::is_same::value || - detail::is_same::value || - detail::is_same::value || - detail::is_same::value>::type* = 0) - : JsonDocument(allocPool(src.memoryUsage())) { - set(src); + result_type visit(const T& value) { + return visitor_->visit(value); } - BasicJsonDocument(JsonVariant src) - : JsonDocument(allocPool(src.memoryUsage())) { - set(src); - } - ~BasicJsonDocument() { - freePool(); + private: + TVisitor* visitor_; + const ResourceManager* resources_; +}; +template +typename TVisitor::result_type accept(JsonVariantConst variant, + TVisitor& visit) { + auto data = VariantAttorney::getData(variant); + if (!data) + return visit.visit(nullptr); + auto resources = VariantAttorney::getResourceManager(variant); + VisitorAdapter adapter(visit, resources); + return data->accept(adapter); +} +struct ComparerBase : JsonVariantVisitor {}; +template +struct Comparer; +template +struct Comparer::value>> : ComparerBase { + T rhs; // TODO: store adapted string? + explicit Comparer(T value) : rhs(value) {} + CompareResult visit(JsonString lhs) { + int i = stringCompare(adaptString(rhs), adaptString(lhs)); + if (i < 0) + return COMPARE_RESULT_GREATER; + else if (i > 0) + return COMPARE_RESULT_LESS; + else + return COMPARE_RESULT_EQUAL; } - BasicJsonDocument& operator=(const BasicJsonDocument& src) { - copyAssignFrom(src); - return *this; + CompareResult visit(nullptr_t) { + if (adaptString(rhs).isNull()) + return COMPARE_RESULT_EQUAL; + else + return COMPARE_RESULT_DIFFER; } - BasicJsonDocument& operator=(BasicJsonDocument&& src) { - moveAssignFrom(src); - return *this; + using ComparerBase::visit; +}; +template +struct Comparer< + T, enable_if_t::value || is_floating_point::value>> + : ComparerBase { + T rhs; + explicit Comparer(T value) : rhs(value) {} + CompareResult visit(JsonFloat lhs) { + return arithmeticCompare(lhs, rhs); } - template - BasicJsonDocument& operator=(const T& src) { - size_t requiredSize = src.memoryUsage(); - if (requiredSize > capacity()) - reallocPool(requiredSize); - set(src); - return *this; + CompareResult visit(JsonInteger lhs) { + return arithmeticCompare(lhs, rhs); } - void shrinkToFit() { - ptrdiff_t bytes_reclaimed = pool_.squash(); - if (bytes_reclaimed == 0) - return; - void* old_ptr = pool_.buffer(); - void* new_ptr = this->reallocate(old_ptr, pool_.capacity()); - ptrdiff_t ptr_offset = - static_cast(new_ptr) - static_cast(old_ptr); - pool_.movePointers(ptr_offset); - data_.movePointers(ptr_offset, ptr_offset - bytes_reclaimed); - } - bool garbageCollect() { - BasicJsonDocument tmp(*this); - if (!tmp.capacity()) - return false; - moveAssignFrom(tmp); - return true; + CompareResult visit(JsonUInt lhs) { + return arithmeticCompare(lhs, rhs); } - using AllocatorOwner::allocator; - private: - detail::MemoryPool allocPool(size_t requiredSize) { - size_t capa = detail::addPadding(requiredSize); - return {reinterpret_cast(this->allocate(capa)), capa}; + CompareResult visit(bool lhs) { + return visit(static_cast(lhs)); } - void reallocPool(size_t requiredSize) { - size_t capa = detail::addPadding(requiredSize); - if (capa == pool_.capacity()) - return; - freePool(); - replacePool(allocPool(detail::addPadding(requiredSize))); + using ComparerBase::visit; +}; +struct NullComparer : ComparerBase { + CompareResult visit(nullptr_t) { + return COMPARE_RESULT_EQUAL; } - void freePool() { - this->deallocate(getPool()->buffer()); + using ComparerBase::visit; +}; +template <> +struct Comparer : NullComparer { + explicit Comparer(nullptr_t) : NullComparer() {} +}; +struct ArrayComparer : ComparerBase { + JsonArrayConst rhs_; + explicit ArrayComparer(JsonArrayConst rhs) : rhs_(rhs) {} + CompareResult visit(JsonArrayConst lhs) { + if (rhs_ == lhs) + return COMPARE_RESULT_EQUAL; + else + return COMPARE_RESULT_DIFFER; } - void copyAssignFrom(const JsonDocument& src) { - reallocPool(src.capacity()); - set(src); + using ComparerBase::visit; +}; +struct ObjectComparer : ComparerBase { + JsonObjectConst rhs_; + explicit ObjectComparer(JsonObjectConst rhs) : rhs_(rhs) {} + CompareResult visit(JsonObjectConst lhs) { + if (lhs == rhs_) + return COMPARE_RESULT_EQUAL; + else + return COMPARE_RESULT_DIFFER; } - void moveAssignFrom(BasicJsonDocument& src) { - freePool(); - data_ = src.data_; - pool_ = src.pool_; - src.data_.setNull(); - src.pool_ = {0, 0}; + using ComparerBase::visit; +}; +struct RawComparer : ComparerBase { + RawString rhs_; + explicit RawComparer(RawString rhs) : rhs_(rhs) {} + CompareResult visit(RawString lhs) { + size_t size = rhs_.size() < lhs.size() ? rhs_.size() : lhs.size(); + int n = memcmp(lhs.data(), rhs_.data(), size); + if (n < 0) + return COMPARE_RESULT_LESS; + else if (n > 0) + return COMPARE_RESULT_GREATER; + else + return COMPARE_RESULT_EQUAL; } + using ComparerBase::visit; }; -struct DefaultAllocator { - void* allocate(size_t size) { - return malloc(size); +struct VariantComparer : ComparerBase { + JsonVariantConst rhs; + explicit VariantComparer(JsonVariantConst value) : rhs(value) {} + CompareResult visit(JsonArrayConst lhs) { + ArrayComparer comparer(lhs); + return reverseResult(comparer); } - void deallocate(void* ptr) { - free(ptr); + CompareResult visit(JsonObjectConst lhs) { + ObjectComparer comparer(lhs); + return reverseResult(comparer); } - void* reallocate(void* ptr, size_t new_size) { - return realloc(ptr, new_size); + CompareResult visit(JsonFloat lhs) { + Comparer comparer(lhs); + return reverseResult(comparer); } -}; -typedef BasicJsonDocument DynamicJsonDocument; -template -class StaticJsonDocument : public JsonDocument { - static const size_t capacity_ = - detail::AddPadding::value>::value; - public: - StaticJsonDocument() : JsonDocument(buffer_, capacity_) {} - StaticJsonDocument(const StaticJsonDocument& src) - : JsonDocument(buffer_, capacity_) { - set(src); + CompareResult visit(JsonString lhs) { + Comparer comparer(lhs); + return reverseResult(comparer); } - template - StaticJsonDocument( - const T& src, - typename detail::enable_if< - detail::is_convertible::value>::type* = 0) - : JsonDocument(buffer_, capacity_) { - set(src); + CompareResult visit(RawString value) { + RawComparer comparer(value); + return reverseResult(comparer); } - StaticJsonDocument(JsonVariant src) : JsonDocument(buffer_, capacity_) { - set(src); + CompareResult visit(JsonInteger lhs) { + Comparer comparer(lhs); + return reverseResult(comparer); } - StaticJsonDocument& operator=(const StaticJsonDocument& src) { - set(src); - return *this; + CompareResult visit(JsonUInt lhs) { + Comparer comparer(lhs); + return reverseResult(comparer); } - template - StaticJsonDocument& operator=(const T& src) { - set(src); - return *this; + CompareResult visit(bool lhs) { + Comparer comparer(lhs); + return reverseResult(comparer); } - void garbageCollect() { - StaticJsonDocument tmp(*this); - set(tmp); + CompareResult visit(nullptr_t) { + NullComparer comparer; + return reverseResult(comparer); } private: - char buffer_[capacity_]; + template + CompareResult reverseResult(TComparer& comparer) { + CompareResult reversedResult = accept(rhs, comparer); + switch (reversedResult) { + case COMPARE_RESULT_GREATER: + return COMPARE_RESULT_LESS; + case COMPARE_RESULT_LESS: + return COMPARE_RESULT_GREATER; + default: + return reversedResult; + } + } }; -inline JsonObject JsonArray::createNestedObject() const { - return add().to(); +template +struct Comparer< + T, enable_if_t::value>> + : VariantComparer { + explicit Comparer(const T& value) + : VariantComparer(static_cast(value)) {} +}; +template +CompareResult compare(ArduinoJson::JsonVariantConst lhs, const T& rhs) { + Comparer comparer(rhs); + return accept(lhs, comparer); +} +inline ArrayData::iterator ArrayData::at( + size_t index, const ResourceManager* resources) const { + auto it = createIterator(resources); + while (!it.done() && index) { + it.next(resources); + --index; + } + return it; +} +inline VariantData* ArrayData::getOrAddElement(size_t index, + ResourceManager* resources) { + auto it = createIterator(resources); + while (!it.done() && index > 0) { + it.next(resources); + index--; + } + if (it.done()) + index++; + VariantData* element = it.data(); + while (index > 0) { + element = addElement(resources); + if (!element) + return nullptr; + index--; + } + return element; } -ARDUINOJSON_END_PUBLIC_NAMESPACE -ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -template -inline JsonArray VariantRefBase::createNestedArray() const { - return add().template to(); +inline VariantData* ArrayData::getElement( + size_t index, const ResourceManager* resources) const { + return at(index, resources).data(); } -template -inline JsonObject VariantRefBase::createNestedObject() const { - return add().template to(); +inline void ArrayData::removeElement(size_t index, ResourceManager* resources) { + remove(at(index, resources), resources); } -template -inline ElementProxy VariantRefBase::operator[]( - size_t index) const { - return ElementProxy(derived(), index); +template +inline bool ArrayData::addValue(T&& value, ResourceManager* resources) { + ARDUINOJSON_ASSERT(resources != nullptr); + auto slot = resources->allocSlot(); + if (!slot) + return false; + JsonVariant variant(slot->data(), resources); + if (!variant.set(detail::forward(value))) { + resources->freeSlot(slot); + return false; + } + addSlot(slot, resources); + return true; } ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE template -inline typename detail::enable_if::value, bool>::type -copyArray(const T& src, JsonVariant dst) { +inline detail::enable_if_t::value, bool> copyArray( + const T& src, JsonVariant dst) { return dst.set(src); } template -inline typename detail::enable_if< - !detail::is_base_of::value, bool>::type +inline detail::enable_if_t< + !detail::is_base_of::value, bool> copyArray(T (&src)[N], const TDestination& dst) { return copyArray(src, N, dst); } template -inline typename detail::enable_if< - !detail::is_base_of::value, bool>::type +inline detail::enable_if_t< + !detail::is_base_of::value, bool> copyArray(const T* src, size_t len, const TDestination& dst) { bool ok = true; for (size_t i = 0; i < len; i++) { - ok &= copyArray(src[i], dst.add()); + ok &= copyArray(src[i], dst.template add()); } return ok; } @@ -3895,8 +4419,8 @@ 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) { +inline detail::enable_if_t::value, size_t> copyArray( + JsonVariantConst src, T& dst) { dst = src.as(); return 1; } @@ -3923,235 +4447,250 @@ inline size_t copyArray(JsonVariantConst src, char (&dst)[N]) { return 1; } template -inline typename detail::enable_if< - detail::is_array::value && - detail::is_base_of::value, - size_t>::type +inline detail::enable_if_t::value && + detail::is_base_of::value, + size_t> copyArray(const TSource& src, T& dst) { return copyArray(src.template as(), dst); } ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -inline VariantSlot* CollectionData::addSlot(MemoryPool* pool) { - VariantSlot* slot = pool->allocVariant(); - if (!slot) - return 0; - if (tail_) { - ARDUINOJSON_ASSERT(pool->owns(tail_)); // Can't alter a linked array/object - tail_->setNextNotNull(slot); - tail_ = slot; - } else { - head_ = slot; - tail_ = slot; - } - slot->clear(); - return slot; -} -inline VariantData* CollectionData::addElement(MemoryPool* pool) { - return slotData(addSlot(pool)); -} -template -inline VariantData* CollectionData::addMember(TAdaptedString key, - MemoryPool* pool) { - VariantSlot* slot = addSlot(pool); - if (!slotSetKey(slot, key, pool)) { - removeSlot(slot); - return 0; - } - return slot->data(); +#if ARDUINOJSON_ENABLE_ALIGNMENT +inline bool isAligned(size_t value) { + const size_t mask = sizeof(void*) - 1; + size_t addr = value; + return (addr & mask) == 0; } -inline void CollectionData::clear() { - head_ = 0; - tail_ = 0; +inline size_t addPadding(size_t bytes) { + const size_t mask = sizeof(void*) - 1; + return (bytes + mask) & ~mask; } -template -inline bool CollectionData::containsKey(const TAdaptedString& key) const { - return getSlot(key) != 0; -} -inline bool CollectionData::copyFrom(const CollectionData& src, - MemoryPool* pool) { - clear(); - for (VariantSlot* s = src.head_; s; s = s->next()) { - VariantData* var; - if (s->key() != 0) { - JsonString key(s->key(), - s->ownsKey() ? JsonString::Copied : JsonString::Linked); - var = addMember(adaptString(key), pool); - } else { - var = addElement(pool); - } - if (!var) - return false; - if (!var->copyFrom(*s->data(), pool)) - return false; - } +template +struct AddPadding { + static const size_t mask = sizeof(void*) - 1; + static const size_t value = (bytes + mask) & ~mask; +}; +#else +inline bool isAligned(size_t) { return true; } -template -inline VariantSlot* CollectionData::getSlot(TAdaptedString key) const { - if (key.isNull()) - return 0; - VariantSlot* slot = head_; - while (slot) { - if (stringEquals(key, adaptString(slot->key()))) - break; - slot = slot->next(); - } - return slot; -} -inline VariantSlot* CollectionData::getSlot(size_t index) const { - if (!head_) - return 0; - return head_->next(index); +inline size_t addPadding(size_t bytes) { + return bytes; } -inline VariantSlot* CollectionData::getPreviousSlot(VariantSlot* target) const { - VariantSlot* current = head_; - while (current) { - VariantSlot* next = current->next(); - if (next == target) - return current; - current = next; - } - return 0; +template +struct AddPadding { + static const size_t value = bytes; +}; +#endif +template +inline bool isAligned(T* ptr) { + return isAligned(reinterpret_cast(ptr)); } -template -inline VariantData* CollectionData::getMember(TAdaptedString key) const { - VariantSlot* slot = getSlot(key); - return slot ? slot->data() : 0; +template +inline T* addPadding(T* p) { + size_t address = addPadding(reinterpret_cast(p)); + return reinterpret_cast(address); } -template -inline VariantData* CollectionData::getOrAddMember(TAdaptedString key, - MemoryPool* pool) { - if (key.isNull()) - return 0; - VariantSlot* slot = getSlot(key); - if (slot) - return slot->data(); - return addMember(key, pool); -} -inline VariantData* CollectionData::getElement(size_t index) const { - VariantSlot* slot = getSlot(index); - return slot ? slot->data() : 0; -} -inline VariantData* CollectionData::getOrAddElement(size_t index, - MemoryPool* pool) { - VariantSlot* slot = head_; - while (slot && index > 0) { - slot = slot->next(); - index--; - } +inline CollectionIterator::CollectionIterator(VariantSlot* slot, SlotId slotId) + : slot_(slot), currentId_(slotId) { + nextId_ = slot_ ? slot_->next() : NULL_SLOT; +} +inline const char* CollectionIterator::key() const { + ARDUINOJSON_ASSERT(slot_ != nullptr); + return slot_->key(); +} +inline void CollectionIterator::setKey(const char* s) { + ARDUINOJSON_ASSERT(slot_ != nullptr); + ARDUINOJSON_ASSERT(s != nullptr); + return slot_->setKey(s); +} +inline void CollectionIterator::setKey(StringNode* s) { + ARDUINOJSON_ASSERT(slot_ != nullptr); + ARDUINOJSON_ASSERT(s != nullptr); + return slot_->setKey(s); +} +inline bool CollectionIterator::ownsKey() const { + ARDUINOJSON_ASSERT(slot_ != nullptr); + return slot_->ownsKey(); +} +inline void CollectionIterator::next(const ResourceManager* resources) { + ARDUINOJSON_ASSERT(currentId_ != NULL_SLOT); + slot_ = resources->getSlot(nextId_); + currentId_ = nextId_; + if (slot_) + nextId_ = slot_->next(); +} +inline CollectionData::iterator CollectionData::addSlot( + ResourceManager* resources) { + auto slot = resources->allocSlot(); if (!slot) - index++; - while (index > 0) { - slot = addSlot(pool); - index--; + return {}; + if (tail_ != NULL_SLOT) { + auto tail = resources->getSlot(tail_); + tail->setNext(slot.id()); + tail_ = slot.id(); + } else { + head_ = slot.id(); + tail_ = slot.id(); } - return slotData(slot); + return iterator(slot, slot.id()); } -inline void CollectionData::removeSlot(VariantSlot* slot) { - if (!slot) +inline void CollectionData::addSlot(SlotWithId slot, + ResourceManager* resources) { + if (tail_ != NULL_SLOT) { + auto tail = resources->getSlot(tail_); + tail->setNext(slot.id()); + tail_ = slot.id(); + } else { + head_ = slot.id(); + tail_ = slot.id(); + } +} +inline void CollectionData::clear(ResourceManager* resources) { + auto next = head_; + while (next != NULL_SLOT) { + auto currId = next; + auto slot = resources->getSlot(next); + next = slot->next(); + resources->freeSlot(SlotWithId(slot, currId)); + } + head_ = NULL_SLOT; + tail_ = NULL_SLOT; +} +inline SlotWithId CollectionData::getPreviousSlot( + VariantSlot* target, const ResourceManager* resources) const { + auto prev = SlotWithId(); + auto currentId = head_; + while (currentId != NULL_SLOT) { + auto currentSlot = resources->getSlot(currentId); + if (currentSlot == target) + return prev; + prev = SlotWithId(currentSlot, currentId); + currentId = currentSlot->next(); + } + return SlotWithId(); +} +inline void CollectionData::remove(iterator it, ResourceManager* resources) { + if (it.done()) return; - VariantSlot* prev = getPreviousSlot(slot); - VariantSlot* next = slot->next(); + auto curr = it.slot_; + auto prev = getPreviousSlot(curr, resources); + auto next = curr->next(); if (prev) prev->setNext(next); else head_ = next; - if (!next) - tail_ = prev; -} -inline void CollectionData::removeElement(size_t index) { - removeSlot(getSlot(index)); + if (next == NULL_SLOT) + tail_ = prev.id(); + resources->freeSlot({it.slot_, it.currentId_}); } -inline size_t CollectionData::memoryUsage() const { - size_t total = 0; - for (VariantSlot* s = head_; s; s = s->next()) { - total += sizeof(VariantSlot) + s->data()->memoryUsage(); - if (s->ownsKey()) - total += strlen(s->key()) + 1; +inline size_t CollectionData::nesting(const ResourceManager* resources) const { + size_t maxChildNesting = 0; + for (auto it = createIterator(resources); !it.done(); it.next(resources)) { + size_t childNesting = it->nesting(resources); + if (childNesting > maxChildNesting) + maxChildNesting = childNesting; } - return total; -} -inline size_t CollectionData::size() const { - return slotSize(head_); -} -template -inline void movePointer(T*& p, ptrdiff_t offset) { - if (!p) - return; - p = reinterpret_cast( - reinterpret_cast(reinterpret_cast(p) + offset)); - ARDUINOJSON_ASSERT(isAligned(p)); -} -inline void CollectionData::movePointers(ptrdiff_t stringDistance, - ptrdiff_t variantDistance) { - movePointer(head_, variantDistance); - movePointer(tail_, variantDistance); - for (VariantSlot* slot = head_; slot; slot = slot->next()) - slot->movePointers(stringDistance, variantDistance); -} -ARDUINOJSON_END_PRIVATE_NAMESPACE -ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE -template -inline JsonArray JsonObject::createNestedArray(const TString& key) const { - return operator[](key).template to(); -} -template -inline JsonArray JsonObject::createNestedArray(TChar* key) const { - return operator[](key).template to(); -} -ARDUINOJSON_END_PUBLIC_NAMESPACE -ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -template -template -inline JsonArray VariantRefBase::createNestedArray( - const TString& key) const { - return operator[](key).template to(); -} -template -template -inline JsonArray VariantRefBase::createNestedArray(TChar* key) const { - return operator[](key).template to(); -} -template -template -inline JsonObject VariantRefBase::createNestedObject( - const TString& key) const { - return operator[](key).template to(); + return maxChildNesting + 1; } -template -template -inline JsonObject VariantRefBase::createNestedObject( - TChar* key) const { - return operator[](key).template to(); +inline size_t CollectionData::size(const ResourceManager* resources) const { + size_t count = 0; + for (auto it = createIterator(resources); !it.done(); it.next(resources)) + count++; + return count; +} +inline void ResourceManager::freeSlot(SlotWithId slot) { + if (slot->ownsKey()) + dereferenceString(slot->key()); + slot->data()->setNull(this); + variantPools_.freeSlot(slot); +} +inline void VariantPool::create(SlotCount cap, Allocator* allocator) { + ARDUINOJSON_ASSERT(cap > 0); + slots_ = + reinterpret_cast(allocator->allocate(slotsToBytes(cap))); + capacity_ = slots_ ? cap : 0; + usage_ = 0; +} +inline void VariantPool::destroy(Allocator* allocator) { + if (slots_) + allocator->deallocate(slots_); + slots_ = nullptr; + capacity_ = 0; + usage_ = 0; +} +inline void VariantPool::shrinkToFit(Allocator* allocator) { + auto newSlots = reinterpret_cast( + allocator->reallocate(slots_, slotsToBytes(usage_))); + if (newSlots) { + slots_ = newSlots; + capacity_ = usage_; + } +} +inline SlotWithId VariantPool::allocSlot() { + if (!slots_) + return {}; + if (usage_ >= capacity_) + return {}; + auto index = usage_++; + auto slot = &slots_[index]; + return {new (slot) VariantSlot, SlotId(index)}; +} +inline VariantSlot* VariantPool::getSlot(SlotId id) const { + ARDUINOJSON_ASSERT(id < usage_); + return &slots_[id]; +} +inline SlotCount VariantPool::usage() const { + return usage_; +} +inline void VariantPool::clear() { + usage_ = 0; +} +inline SlotCount VariantPool::bytesToSlots(size_t n) { + return static_cast(n / sizeof(VariantSlot)); +} +inline size_t VariantPool::slotsToBytes(SlotCount n) { + return n * sizeof(VariantSlot); +} +inline SlotWithId VariantPoolList::allocFromFreeList() { + ARDUINOJSON_ASSERT(freeList_ != NULL_SLOT); + auto id = freeList_; + auto slot = getSlot(freeList_); + freeList_ = slot->next(); + return {new (slot) VariantSlot, id}; +} +inline void VariantPoolList::freeSlot(SlotWithId slot) { + slot->setNext(freeList_); + freeList_ = slot.id(); } -template -template -inline typename enable_if::value, bool>::type -VariantRefBase::containsKey(const TString& key) const { - return variantGetMember(VariantAttorney::getData(derived()), - adaptString(key)) != 0; +template +inline VariantData* ObjectData::getMember( + TAdaptedString key, const ResourceManager* resources) const { + return findKey(key, resources).data(); } -template -template -inline typename enable_if::value, bool>::type -VariantRefBase::containsKey(TChar* key) const { - return variantGetMember(VariantAttorney::getData(derived()), - adaptString(key)) != 0; +template +VariantData* ObjectData::getOrAddMember(TAdaptedString key, + ResourceManager* resources) { + auto it = findKey(key, resources); + if (!it.done()) + return it.data(); + return addMember(key, resources); } -template -template -inline typename enable_if::value, - MemberProxy>::type -VariantRefBase::operator[](TString* key) const { - return MemberProxy(derived(), key); +template +inline ObjectData::iterator ObjectData::findKey( + TAdaptedString key, const ResourceManager* resources) const { + if (key.isNull()) + return iterator(); + for (auto it = createIterator(resources); !it.done(); it.next(resources)) { + if (stringEquals(key, adaptString(it.key()))) + return it; + } + return iterator(); } -template -template -inline typename enable_if::value, - MemberProxy>::type -VariantRefBase::operator[](const TString& key) const { - return MemberProxy(derived(), key); +template +inline void ObjectData::removeMember(TAdaptedString key, + ResourceManager* resources) { + remove(findKey(key, resources), resources); } class EscapeSequence { public: @@ -4320,8 +4859,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) { + typedef make_unsigned_t unsigned_type; unsigned_type unsigned_value; if (value < 0) { writeRaw('-'); @@ -4332,7 +4871,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; @@ -4385,8 +4924,9 @@ class DummyWriter { template