diff --git a/Arduino_package/hardware/cores/ambd/ArduinoJson.h b/Arduino_package/hardware/cores/ambd/ArduinoJson.h new file mode 100644 index 00000000..1dd1391d --- /dev/null +++ b/Arduino_package/hardware/cores/ambd/ArduinoJson.h @@ -0,0 +1,8873 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2024, Benoit BLANCHON +// MIT License + +#pragma once + +#ifdef __cplusplus + +#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_VER < 1910) +#error ArduinoJson requires C++11 or newer. Configure your compiler for C++11 or downgrade ArduinoJson to 6.20. +#endif +#ifndef ARDUINOJSON_ENABLE_STD_STREAM +#ifdef __has_include +#if __has_include() && \ + __has_include() && \ + !defined(min) && \ + !defined(max) +#define ARDUINOJSON_ENABLE_STD_STREAM 1 +#else +#define ARDUINOJSON_ENABLE_STD_STREAM 0 +#endif +#else +#ifdef ARDUINO +#define ARDUINOJSON_ENABLE_STD_STREAM 0 +#else +#define ARDUINOJSON_ENABLE_STD_STREAM 1 +#endif +#endif +#endif +#ifndef ARDUINOJSON_ENABLE_STD_STRING +#ifdef __has_include +#if __has_include() && !defined(min) && !defined(max) +#define ARDUINOJSON_ENABLE_STD_STRING 1 +#else +#define ARDUINOJSON_ENABLE_STD_STRING 0 +#endif +#else +#ifdef ARDUINO +#define ARDUINOJSON_ENABLE_STD_STRING 0 +#else +#define ARDUINOJSON_ENABLE_STD_STRING 1 +#endif +#endif +#endif +#ifndef ARDUINOJSON_ENABLE_STRING_VIEW +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +#define ARDUINOJSON_ENABLE_STRING_VIEW 1 +#else +#define ARDUINOJSON_ENABLE_STRING_VIEW 0 +#endif +#else +#define ARDUINOJSON_ENABLE_STRING_VIEW 0 +#endif +#endif +#ifndef ARDUINOJSON_USE_DOUBLE +#define ARDUINOJSON_USE_DOUBLE 1 +#endif +#ifndef ARDUINOJSON_SIZEOF_POINTER +#if defined(__SIZEOF_POINTER__) +#define ARDUINOJSON_SIZEOF_POINTER __SIZEOF_POINTER__ +#elif defined(_WIN64) && _WIN64 +#define ARDUINOJSON_SIZEOF_POINTER 8 // 64 bits +#else +#define ARDUINOJSON_SIZEOF_POINTER 4 // assume 32 bits otherwise +#endif +#endif +#ifndef ARDUINOJSON_USE_LONG_LONG +#if ARDUINOJSON_SIZEOF_POINTER >= 4 // 32 & 64 bits systems +#define ARDUINOJSON_USE_LONG_LONG 1 +#else +#define ARDUINOJSON_USE_LONG_LONG 0 +#endif +#endif +#ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT +#define ARDUINOJSON_DEFAULT_NESTING_LIMIT 10 +#endif +#ifndef ARDUINOJSON_SLOT_ID_SIZE +#if ARDUINOJSON_SIZEOF_POINTER <= 2 +#define ARDUINOJSON_SLOT_ID_SIZE 1 // up to 255 slots +#elif ARDUINOJSON_SIZEOF_POINTER == 4 +#define ARDUINOJSON_SLOT_ID_SIZE 2 // up to 65535 slots +#else +#define ARDUINOJSON_SLOT_ID_SIZE 4 // up to 4294967295 slots +#endif +#endif +#ifndef ARDUINOJSON_POOL_CAPACITY +#if ARDUINOJSON_SIZEOF_POINTER <= 2 +#define ARDUINOJSON_POOL_CAPACITY 16 // 128 bytes +#elif ARDUINOJSON_SIZEOF_POINTER == 4 +#define ARDUINOJSON_POOL_CAPACITY 64 // 1024 bytes +#else +#define ARDUINOJSON_POOL_CAPACITY 128 // 3072 bytes +#endif +#endif +#ifndef ARDUINOJSON_INITIAL_POOL_COUNT +#define ARDUINOJSON_INITIAL_POOL_COUNT 4 +#endif +#ifndef ARDUINOJSON_AUTO_SHRINK +#if ARDUINOJSON_SIZEOF_POINTER <= 2 +#define ARDUINOJSON_AUTO_SHRINK 0 +#else +#define ARDUINOJSON_AUTO_SHRINK 1 +#endif +#endif +#ifndef ARDUINOJSON_STRING_LENGTH_SIZE +#if ARDUINOJSON_SIZEOF_POINTER <= 2 +#define ARDUINOJSON_STRING_LENGTH_SIZE 1 // up to 255 characters +#else +#define ARDUINOJSON_STRING_LENGTH_SIZE 2 // up to 65535 characters +#endif +#endif +#ifdef ARDUINO +#ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING +#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1 +#endif +#ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM +#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1 +#endif +#ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT +#define ARDUINOJSON_ENABLE_ARDUINO_PRINT 1 +#endif +#ifndef ARDUINOJSON_ENABLE_PROGMEM +#define ARDUINOJSON_ENABLE_PROGMEM 1 +#endif +#else // ARDUINO +#ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING +#define ARDUINOJSON_ENABLE_ARDUINO_STRING 0 +#endif +#ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM +#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 0 +#endif +#ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT +#define ARDUINOJSON_ENABLE_ARDUINO_PRINT 0 +#endif +#ifndef ARDUINOJSON_ENABLE_PROGMEM +#ifdef __AVR__ +#define ARDUINOJSON_ENABLE_PROGMEM 1 +#else +#define ARDUINOJSON_ENABLE_PROGMEM 0 +#endif +#endif +#endif // ARDUINO +#ifndef ARDUINOJSON_DECODE_UNICODE +#define ARDUINOJSON_DECODE_UNICODE 1 +#endif +#ifndef ARDUINOJSON_ENABLE_COMMENTS +#define ARDUINOJSON_ENABLE_COMMENTS 0 +#endif +#ifndef ARDUINOJSON_ENABLE_NAN +#define ARDUINOJSON_ENABLE_NAN 0 +#endif +#ifndef ARDUINOJSON_ENABLE_INFINITY +#define ARDUINOJSON_ENABLE_INFINITY 0 +#endif +#ifndef ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD +#define ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD 1e7 +#endif +#ifndef ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD +#define ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD 1e-5 +#endif +#ifndef ARDUINOJSON_LITTLE_ENDIAN +#if defined(_MSC_VER) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || defined(__LITTLE_ENDIAN__) || defined(__i386) || defined(__x86_64) +#define ARDUINOJSON_LITTLE_ENDIAN 1 +#else +#define ARDUINOJSON_LITTLE_ENDIAN 0 +#endif +#endif +#ifndef ARDUINOJSON_ENABLE_ALIGNMENT +#if defined(__AVR) +#define ARDUINOJSON_ENABLE_ALIGNMENT 0 +#else +#define ARDUINOJSON_ENABLE_ALIGNMENT 1 +#endif +#endif +#ifndef ARDUINOJSON_TAB +#define ARDUINOJSON_TAB " " +#endif +#ifndef ARDUINOJSON_STRING_BUFFER_SIZE +#define ARDUINOJSON_STRING_BUFFER_SIZE 32 +#endif +#ifndef ARDUINOJSON_DEBUG +#ifdef __PLATFORMIO_BUILD_DEBUG__ +#define ARDUINOJSON_DEBUG 1 +#else +#define ARDUINOJSON_DEBUG 0 +#endif +#endif +#if defined(nullptr) +#error nullptr is defined as a macro. Remove the faulty #define or #undef nullptr +#endif +#if ARDUINOJSON_ENABLE_ARDUINO_STRING || ARDUINOJSON_ENABLE_ARDUINO_STREAM || ARDUINOJSON_ENABLE_ARDUINO_PRINT || (ARDUINOJSON_ENABLE_PROGMEM && defined(ARDUINO)) +#include +#endif +#if !ARDUINOJSON_DEBUG +#ifdef __clang__ +#pragma clang system_header +#elif defined __GNUC__ +#pragma GCC system_header +#endif +#endif +#define ARDUINOJSON_CONCAT_(A, B) A##B +#define ARDUINOJSON_CONCAT2(A, B) ARDUINOJSON_CONCAT_(A, B) +#define ARDUINOJSON_CONCAT4(A, B, C, D) \ + ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT2(A, B), ARDUINOJSON_CONCAT2(C, D)) +#define ARDUINOJSON_BIN2ALPHA_0000() A +#define ARDUINOJSON_BIN2ALPHA_0001() B +#define ARDUINOJSON_BIN2ALPHA_0010() C +#define ARDUINOJSON_BIN2ALPHA_0011() D +#define ARDUINOJSON_BIN2ALPHA_0100() E +#define ARDUINOJSON_BIN2ALPHA_0101() F +#define ARDUINOJSON_BIN2ALPHA_0110() G +#define ARDUINOJSON_BIN2ALPHA_0111() H +#define ARDUINOJSON_BIN2ALPHA_1000() I +#define ARDUINOJSON_BIN2ALPHA_1001() J +#define ARDUINOJSON_BIN2ALPHA_1010() K +#define ARDUINOJSON_BIN2ALPHA_1011() L +#define ARDUINOJSON_BIN2ALPHA_1100() M +#define ARDUINOJSON_BIN2ALPHA_1101() N +#define ARDUINOJSON_BIN2ALPHA_1110() O +#define ARDUINOJSON_BIN2ALPHA_1111() P +#define ARDUINOJSON_BIN2ALPHA_(A, B, C, D) ARDUINOJSON_BIN2ALPHA_##A##B##C##D() +#define ARDUINOJSON_BIN2ALPHA(A, B, C, D) ARDUINOJSON_BIN2ALPHA_(A, B, C, D) +#define ARDUINOJSON_VERSION "7.0.4" +#define ARDUINOJSON_VERSION_MAJOR 7 +#define ARDUINOJSON_VERSION_MINOR 0 +#define ARDUINOJSON_VERSION_REVISION 4 +#define ARDUINOJSON_VERSION_MACRO V704 +#ifndef ARDUINOJSON_VERSION_NAMESPACE +#define ARDUINOJSON_VERSION_NAMESPACE \ + ARDUINOJSON_CONCAT4(ARDUINOJSON_VERSION_MACRO, \ + ARDUINOJSON_BIN2ALPHA(ARDUINOJSON_ENABLE_PROGMEM, \ + ARDUINOJSON_USE_LONG_LONG, \ + ARDUINOJSON_USE_DOUBLE, 1), \ + ARDUINOJSON_BIN2ALPHA(ARDUINOJSON_ENABLE_NAN, \ + ARDUINOJSON_ENABLE_INFINITY, \ + ARDUINOJSON_ENABLE_COMMENTS, \ + ARDUINOJSON_DECODE_UNICODE), \ + ARDUINOJSON_SLOT_ID_SIZE) +#endif +#define ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE \ + namespace ArduinoJson { \ + inline namespace ARDUINOJSON_VERSION_NAMESPACE { +#define ARDUINOJSON_END_PUBLIC_NAMESPACE \ + } \ + } +#define ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE \ + namespace ArduinoJson { \ + inline namespace ARDUINOJSON_VERSION_NAMESPACE { \ + namespace detail { +#define ARDUINOJSON_END_PRIVATE_NAMESPACE \ + } \ + } \ + } +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +template +struct Converter; +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +class InvalidConversion; // Error here? See https://arduinojson.org/v7/invalid-conversion/ +template +struct ConverterNeedsWriteableRef; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#include +#include +#include +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class Allocator { +public: + virtual void* allocate(size_t size) = 0; + virtual void deallocate(void* ptr) = 0; + virtual void* reallocate(void* ptr, size_t new_size) = 0; +protected: + ~Allocator() = default; +}; +namespace detail { + class DefaultAllocator: public Allocator { + public: + void* allocate(size_t size) override + { + return malloc(size); + } + void deallocate(void* ptr) override + { + free(ptr); + } + void* reallocate(void* ptr, size_t new_size) override + { + return realloc(ptr, new_size); + } + static Allocator* instance() + { + static DefaultAllocator allocator; + return &allocator; + } + private: + DefaultAllocator() = default; + ~DefaultAllocator() = default; + }; +} // namespace detail +ARDUINOJSON_END_PUBLIC_NAMESPACE +#if ARDUINOJSON_DEBUG +#include +#define ARDUINOJSON_ASSERT(X) assert(X) +#else +#define ARDUINOJSON_ASSERT(X) ((void)0) +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct uint_t; +template<> +struct uint_t<8> { + typedef uint8_t type; +}; +template<> +struct uint_t<16> { + typedef uint16_t type; +}; +template<> +struct uint_t<32> { + typedef uint32_t type; +}; +template +struct conditional { + typedef TrueType type; +}; +template +struct conditional { + typedef FalseType type; +}; +template +struct enable_if {}; +template +struct enable_if { + typedef T type; +}; +template +struct integral_constant { + static const T value = v; +}; +typedef integral_constant true_type; +typedef integral_constant false_type; +template +struct is_array: false_type {}; +template +struct is_array: true_type {}; +template +struct is_array: true_type {}; +template +struct remove_reference { + typedef T type; +}; +template +struct remove_reference { + typedef T type; +}; +template +class is_base_of { +protected: // <- to avoid GCC's "all member functions in class are private" + static int probe(const TBase*); + static char probe(...); +public: + static const bool value = + sizeof(probe(reinterpret_cast::type*>( + 0))) + == sizeof(int); +}; +template +T&& declval(); +template +struct is_class { +protected: // <- to avoid GCC's "all member functions in class are private" + template + static int probe(void (U::*)(void)); + template + static char probe(...); +public: + static const bool value = sizeof(probe(0)) == sizeof(int); +}; +template +struct is_const: false_type {}; +template +struct is_const: true_type {}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4244) +#endif +#ifdef __ICCARM__ +#pragma diag_suppress = Pa093 +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct is_convertible { +protected: // <- to avoid GCC's "all member functions in class are private" + static int probe(To); + static char probe(...); + static From& from_; +public: + static const bool value = sizeof(probe(from_)) == sizeof(int); +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#ifdef __ICCARM__ +#pragma diag_default = Pa093 +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct is_same: false_type {}; +template +struct is_same: true_type {}; +template +struct remove_cv { + typedef T type; +}; +template +struct remove_cv { + typedef T type; +}; +template +struct remove_cv { + typedef T type; +}; +template +struct remove_cv { + typedef T type; +}; +template +struct is_floating_point: integral_constant< bool, // + is_same::type>::value || is_same::type>::value> {}; +template +struct is_integral: integral_constant::type, signed char>::value || is_same::type, unsigned char>::value || is_same::type, signed short>::value || is_same::type, unsigned short>::value || is_same::type, signed int>::value || is_same::type, unsigned int>::value || is_same::type, signed long>::value || is_same::type, unsigned long>::value || is_same::type, signed long long>::value || is_same::type, unsigned long long>::value || is_same::type, char>::value || is_same::type, bool>::value> {}; +template +struct is_enum { + static const bool value = is_convertible::value && !is_class::value && !is_integral::value && !is_floating_point::value; +}; +template +struct is_pointer: false_type {}; +template +struct is_pointer: true_type {}; +template +struct is_signed: integral_constant::type, char>::value || is_same::type, signed char>::value || is_same::type, signed short>::value || is_same::type, signed int>::value || is_same::type, signed long>::value || is_same::type, signed long long>::value || is_same::type, float>::value || is_same::type, double>::value> {}; +template +struct is_unsigned: integral_constant::type, unsigned char>::value || is_same::type, unsigned short>::value || is_same::type, unsigned int>::value || is_same::type, unsigned long>::value || is_same::type, unsigned long long>::value || is_same::type, bool>::value> {}; +template +struct type_identity { + typedef T type; +}; +template +struct make_unsigned; +template<> +struct make_unsigned: type_identity {}; +template<> +struct make_unsigned: type_identity {}; +template<> +struct make_unsigned: type_identity {}; +template<> +struct make_unsigned: type_identity {}; +template<> +struct make_unsigned: type_identity {}; +template<> +struct make_unsigned: type_identity {}; +template<> +struct make_unsigned: type_identity {}; +template<> +struct make_unsigned: type_identity {}; +template<> +struct make_unsigned: type_identity {}; +template<> +struct make_unsigned: type_identity {}; +template<> +struct make_unsigned: type_identity {}; +template +struct make_void { + typedef void type; +}; +template +struct remove_const { + typedef T type; +}; +template +struct remove_const { + typedef T type; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4310) +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct numeric_limits; +template +struct numeric_limits::value>::type> { + static constexpr T lowest() + { + return 0; + } + static constexpr T highest() + { + return T(-1); + } +}; +template +struct numeric_limits< + T, typename enable_if::value && is_signed::value>::type> { + static constexpr T lowest() + { + return T(T(1) << (sizeof(T) * 8 - 1)); + } + static constexpr T highest() + { + return T(~lowest()); + } +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#ifdef _MSC_VER +#pragma warning(pop) +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +struct StringNode { + using references_type = uint_t::type; + using length_type = uint_t::type; + struct StringNode* next; + references_type references; + length_type length; + char data[1]; + static constexpr size_t maxLength = numeric_limits::highest(); + static constexpr size_t sizeForLength(size_t n) + { + return n + 1 + offsetof(StringNode, data); + } + static StringNode* create(size_t length, Allocator* allocator) + { + if (length > maxLength) { + return nullptr; + } + auto node = reinterpret_cast( + allocator->allocate(sizeForLength(length))); + if (node) { + node->length = length_type(length); + node->references = 1; + } + return node; + } + static StringNode* resize(StringNode* node, size_t length, + Allocator* allocator) + { + ARDUINOJSON_ASSERT(node != nullptr); + StringNode* newNode; + if (length <= maxLength) { + newNode = reinterpret_cast( + allocator->reallocate(node, sizeForLength(length))); + } else { + newNode = nullptr; + } + if (newNode) { + newNode->length = length_type(length); + } else { + allocator->deallocate(node); + } + return newNode; + } + static void destroy(StringNode* node, Allocator* allocator) + { + allocator->deallocate(node); + } +}; +constexpr size_t sizeofString(size_t n) +{ + return StringNode::sizeForLength(n); +} +using nullptr_t = decltype(nullptr); +template +T&& forward(typename remove_reference::type& t) noexcept +{ + return static_cast(t); +} +template +typename remove_reference::type&& move(T&& t) +{ + return static_cast::type&&>(t); +} +template +void swap_(T& a, T& b) +{ + T tmp = move(a); + a = move(b); + b = move(tmp); +} +ARDUINOJSON_END_PRIVATE_NAMESPACE +#include +#ifdef _MSC_VER // Visual Studio +#define FORCE_INLINE // __forceinline causes C4714 when returning std::string +#ifndef ARDUINOJSON_DEPRECATED +#define ARDUINOJSON_DEPRECATED(msg) __declspec(deprecated(msg)) +#endif +#elif defined(__GNUC__) // GCC or Clang +#define FORCE_INLINE __attribute__((always_inline)) +#ifndef ARDUINOJSON_DEPRECATED +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +#define ARDUINOJSON_DEPRECATED(msg) __attribute__((deprecated(msg))) +#else +#define ARDUINOJSON_DEPRECATED(msg) __attribute__((deprecated)) +#endif +#endif +#else // Other compilers +#define FORCE_INLINE +#ifndef ARDUINOJSON_DEPRECATED +#define ARDUINOJSON_DEPRECATED(msg) +#endif +#endif +#if defined(__has_attribute) +#if __has_attribute(no_sanitize) +#define ARDUINOJSON_NO_SANITIZE(check) __attribute__((no_sanitize(check))) +#else +#define ARDUINOJSON_NO_SANITIZE(check) +#endif +#else +#define ARDUINOJSON_NO_SANITIZE(check) +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct StringAdapter; +template +struct SizedStringAdapter; +template +typename StringAdapter::AdaptedString adaptString(const TString& s) +{ + return StringAdapter::adapt(s); +} +template +typename StringAdapter::AdaptedString adaptString(TChar* p) +{ + return StringAdapter::adapt(p); +} +template +typename SizedStringAdapter::AdaptedString adaptString(TChar* p, + size_t n) +{ + return SizedStringAdapter::adapt(p, n); +} +template +struct IsChar: integral_constant::value && sizeof(T) == 1> {}; +class ZeroTerminatedRamString { +public: + static const size_t typeSortKey = 3; + ZeroTerminatedRamString(const char* str): + str_(str) + {} + bool isNull() const + { + return !str_; + } + FORCE_INLINE size_t size() const + { + return str_ ? ::strlen(str_) : 0; + } + char operator[](size_t i) const + { + ARDUINOJSON_ASSERT(str_ != 0); + ARDUINOJSON_ASSERT(i <= size()); + return str_[i]; + } + const char* data() const + { + return str_; + } + friend int stringCompare(ZeroTerminatedRamString a, + ZeroTerminatedRamString b) + { + ARDUINOJSON_ASSERT(!a.isNull()); + ARDUINOJSON_ASSERT(!b.isNull()); + return ::strcmp(a.str_, b.str_); + } + friend bool stringEquals(ZeroTerminatedRamString a, + ZeroTerminatedRamString b) + { + return stringCompare(a, b) == 0; + } + bool isLinked() const + { + return false; + } +protected: + const char* str_; +}; +template +struct StringAdapter::value>::type> { + typedef ZeroTerminatedRamString AdaptedString; + static AdaptedString adapt(const TChar* p) + { + return AdaptedString(reinterpret_cast(p)); + } +}; +template +struct StringAdapter::value>::type> { + typedef ZeroTerminatedRamString AdaptedString; + static AdaptedString adapt(const TChar* p) + { + return AdaptedString(reinterpret_cast(p)); + } +}; +class StaticStringAdapter: public ZeroTerminatedRamString { +public: + StaticStringAdapter(const char* str): + ZeroTerminatedRamString(str) + {} + bool isLinked() const + { + return true; + } +}; +template<> +struct StringAdapter { + typedef StaticStringAdapter AdaptedString; + static AdaptedString adapt(const char* p) + { + return AdaptedString(p); + } +}; +class SizedRamString { +public: + static const size_t typeSortKey = 2; + SizedRamString(const char* str, size_t sz): + str_(str), + size_(sz) + {} + bool isNull() const + { + return !str_; + } + size_t size() const + { + return size_; + } + char operator[](size_t i) const + { + ARDUINOJSON_ASSERT(str_ != 0); + ARDUINOJSON_ASSERT(i <= size()); + return str_[i]; + } + const char* data() const + { + return str_; + } + bool isLinked() const + { + return false; + } +protected: + const char* str_; + size_t size_; +}; +template +struct SizedStringAdapter::value>::type> { + typedef SizedRamString AdaptedString; + static AdaptedString adapt(const TChar* p, size_t n) + { + return AdaptedString(reinterpret_cast(p), n); + } +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#if ARDUINOJSON_ENABLE_STD_STREAM +#include +#endif +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class JsonString { +public: + enum Ownership { + Copied, + Linked + }; + JsonString(): + data_(0), + size_(0), + ownership_(Linked) + {} + JsonString(const char* data, Ownership ownership = Linked): + data_(data), + size_(data ? ::strlen(data) : 0), + ownership_(ownership) + {} + JsonString(const char* data, size_t sz, Ownership ownership = Linked): + data_(data), + size_(sz), + ownership_(ownership) + {} + const char* c_str() const + { + return data_; + } + bool isNull() const + { + return !data_; + } + bool isLinked() const + { + return ownership_ == Linked; + } + size_t size() const + { + return size_; + } + explicit operator bool() const + { + return data_ != 0; + } + friend bool operator==(JsonString lhs, JsonString rhs) + { + if (lhs.size_ != rhs.size_) { + return false; + } + if (lhs.data_ == rhs.data_) { + return true; + } + if (!lhs.data_) { + return false; + } + if (!rhs.data_) { + return false; + } + return memcmp(lhs.data_, rhs.data_, lhs.size_) == 0; + } + friend bool operator!=(JsonString lhs, JsonString rhs) + { + return !(lhs == rhs); + } +#if ARDUINOJSON_ENABLE_STD_STREAM + friend std::ostream& operator<<(std::ostream& lhs, const JsonString& rhs) + { + lhs.write(rhs.c_str(), static_cast(rhs.size())); + return lhs; + } +#endif +private: + const char* data_; + size_t size_; + Ownership ownership_; +}; +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +class JsonStringAdapter: public SizedRamString { +public: + JsonStringAdapter(const JsonString& s): + SizedRamString(s.c_str(), s.size()), + linked_(s.isLinked()) + {} + bool isLinked() const + { + return linked_; + } +private: + bool linked_; +}; +template<> +struct StringAdapter { + typedef JsonStringAdapter AdaptedString; + static AdaptedString adapt(const JsonString& s) + { + return AdaptedString(s); + } +}; +namespace string_traits_impl { + template + struct has_cstr: false_type {}; + template + struct has_cstr().c_str()), + const char*>::value>::type>: true_type {}; + template + struct has_data: false_type {}; + template + struct has_data().data()), + const char*>::value>::type>: true_type {}; + template + struct has_length: false_type {}; + template + struct has_length< + T, typename enable_if< + is_same().length()), size_t>::value>::type>: true_type {}; + template + struct has_size: false_type {}; + template + struct has_size< + T, typename enable_if< + is_same().size()), size_t>::value>::type>: true_type {}; +} // namespace string_traits_impl +template +struct string_traits { + enum { + has_cstr = string_traits_impl::has_cstr::value, + has_length = string_traits_impl::has_length::value, + has_data = string_traits_impl::has_data::value, + has_size = string_traits_impl::has_size::value + }; +}; +template +struct StringAdapter< + T, + typename enable_if< + (string_traits::has_cstr || string_traits::has_data) && (string_traits::has_length || string_traits::has_size)>::type> { + typedef SizedRamString AdaptedString; + static AdaptedString adapt(const T& s) + { + return AdaptedString(get_data(s), get_size(s)); + } +private: + template + static typename enable_if::has_size, size_t>::type get_size( + const U& s) + { + return s.size(); + } + template + static typename enable_if::has_size, size_t>::type get_size( + const U& s) + { + return s.length(); + } + template + static typename enable_if::has_data, const char*>::type + get_data(const U& s) + { + return s.data(); + } + template + static typename enable_if::has_data, const char*>::type + get_data(const U& s) + { + return s.c_str(); + } +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#if ARDUINOJSON_ENABLE_PROGMEM +#ifdef ARDUINO +#else +class __FlashStringHelper; +#include +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +struct pgm_p { + pgm_p(const void* p): + address(reinterpret_cast(p)) + {} + const char* address; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#ifndef strlen_P +inline size_t strlen_P(ArduinoJson::detail::pgm_p s) +{ + const char* p = s.address; + ARDUINOJSON_ASSERT(p != NULL); + while (pgm_read_byte(p)) { + p++; + } + return size_t(p - s.address); +} +#endif +#ifndef strncmp_P +inline int strncmp_P(const char* a, ArduinoJson::detail::pgm_p b, size_t n) +{ + const char* s1 = a; + const char* s2 = b.address; + ARDUINOJSON_ASSERT(s1 != NULL); + ARDUINOJSON_ASSERT(s2 != NULL); + while (n-- > 0) { + char c1 = *s1++; + char c2 = static_cast(pgm_read_byte(s2++)); + if (c1 < c2) { + return -1; + } + if (c1 > c2) { + return 1; + } + if (c1 == 0 /* and c2 as well */) { + return 0; + } + } + return 0; +} +#endif +#ifndef strcmp_P +inline int strcmp_P(const char* a, ArduinoJson::detail::pgm_p b) +{ + const char* s1 = a; + const char* s2 = b.address; + ARDUINOJSON_ASSERT(s1 != NULL); + ARDUINOJSON_ASSERT(s2 != NULL); + for (;;) { + char c1 = *s1++; + char c2 = static_cast(pgm_read_byte(s2++)); + if (c1 < c2) { + return -1; + } + if (c1 > c2) { + return 1; + } + if (c1 == 0 /* and c2 as well */) { + return 0; + } + } +} +#endif +#ifndef memcmp_P +inline int memcmp_P(const void* a, ArduinoJson::detail::pgm_p b, size_t n) +{ + const uint8_t* p1 = reinterpret_cast(a); + const char* p2 = b.address; + ARDUINOJSON_ASSERT(p1 != NULL); + ARDUINOJSON_ASSERT(p2 != NULL); + while (n-- > 0) { + uint8_t v1 = *p1++; + uint8_t v2 = pgm_read_byte(p2++); + if (v1 != v2) { + return v1 - v2; + } + } + return 0; +} +#endif +#ifndef memcpy_P +inline void* memcpy_P(void* dst, ArduinoJson::detail::pgm_p src, size_t n) +{ + uint8_t* d = reinterpret_cast(dst); + const char* s = src.address; + ARDUINOJSON_ASSERT(d != NULL); + ARDUINOJSON_ASSERT(s != NULL); + while (n-- > 0) { + *d++ = pgm_read_byte(s++); + } + return dst; +} +#endif +#ifndef pgm_read_dword +inline uint32_t pgm_read_dword(ArduinoJson::detail::pgm_p p) +{ + uint32_t result; + memcpy_P(&result, p.address, 4); + return result; +} +#endif +#ifndef pgm_read_float +inline float pgm_read_float(ArduinoJson::detail::pgm_p p) +{ + float result; + memcpy_P(&result, p.address, sizeof(float)); + return result; +} +#endif +#ifndef pgm_read_double +#if defined(__SIZEOF_DOUBLE__) && defined(__SIZEOF_FLOAT__) && __SIZEOF_DOUBLE__ == __SIZEOF_FLOAT__ +inline double pgm_read_double(ArduinoJson::detail::pgm_p p) +{ + return pgm_read_float(p.address); +} +#else +inline double pgm_read_double(ArduinoJson::detail::pgm_p p) +{ + double result; + memcpy_P(&result, p.address, sizeof(double)); + return result; +} +#endif +#endif +#ifndef pgm_read_ptr +inline void* pgm_read_ptr(ArduinoJson::detail::pgm_p p) +{ + void* result; + memcpy_P(&result, p.address, sizeof(result)); + return result; +} +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +class FlashString { +public: + static const size_t typeSortKey = 1; + FlashString(const __FlashStringHelper* str, size_t sz): + str_(reinterpret_cast(str)), + size_(sz) + {} + bool isNull() const + { + return !str_; + } + char operator[](size_t i) const + { + ARDUINOJSON_ASSERT(str_ != 0); + ARDUINOJSON_ASSERT(i <= size_); + return static_cast(pgm_read_byte(str_ + i)); + } + const char* data() const + { + return nullptr; + } + size_t size() const + { + return size_; + } + friend bool stringEquals(FlashString a, SizedRamString b) + { + ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey); + ARDUINOJSON_ASSERT(!a.isNull()); + ARDUINOJSON_ASSERT(!b.isNull()); + if (a.size() != b.size()) { + return false; + } + return ::memcmp_P(b.data(), a.str_, a.size_) == 0; + } + friend int stringCompare(FlashString a, SizedRamString b) + { + ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey); + ARDUINOJSON_ASSERT(!a.isNull()); + ARDUINOJSON_ASSERT(!b.isNull()); + size_t minsize = a.size() < b.size() ? a.size() : b.size(); + int res = ::memcmp_P(b.data(), a.str_, minsize); + if (res) { + return -res; + } + if (a.size() < b.size()) { + return -1; + } + if (a.size() > b.size()) { + return 1; + } + return 0; + } + friend void stringGetChars(FlashString s, char* p, size_t n) + { + ARDUINOJSON_ASSERT(s.size() <= n); + ::memcpy_P(p, s.str_, n); + } + bool isLinked() const + { + return false; + } +private: + const char* str_; + size_t size_; +}; +template<> +struct StringAdapter { + typedef FlashString AdaptedString; + static AdaptedString adapt(const __FlashStringHelper* s) + { + return AdaptedString(s, s ? strlen_P(reinterpret_cast(s)) : 0); + } +}; +template<> +struct SizedStringAdapter { + typedef FlashString AdaptedString; + static AdaptedString adapt(const __FlashStringHelper* s, size_t n) + { + return AdaptedString(s, n); + } +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +typename enable_if::type +stringCompare(TAdaptedString1 s1, TAdaptedString2 s2) +{ + ARDUINOJSON_ASSERT(!s1.isNull()); + ARDUINOJSON_ASSERT(!s2.isNull()); + size_t size1 = s1.size(); + size_t size2 = s2.size(); + size_t n = size1 < size2 ? size1 : size2; + for (size_t i = 0; i < n; i++) { + if (s1[i] != s2[i]) { + return s1[i] - s2[i]; + } + } + if (size1 < size2) { + return -1; + } + if (size1 > size2) { + return 1; + } + return 0; +} +template +typename enable_if< + (TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), int>::type +stringCompare(TAdaptedString1 s1, TAdaptedString2 s2) +{ + return -stringCompare(s2, s1); +} +template +typename enable_if::type +stringEquals(TAdaptedString1 s1, TAdaptedString2 s2) +{ + ARDUINOJSON_ASSERT(!s1.isNull()); + ARDUINOJSON_ASSERT(!s2.isNull()); + size_t size1 = s1.size(); + size_t size2 = s2.size(); + if (size1 != size2) { + return false; + } + for (size_t i = 0; i < size1; i++) { + if (s1[i] != s2[i]) { + return false; + } + } + return true; +} +template +typename enable_if< + (TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), bool>::type +stringEquals(TAdaptedString1 s1, TAdaptedString2 s2) +{ + return stringEquals(s2, s1); +} +template +static void stringGetChars(TAdaptedString s, char* p, size_t n) +{ + ARDUINOJSON_ASSERT(s.size() <= n); + for (size_t i = 0; i < n; i++) { + p[i] = s[i]; + } +} +class VariantSlot; +class VariantPool; +class StringPool { +public: + StringPool() = default; + StringPool(const StringPool&) = delete; + void operator=(StringPool&& src) = delete; + ~StringPool() + { + ARDUINOJSON_ASSERT(strings_ == nullptr); + } + friend void swap(StringPool& a, StringPool& b) + { + swap_(a.strings_, b.strings_); + } + void clear(Allocator* allocator) + { + while (strings_) { + auto node = strings_; + strings_ = node->next; + StringNode::destroy(node, allocator); + } + } + size_t size() const + { + size_t total = 0; + for (auto node = strings_; node; node = node->next) { + total += sizeofString(node->length); + } + return total; + } + template + StringNode* add(TAdaptedString str, Allocator* allocator) + { + ARDUINOJSON_ASSERT(str.isNull() == false); + auto node = get(str); + if (node) { + node->references++; + return node; + } + size_t n = str.size(); + node = StringNode::create(n, allocator); + if (!node) { + return nullptr; + } + stringGetChars(str, node->data, n); + node->data[n] = 0; // force NUL terminator + add(node); + return node; + } + void add(StringNode* node) + { + ARDUINOJSON_ASSERT(node != nullptr); + node->next = strings_; + strings_ = node; + } + template + StringNode* get(const TAdaptedString& str) const + { + for (auto node = strings_; node; node = node->next) { + if (stringEquals(str, adaptString(node->data, node->length))) { + return node; + } + } + return nullptr; + } + void dereference(const char* s, Allocator* allocator) + { + StringNode* prev = nullptr; + for (auto node = strings_; node; node = node->next) { + if (node->data == s) { + if (--node->references == 0) { + if (prev) { + prev->next = node->next; + } else { + strings_ = node->next; + } + StringNode::destroy(node, allocator); + } + return; + } + prev = node; + } + } +private: + StringNode* strings_ = nullptr; +}; +class VariantSlot; +using SlotId = uint_t::type; +using SlotCount = SlotId; +const SlotId NULL_SLOT = SlotId(-1); +class SlotWithId { +public: + SlotWithId(): + slot_(nullptr), + id_(NULL_SLOT) + {} + SlotWithId(VariantSlot* slot, SlotId id): + slot_(slot), + id_(id) + { + ARDUINOJSON_ASSERT((slot == nullptr) == (id == NULL_SLOT)); + } + SlotId id() const + { + return id_; + } + operator VariantSlot*() + { + return slot_; + } + VariantSlot* operator->() + { + ARDUINOJSON_ASSERT(slot_ != nullptr); + return slot_; + } +private: + VariantSlot* slot_; + SlotId id_; +}; +class VariantPool { +public: + void create(SlotCount cap, Allocator* allocator); + void destroy(Allocator* allocator); + SlotWithId allocSlot(); + VariantSlot* getSlot(SlotId id) const; + void clear(); + void shrinkToFit(Allocator*); + SlotCount usage() const; + static SlotCount bytesToSlots(size_t); + static size_t slotsToBytes(SlotCount); +private: + SlotCount capacity_; + SlotCount usage_; + VariantSlot* slots_; +}; +using PoolCount = SlotId; +class VariantPoolList { +public: + VariantPoolList() = default; + ~VariantPoolList() + { + ARDUINOJSON_ASSERT(count_ == 0); + } + friend void swap(VariantPoolList& a, VariantPoolList& b) + { + bool aUsedPreallocated = a.pools_ == a.preallocatedPools_; + bool bUsedPreallocated = b.pools_ == b.preallocatedPools_; + if (aUsedPreallocated && bUsedPreallocated) { + for (PoolCount i = 0; i < ARDUINOJSON_INITIAL_POOL_COUNT; i++) { + swap_(a.preallocatedPools_[i], b.preallocatedPools_[i]); + } + } else if (bUsedPreallocated) { + for (PoolCount i = 0; i < b.count_; i++) { + a.preallocatedPools_[i] = b.preallocatedPools_[i]; + } + b.pools_ = a.pools_; + a.pools_ = a.preallocatedPools_; + } else if (aUsedPreallocated) { + for (PoolCount i = 0; i < a.count_; i++) { + b.preallocatedPools_[i] = a.preallocatedPools_[i]; + } + a.pools_ = b.pools_; + b.pools_ = b.preallocatedPools_; + } else { + swap_(a.pools_, b.pools_); + } + swap_(a.count_, b.count_); + swap_(a.capacity_, b.capacity_); + swap_(a.freeList_, b.freeList_); + } + VariantPoolList& operator=(VariantPoolList&& src) + { + ARDUINOJSON_ASSERT(count_ == 0); + if (src.pools_ == src.preallocatedPools_) { + memcpy(preallocatedPools_, src.preallocatedPools_, + sizeof(preallocatedPools_)); + pools_ = preallocatedPools_; + } else { + pools_ = src.pools_; + src.pools_ = nullptr; + } + count_ = src.count_; + capacity_ = src.capacity_; + src.count_ = 0; + src.capacity_ = 0; + return *this; + } + SlotWithId allocSlot(Allocator* allocator) + { + if (freeList_ != NULL_SLOT) { + return allocFromFreeList(); + } + if (count_) { + auto slot = allocFromLastPool(); + if (slot) { + return slot; + } + } + auto pool = addPool(allocator); + if (!pool) { + return {}; + } + return allocFromLastPool(); + } + void freeSlot(SlotWithId slot); + VariantSlot* getSlot(SlotId id) const + { + if (id == NULL_SLOT) { + return nullptr; + } + auto poolIndex = SlotId(id / ARDUINOJSON_POOL_CAPACITY); + auto indexInPool = SlotId(id % ARDUINOJSON_POOL_CAPACITY); + ARDUINOJSON_ASSERT(poolIndex < count_); + return pools_[poolIndex].getSlot(indexInPool); + } + void clear(Allocator* allocator) + { + for (PoolCount i = 0; i < count_; i++) { + pools_[i].destroy(allocator); + } + count_ = 0; + freeList_ = NULL_SLOT; + if (pools_ != preallocatedPools_) { + allocator->deallocate(pools_); + pools_ = preallocatedPools_; + capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT; + } + } + SlotCount usage() const + { + SlotCount total = 0; + for (PoolCount i = 0; i < count_; i++) { + total = SlotCount(total + pools_[i].usage()); + } + return total; + } + void shrinkToFit(Allocator* allocator) + { + if (count_ > 0) { + pools_[count_ - 1].shrinkToFit(allocator); + } + if (pools_ != preallocatedPools_ && count_ != capacity_) { + pools_ = static_cast( + allocator->reallocate(pools_, count_ * sizeof(VariantPool))); + ARDUINOJSON_ASSERT(pools_ != nullptr); // realloc to smaller can't fail + capacity_ = count_; + } + } +private: + SlotWithId allocFromFreeList(); + SlotWithId allocFromLastPool() + { + ARDUINOJSON_ASSERT(count_ > 0); + auto poolIndex = SlotId(count_ - 1); + auto slot = pools_[poolIndex].allocSlot(); + if (!slot) { + return {}; + } + return {slot, SlotId(poolIndex * ARDUINOJSON_POOL_CAPACITY + slot.id())}; + } + VariantPool* addPool(Allocator* allocator) + { + if (count_ == capacity_ && !increaseCapacity(allocator)) { + return nullptr; + } + auto pool = &pools_[count_++]; + SlotCount poolCapacity = ARDUINOJSON_POOL_CAPACITY; + if (count_ == maxPools) { // last pool is smaller because of NULL_SLOT + poolCapacity--; + } + pool->create(poolCapacity, allocator); + return pool; + } + bool increaseCapacity(Allocator* allocator) + { + if (capacity_ == maxPools) { + return false; + } + void* newPools; + auto newCapacity = PoolCount(capacity_ * 2); + if (pools_ == preallocatedPools_) { + newPools = allocator->allocate(newCapacity * sizeof(VariantPool)); + if (!newPools) { + return false; + } + memcpy(newPools, preallocatedPools_, sizeof(preallocatedPools_)); + } else { + newPools = + allocator->reallocate(pools_, newCapacity * sizeof(VariantPool)); + if (!newPools) { + return false; + } + } + pools_ = static_cast(newPools); + capacity_ = newCapacity; + return true; + } + VariantPool preallocatedPools_[ARDUINOJSON_INITIAL_POOL_COUNT]; + VariantPool* pools_ = preallocatedPools_; + PoolCount count_ = 0; + PoolCount capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT; + SlotId freeList_ = NULL_SLOT; +public: + static const PoolCount maxPools = + PoolCount(NULL_SLOT / ARDUINOJSON_POOL_CAPACITY + 1); +}; +class VariantSlot; +class VariantPool; +class ResourceManager { +public: + ResourceManager(Allocator* allocator = DefaultAllocator::instance()): + allocator_(allocator), + overflowed_(false) + {} + ~ResourceManager() + { + stringPool_.clear(allocator_); + variantPools_.clear(allocator_); + } + ResourceManager(const ResourceManager&) = delete; + ResourceManager& operator=(const ResourceManager& src) = delete; + friend void swap(ResourceManager& a, ResourceManager& b) + { + swap(a.stringPool_, b.stringPool_); + swap(a.variantPools_, b.variantPools_); + swap_(a.allocator_, b.allocator_); + swap_(a.overflowed_, b.overflowed_); + } + Allocator* allocator() const + { + return allocator_; + } + size_t size() const + { + return VariantPool::slotsToBytes(variantPools_.usage()) + stringPool_.size(); + } + bool overflowed() const + { + return overflowed_; + } + SlotWithId allocSlot() + { + auto p = variantPools_.allocSlot(allocator_); + if (!p) { + overflowed_ = true; + } + return p; + } + void freeSlot(SlotWithId id) + { + variantPools_.freeSlot(id); + } + VariantSlot* getSlot(SlotId id) const + { + return variantPools_.getSlot(id); + } + template + StringNode* saveString(TAdaptedString str) + { + if (str.isNull()) { + return 0; + } + auto node = stringPool_.add(str, allocator_); + if (!node) { + overflowed_ = true; + } + return node; + } + void saveString(StringNode* node) + { + stringPool_.add(node); + } + template + StringNode* getString(const TAdaptedString& str) const + { + return stringPool_.get(str); + } + StringNode* createString(size_t length) + { + auto node = StringNode::create(length, allocator_); + if (!node) { + overflowed_ = true; + } + return node; + } + StringNode* resizeString(StringNode* node, size_t length) + { + node = StringNode::resize(node, length, allocator_); + if (!node) { + overflowed_ = true; + } + return node; + } + void destroyString(StringNode* node) + { + StringNode::destroy(node, allocator_); + } + void dereferenceString(const char* s) + { + stringPool_.dereference(s, allocator_); + } + void clear() + { + variantPools_.clear(allocator_); + overflowed_ = false; + stringPool_.clear(allocator_); + } + void shrinkToFit() + { + variantPools_.shrinkToFit(allocator_); + } +private: + Allocator* allocator_; + bool overflowed_; + StringPool stringPool_; + VariantPoolList variantPools_; +}; +template +struct IsString: false_type {}; +template +struct IsString< + T, typename make_void::AdaptedString>::type>: true_type {}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +template +class SerializedValue { +public: + explicit SerializedValue(T str): + str_(str) + {} + operator T() const + { + return str_; + } + const char* data() const + { + return str_.c_str(); + } + size_t size() const + { + return str_.length(); + } +private: + T str_; +}; +template +class SerializedValue { +public: + explicit SerializedValue(TChar* p, size_t n): + data_(p), + size_(n) + {} + operator TChar*() const + { + return data_; + } + TChar* data() const + { + return data_; + } + size_t size() const + { + return size_; + } +private: + TChar* data_; + size_t size_; +}; +using RawString = SerializedValue; +template +inline SerializedValue serialized(T str) +{ + return SerializedValue(str); +} +template +inline SerializedValue serialized(TChar* p) +{ + return SerializedValue(p, detail::adaptString(p).size()); +} +template +inline SerializedValue serialized(TChar* p, size_t n) +{ + return SerializedValue(p, n); +} +ARDUINOJSON_END_PUBLIC_NAMESPACE +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +#ifndef isnan +template +bool isnan(T x) +{ + return x != x; +} +#endif +#ifndef isinf +template +bool isinf(T x) +{ + return x != 0.0 && x * 2 == x; +} +#endif +template +struct alias_cast_t { + union { + F raw; + T data; + }; +}; +template +T alias_cast(F raw_data) +{ + alias_cast_t ac; + ac.raw = raw_data; + return ac.data; +} +ARDUINOJSON_END_PRIVATE_NAMESPACE +#if ARDUINOJSON_ENABLE_PROGMEM +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +#if ARDUINOJSON_ENABLE_PROGMEM +#ifndef ARDUINOJSON_DEFINE_PROGMEM_ARRAY +#define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, ...) \ + static type const name[] PROGMEM = __VA_ARGS__; +#endif +template +inline const T* pgm_read(const T* const* p) +{ + return reinterpret_cast(pgm_read_ptr(p)); +} +inline uint32_t pgm_read(const uint32_t* p) +{ + return pgm_read_dword(p); +} +inline double pgm_read(const double* p) +{ + return pgm_read_double(p); +} +inline float pgm_read(const float* p) +{ + return pgm_read_float(p); +} +#else +#ifndef ARDUINOJSON_DEFINE_PROGMEM_ARRAY +#define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, ...) \ + static type const name[] = __VA_ARGS__; +#endif +template +inline T pgm_read(const T* p) +{ + return *p; +} +#endif +template +class pgm_ptr { +public: + explicit pgm_ptr(const T* ptr): + ptr_(ptr) + {} + T operator[](intptr_t index) const + { + return pgm_read(ptr_ + index); + } +private: + const T* ptr_; +}; +template +struct FloatTraits {}; +template +struct FloatTraits { + typedef uint64_t mantissa_type; + static const short mantissa_bits = 52; + static const mantissa_type mantissa_max = + (mantissa_type(1) << mantissa_bits) - 1; + typedef int16_t exponent_type; + static const exponent_type exponent_max = 308; + static pgm_ptr positiveBinaryPowersOfTen() + { + ARDUINOJSON_DEFINE_PROGMEM_ARRAY( // + uint64_t, factors, + { + 0x4024000000000000, // 1e1 + 0x4059000000000000, // 1e2 + 0x40C3880000000000, // 1e4 + 0x4197D78400000000, // 1e8 + 0x4341C37937E08000, // 1e16 + 0x4693B8B5B5056E17, // 1e32 + 0x4D384F03E93FF9F5, // 1e64 + 0x5A827748F9301D32, // 1e128 + 0x75154FDD7F73BF3C, // 1e256 + }); + return pgm_ptr(reinterpret_cast(factors)); + } + static pgm_ptr negativeBinaryPowersOfTen() + { + ARDUINOJSON_DEFINE_PROGMEM_ARRAY( // + uint64_t, factors, + { + 0x3FB999999999999A, // 1e-1 + 0x3F847AE147AE147B, // 1e-2 + 0x3F1A36E2EB1C432D, // 1e-4 + 0x3E45798EE2308C3A, // 1e-8 + 0x3C9CD2B297D889BC, // 1e-16 + 0x3949F623D5A8A733, // 1e-32 + 0x32A50FFD44F4A73D, // 1e-64 + 0x255BBA08CF8C979D, // 1e-128 + 0x0AC8062864AC6F43 // 1e-256 + }); + return pgm_ptr(reinterpret_cast(factors)); + } + static T nan() + { + return forge(0x7ff8000000000000); + } + static T inf() + { + return forge(0x7ff0000000000000); + } + static T highest() + { + return forge(0x7FEFFFFFFFFFFFFF); + } + template // int64_t + static T highest_for( + typename enable_if::value && is_signed::value && sizeof(TOut) == 8, + signed>::type* = 0) + { + return forge(0x43DFFFFFFFFFFFFF); // 9.2233720368547748e+18 + } + template // uint64_t + static T highest_for( + typename enable_if::value && is_unsigned::value && sizeof(TOut) == 8, + unsigned>::type* = 0) + { + return forge(0x43EFFFFFFFFFFFFF); // 1.8446744073709549568e+19 + } + static T lowest() + { + return forge(0xFFEFFFFFFFFFFFFF); + } + static T forge(uint64_t bits) + { + return alias_cast(bits); + } +}; +template +struct FloatTraits { + typedef uint32_t mantissa_type; + static const short mantissa_bits = 23; + static const mantissa_type mantissa_max = + (mantissa_type(1) << mantissa_bits) - 1; + typedef int8_t exponent_type; + static const exponent_type exponent_max = 38; + static pgm_ptr positiveBinaryPowersOfTen() + { + ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors, + { + 0x41200000, // 1e1f + 0x42c80000, // 1e2f + 0x461c4000, // 1e4f + 0x4cbebc20, // 1e8f + 0x5a0e1bca, // 1e16f + 0x749dc5ae // 1e32f + }); + return pgm_ptr(reinterpret_cast(factors)); + } + static pgm_ptr negativeBinaryPowersOfTen() + { + ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors, + { + 0x3dcccccd, // 1e-1f + 0x3c23d70a, // 1e-2f + 0x38d1b717, // 1e-4f + 0x322bcc77, // 1e-8f + 0x24e69595, // 1e-16f + 0x0a4fb11f // 1e-32f + }); + return pgm_ptr(reinterpret_cast(factors)); + } + static T forge(uint32_t bits) + { + return alias_cast(bits); + } + static T nan() + { + return forge(0x7fc00000); + } + static T inf() + { + return forge(0x7f800000); + } + static T highest() + { + return forge(0x7f7fffff); + } + template // int32_t + static T highest_for( + typename enable_if::value && is_signed::value && sizeof(TOut) == 4, + signed>::type* = 0) + { + return forge(0x4EFFFFFF); // 2.14748352E9 + } + template // uint32_t + static T highest_for( + typename enable_if::value && is_unsigned::value && sizeof(TOut) == 4, + unsigned>::type* = 0) + { + return forge(0x4F7FFFFF); // 4.29496704E9 + } + template // int64_t + static T highest_for( + typename enable_if::value && is_signed::value && sizeof(TOut) == 8, + signed>::type* = 0) + { + return forge(0x5EFFFFFF); // 9.22337148709896192E18 + } + template // uint64_t + static T highest_for( + typename enable_if::value && is_unsigned::value && sizeof(TOut) == 8, + unsigned>::type* = 0) + { + return forge(0x5F7FFFFF); // 1.844674297419792384E19 + } + static T lowest() + { + return forge(0xFf7fffff); + } +}; +template +inline TFloat make_float(TFloat m, TExponent e) +{ + using traits = FloatTraits; + auto powersOfTen = e > 0 ? traits::positiveBinaryPowersOfTen() + : traits::negativeBinaryPowersOfTen(); + if (e <= 0) { + e = TExponent(-e); + } + for (uint8_t index = 0; e != 0; index++) { + if (e & 1) { + m *= powersOfTen[index]; + } + e >>= 1; + } + return m; +} +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +#if ARDUINOJSON_USE_DOUBLE +typedef double JsonFloat; +#else +typedef float JsonFloat; +#endif +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +typename enable_if::value && is_unsigned::value && is_integral::value && sizeof(TOut) <= sizeof(TIn), + bool>::type +canConvertNumber(TIn value) +{ + return value <= TIn(numeric_limits::highest()); +} +template +typename enable_if::value && is_unsigned::value && is_integral::value && sizeof(TIn) < sizeof(TOut), + bool>::type +canConvertNumber(TIn) +{ + return true; +} +template +typename enable_if::value && is_floating_point::value, + bool>::type +canConvertNumber(TIn) +{ + return true; +} +template +typename enable_if::value && is_signed::value && is_integral::value && is_signed::value && sizeof(TOut) < sizeof(TIn), + bool>::type +canConvertNumber(TIn value) +{ + return value >= TIn(numeric_limits::lowest()) && value <= TIn(numeric_limits::highest()); +} +template +typename enable_if::value && is_signed::value && is_integral::value && is_signed::value && sizeof(TIn) <= sizeof(TOut), + bool>::type +canConvertNumber(TIn) +{ + return true; +} +template +typename enable_if::value && is_signed::value && is_integral::value && is_unsigned::value && sizeof(TOut) >= sizeof(TIn), + bool>::type +canConvertNumber(TIn value) +{ + if (value < 0) { + return false; + } + return TOut(value) <= numeric_limits::highest(); +} +template +typename enable_if::value && is_signed::value && is_integral::value && is_unsigned::value && sizeof(TOut) < sizeof(TIn), + bool>::type +canConvertNumber(TIn value) +{ + if (value < 0) { + return false; + } + return value <= TIn(numeric_limits::highest()); +} +template +typename enable_if::value && is_integral::value && sizeof(TOut) < sizeof(TIn), + bool>::type +canConvertNumber(TIn value) +{ + return value >= numeric_limits::lowest() && value <= numeric_limits::highest(); +} +template +typename enable_if::value && is_integral::value && sizeof(TOut) >= sizeof(TIn), + bool>::type +canConvertNumber(TIn value) +{ + return value >= numeric_limits::lowest() && value <= FloatTraits::template highest_for(); +} +template +TOut convertNumber(TIn value) +{ + return canConvertNumber(value) ? TOut(value) : 0; +} +ARDUINOJSON_END_PRIVATE_NAMESPACE +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +class VariantData; +class VariantSlot; +class CollectionIterator { + friend class CollectionData; +public: + CollectionIterator(): + slot_(nullptr), + currentId_(NULL_SLOT) + {} + void next(const ResourceManager* resources); + bool done() const + { + return slot_ == nullptr; + } + bool operator==(const CollectionIterator& other) const + { + return slot_ == other.slot_; + } + bool operator!=(const CollectionIterator& other) const + { + return slot_ != other.slot_; + } + VariantData* operator->() + { + ARDUINOJSON_ASSERT(slot_ != nullptr); + return data(); + } + VariantData& operator*() + { + ARDUINOJSON_ASSERT(slot_ != nullptr); + return *data(); + } + const VariantData& operator*() const + { + ARDUINOJSON_ASSERT(slot_ != nullptr); + return *data(); + } + const char* key() const; + bool ownsKey() const; + void setKey(StringNode*); + void setKey(const char*); + VariantData* data() + { + return reinterpret_cast(slot_); + } + const VariantData* data() const + { + return reinterpret_cast(slot_); + } +private: + CollectionIterator(VariantSlot* slot, SlotId slotId); + VariantSlot* slot_; + SlotId currentId_, nextId_; +}; +class CollectionData { + SlotId head_ = NULL_SLOT; + SlotId tail_ = NULL_SLOT; +public: + static void* operator new(size_t, void* p) noexcept + { + return p; + } + static void operator delete(void*, void*) noexcept + {} + using iterator = CollectionIterator; + iterator createIterator(const ResourceManager* resources) const + { + return iterator(resources->getSlot(head_), head_); + } + size_t size(const ResourceManager*) const; + size_t nesting(const ResourceManager*) const; + void clear(ResourceManager* resources); + static void clear(CollectionData* collection, ResourceManager* resources) + { + if (!collection) { + return; + } + collection->clear(resources); + } + void remove(iterator it, ResourceManager* resources); + static void remove(CollectionData* collection, iterator it, + ResourceManager* resources) + { + if (collection) { + return collection->remove(it, resources); + } + } + SlotId head() const + { + return head_; + } +protected: + iterator addSlot(ResourceManager*); +private: + SlotWithId getPreviousSlot(VariantSlot*, const ResourceManager*) const; + void releaseSlot(SlotWithId, ResourceManager*); +}; +inline const VariantData* collectionToVariant( + const CollectionData* collection) +{ + const void* data = collection; // prevent warning cast-align + return reinterpret_cast(data); +} +inline VariantData* collectionToVariant(CollectionData* collection) +{ + void* data = collection; // prevent warning cast-align + return reinterpret_cast(data); +} +class ArrayData: public CollectionData { +public: + VariantData* addElement(ResourceManager* resources) + { + return addSlot(resources).data(); + } + static VariantData* addElement(ArrayData* array, ResourceManager* resources) + { + if (!array) { + return nullptr; + } + return array->addElement(resources); + } + VariantData* getOrAddElement(size_t index, ResourceManager* resources); + VariantData* getElement(size_t index, const ResourceManager* resources) const; + static VariantData* getElement(const ArrayData* array, size_t index, + const ResourceManager* resources) + { + if (!array) { + return nullptr; + } + return array->getElement(index, resources); + } + void removeElement(size_t index, ResourceManager* resources); + static void removeElement(ArrayData* array, size_t index, + ResourceManager* resources) + { + if (!array) { + return; + } + array->removeElement(index, resources); + } + bool copyFrom(const ArrayData& src, ResourceManager* resources); + static bool copy(ArrayData* dst, const ArrayData* src, + ResourceManager* resources) + { + if (!dst || !src) { + return false; + } + return dst->copyFrom(*src, resources); + } +private: + iterator at(size_t index, const ResourceManager* resources) const; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +#if ARDUINOJSON_USE_LONG_LONG +typedef int64_t JsonInteger; +typedef uint64_t JsonUInt; +#else +typedef long JsonInteger; +typedef unsigned long JsonUInt; +#endif +ARDUINOJSON_END_PUBLIC_NAMESPACE +#define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T) \ + static_assert(sizeof(T) <= sizeof(ArduinoJson::JsonInteger), \ + "To use 64-bit integers with ArduinoJson, you must set " \ + "ARDUINOJSON_USE_LONG_LONG to 1. See " \ + "https://arduinojson.org/v7/api/config/use_long_long/"); +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +class ObjectData: public CollectionData { +public: + VariantData* addMember(StringNode* key, ResourceManager* resources) + { + ARDUINOJSON_ASSERT(key != nullptr); + auto it = addSlot(resources); + if (it.done()) { + return nullptr; + } + it.setKey(key); + return it.data(); + } + template + VariantData* addMember(TAdaptedString key, ResourceManager* resources) + { + ARDUINOJSON_ASSERT(!key.isNull()); + if (key.isLinked()) { + auto it = addSlot(resources); + if (!it.done()) { + it.setKey(key.data()); + } + return it.data(); + } else { + auto storedKey = resources->saveString(key); + if (!storedKey) { + return nullptr; + } + auto it = addSlot(resources); + if (!it.done()) { + it.setKey(storedKey); + } + return it.data(); + } + } + template + VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources); + template + VariantData* getMember(TAdaptedString key, + const ResourceManager* resources) const; + template + static VariantData* getMember(const ObjectData* object, TAdaptedString key, + const ResourceManager* resources) + { + if (!object) { + return nullptr; + } + return object->getMember(key, resources); + } + template + void removeMember(TAdaptedString key, ResourceManager* resources); + template + static void removeMember(ObjectData* obj, TAdaptedString key, + ResourceManager* resources) + { + if (!obj) { + return; + } + obj->removeMember(key, resources); + } +private: + template + iterator findKey(TAdaptedString key, const ResourceManager* resources) const; +}; +enum { + VALUE_MASK = 0x7F, + OWNED_VALUE_BIT = 0x01, + VALUE_IS_NULL = 0, + VALUE_IS_RAW_STRING = 0x03, + VALUE_IS_LINKED_STRING = 0x04, + VALUE_IS_OWNED_STRING = 0x05, + VALUE_IS_BOOLEAN = 0x06, + NUMBER_BIT = 0x08, + VALUE_IS_UNSIGNED_INTEGER = 0x08, + VALUE_IS_SIGNED_INTEGER = 0x0A, + VALUE_IS_FLOAT = 0x0C, + COLLECTION_MASK = 0x60, + VALUE_IS_OBJECT = 0x20, + VALUE_IS_ARRAY = 0x40, + OWNED_KEY_BIT = 0x80 +}; +union VariantContent { + VariantContent() + {} + JsonFloat asFloat; + bool asBoolean; + JsonUInt asUnsignedInteger; + JsonInteger asSignedInteger; + ArrayData asArray; + ObjectData asObject; + CollectionData asCollection; + const char* asLinkedString; + struct StringNode* asOwnedString; +}; +struct StringNode; +class VariantSlot { + VariantContent content_; + uint8_t flags_; + SlotId next_; + const char* key_; +public: + static void* operator new(size_t, void* p) noexcept + { + return p; + } + static void operator delete(void*, void*) noexcept + {} + VariantSlot(): + flags_(0), + next_(NULL_SLOT), + key_(0) + {} + VariantData* data() + { + return reinterpret_cast(&content_); + } + const VariantData* data() const + { + return reinterpret_cast(&content_); + } + SlotId next() const + { + return next_; + } + void setNext(SlotId slot) + { + next_ = slot; + } + void setKey(const char* k) + { + ARDUINOJSON_ASSERT(k); + flags_ &= VALUE_MASK; + key_ = k; + } + void setKey(StringNode* k) + { + ARDUINOJSON_ASSERT(k); + flags_ |= OWNED_KEY_BIT; + key_ = k->data; + } + const char* key() const + { + return key_; + } + bool ownsKey() const + { + return (flags_ & OWNED_KEY_BIT) != 0; + } +}; +inline VariantData* slotData(VariantSlot* slot) +{ + return reinterpret_cast(slot); +} +constexpr size_t sizeofArray(size_t n) +{ + return n * sizeof(VariantSlot); +} +constexpr size_t sizeofObject(size_t n) +{ + return n * sizeof(VariantSlot); +} +template +T parseNumber(const char* s); +class VariantData { + VariantContent content_; // must be first to allow cast from array to variant + uint8_t flags_; +public: + VariantData(): + flags_(VALUE_IS_NULL) + {} + template + typename TVisitor::result_type accept(TVisitor& visit) const + { + switch (type()) { + case VALUE_IS_FLOAT: + return visit.visit(content_.asFloat); + case VALUE_IS_ARRAY: + return visit.visit(content_.asArray); + case VALUE_IS_OBJECT: + return visit.visit(content_.asObject); + case VALUE_IS_LINKED_STRING: + return visit.visit(JsonString(content_.asLinkedString)); + case VALUE_IS_OWNED_STRING: + return visit.visit(JsonString(content_.asOwnedString->data, + content_.asOwnedString->length, + JsonString::Copied)); + case VALUE_IS_RAW_STRING: + return visit.visit(RawString(content_.asOwnedString->data, + content_.asOwnedString->length)); + case VALUE_IS_SIGNED_INTEGER: + return visit.visit(content_.asSignedInteger); + case VALUE_IS_UNSIGNED_INTEGER: + return visit.visit(content_.asUnsignedInteger); + case VALUE_IS_BOOLEAN: + return visit.visit(content_.asBoolean != 0); + default: + return visit.visit(nullptr); + } + } + template + static typename TVisitor::result_type accept(const VariantData* var, + TVisitor& visit) + { + if (var != 0) { + return var->accept(visit); + } else { + return visit.visit(nullptr); + } + } + VariantData* addElement(ResourceManager* resources) + { + auto array = isNull() ? &toArray() : asArray(); + return detail::ArrayData::addElement(array, resources); + } + static VariantData* addElement(VariantData* var, ResourceManager* resources) + { + if (!var) { + return nullptr; + } + return var->addElement(resources); + } + bool asBoolean() const + { + switch (type()) { + case VALUE_IS_BOOLEAN: + return content_.asBoolean; + case VALUE_IS_SIGNED_INTEGER: + case VALUE_IS_UNSIGNED_INTEGER: + return content_.asUnsignedInteger != 0; + case VALUE_IS_FLOAT: + return content_.asFloat != 0; + case VALUE_IS_NULL: + return false; + default: + return true; + } + } + ArrayData* asArray() + { + return isArray() ? &content_.asArray : 0; + } + const ArrayData* asArray() const + { + return const_cast(this)->asArray(); + } + CollectionData* asCollection() + { + return isCollection() ? &content_.asCollection : 0; + } + const CollectionData* asCollection() const + { + return const_cast(this)->asCollection(); + } + template + T asFloat() const + { + static_assert(is_floating_point::value, "T must be a floating point"); + switch (type()) { + case VALUE_IS_BOOLEAN: + return static_cast(content_.asBoolean); + case VALUE_IS_UNSIGNED_INTEGER: + return static_cast(content_.asUnsignedInteger); + case VALUE_IS_SIGNED_INTEGER: + return static_cast(content_.asSignedInteger); + case VALUE_IS_LINKED_STRING: + case VALUE_IS_OWNED_STRING: + return parseNumber(content_.asOwnedString->data); + case VALUE_IS_FLOAT: + return static_cast(content_.asFloat); + default: + return 0; + } + } + template + T asIntegral() const + { + static_assert(is_integral::value, "T must be an integral type"); + switch (type()) { + case VALUE_IS_BOOLEAN: + return content_.asBoolean; + case VALUE_IS_UNSIGNED_INTEGER: + return convertNumber(content_.asUnsignedInteger); + case VALUE_IS_SIGNED_INTEGER: + return convertNumber(content_.asSignedInteger); + case VALUE_IS_LINKED_STRING: + return parseNumber(content_.asLinkedString); + case VALUE_IS_OWNED_STRING: + return parseNumber(content_.asOwnedString->data); + case VALUE_IS_FLOAT: + return convertNumber(content_.asFloat); + default: + return 0; + } + } + ObjectData* asObject() + { + return isObject() ? &content_.asObject : 0; + } + const ObjectData* asObject() const + { + return const_cast(this)->asObject(); + } + JsonString asRawString() const + { + switch (type()) { + case VALUE_IS_RAW_STRING: + return JsonString(content_.asOwnedString->data, + content_.asOwnedString->length, JsonString::Copied); + default: + return JsonString(); + } + } + JsonString asString() const + { + switch (type()) { + case VALUE_IS_LINKED_STRING: + return JsonString(content_.asLinkedString, JsonString::Linked); + case VALUE_IS_OWNED_STRING: + return JsonString(content_.asOwnedString->data, + content_.asOwnedString->length, JsonString::Copied); + default: + return JsonString(); + } + } + VariantData* getElement(size_t index, + const ResourceManager* resources) const + { + return ArrayData::getElement(asArray(), index, resources); + } + static VariantData* getElement(const VariantData* var, size_t index, + const ResourceManager* resources) + { + return var != 0 ? var->getElement(index, resources) : 0; + } + template + VariantData* getMember(TAdaptedString key, + const ResourceManager* resources) const + { + return ObjectData::getMember(asObject(), key, resources); + } + template + static VariantData* getMember(const VariantData* var, TAdaptedString key, + const ResourceManager* resources) + { + if (!var) { + return 0; + } + return var->getMember(key, resources); + } + VariantData* getOrAddElement(size_t index, ResourceManager* resources) + { + auto array = isNull() ? &toArray() : asArray(); + if (!array) { + return nullptr; + } + return array->getOrAddElement(index, resources); + } + template + VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources) + { + if (key.isNull()) { + return nullptr; + } + auto obj = isNull() ? &toObject() : asObject(); + if (!obj) { + return nullptr; + } + return obj->getOrAddMember(key, resources); + } + bool isArray() const + { + return (flags_ & VALUE_IS_ARRAY) != 0; + } + bool isBoolean() const + { + return type() == VALUE_IS_BOOLEAN; + } + bool isCollection() const + { + return (flags_ & COLLECTION_MASK) != 0; + } + bool isFloat() const + { + return (flags_ & NUMBER_BIT) != 0; + } + template + bool isInteger() const + { + switch (type()) { + case VALUE_IS_UNSIGNED_INTEGER: + return canConvertNumber(content_.asUnsignedInteger); + case VALUE_IS_SIGNED_INTEGER: + return canConvertNumber(content_.asSignedInteger); + default: + return false; + } + } + bool isNull() const + { + return type() == VALUE_IS_NULL; + } + static bool isNull(const VariantData* var) + { + if (!var) { + return true; + } + return var->isNull(); + } + bool isObject() const + { + return (flags_ & VALUE_IS_OBJECT) != 0; + } + bool isString() const + { + return type() == VALUE_IS_LINKED_STRING || type() == VALUE_IS_OWNED_STRING; + } + size_t nesting(const ResourceManager* resources) const + { + auto collection = asCollection(); + if (collection) { + return collection->nesting(resources); + } else { + return 0; + } + } + static size_t nesting(const VariantData* var, + const ResourceManager* resources) + { + if (!var) { + return 0; + } + return var->nesting(resources); + } + void removeElement(size_t index, ResourceManager* resources) + { + ArrayData::removeElement(asArray(), index, resources); + } + static void removeElement(VariantData* var, size_t index, + ResourceManager* resources) + { + if (!var) { + return; + } + var->removeElement(index, resources); + } + template + void removeMember(TAdaptedString key, ResourceManager* resources) + { + ObjectData::removeMember(asObject(), key, resources); + } + template + static void removeMember(VariantData* var, TAdaptedString key, + ResourceManager* resources) + { + if (!var) { + return; + } + var->removeMember(key, resources); + } + void reset() + { + flags_ = VALUE_IS_NULL; + } + void setBoolean(bool value) + { + setType(VALUE_IS_BOOLEAN); + content_.asBoolean = value; + } + void setBoolean(bool value, ResourceManager* resources) + { + release(resources); + setBoolean(value); + } + void setFloat(JsonFloat value) + { + setType(VALUE_IS_FLOAT); + content_.asFloat = value; + } + void setFloat(JsonFloat value, ResourceManager* resources) + { + release(resources); + setFloat(value); + } + template + typename enable_if::value>::type setInteger(T value) + { + setType(VALUE_IS_SIGNED_INTEGER); + content_.asSignedInteger = value; + } + template + typename enable_if::value>::type setInteger(T value) + { + setType(VALUE_IS_UNSIGNED_INTEGER); + content_.asUnsignedInteger = static_cast(value); + } + template + void setInteger(T value, ResourceManager* resources) + { + release(resources); + setInteger(value); + } + void setNull() + { + setType(VALUE_IS_NULL); + } + void setNull(ResourceManager* resources) + { + release(resources); + setNull(); + } + static void setNull(VariantData* var, ResourceManager* resources) + { + if (!var) { + return; + } + var->setNull(resources); + } + void setRawString(StringNode* s) + { + ARDUINOJSON_ASSERT(s); + setType(VALUE_IS_RAW_STRING); + content_.asOwnedString = s; + } + template + void setRawString(SerializedValue value, ResourceManager* resources) + { + release(resources); + auto dup = resources->saveString(adaptString(value.data(), value.size())); + if (dup) { + setRawString(dup); + } else { + setNull(); + } + } + template + static void setRawString(VariantData* var, SerializedValue value, + ResourceManager* resources) + { + if (!var) { + return; + } + var->setRawString(value, resources); + } + template + void setString(TAdaptedString value, ResourceManager* resources) + { + setNull(resources); + if (value.isNull()) { + return; + } + if (value.isLinked()) { + setLinkedString(value.data()); + return; + } + auto dup = resources->saveString(value); + if (dup) { + setOwnedString(dup); + } + } + template + static void setString(VariantData* var, TAdaptedString value, + ResourceManager* resources) + { + if (!var) { + return; + } + var->setString(value, resources); + } + void setLinkedString(const char* s) + { + ARDUINOJSON_ASSERT(s); + setType(VALUE_IS_LINKED_STRING); + content_.asLinkedString = s; + } + void setOwnedString(StringNode* s) + { + ARDUINOJSON_ASSERT(s); + setType(VALUE_IS_OWNED_STRING); + content_.asOwnedString = s; + } + size_t size(const ResourceManager* resources) const + { + return isCollection() ? content_.asCollection.size(resources) : 0; + } + static size_t size(const VariantData* var, const ResourceManager* resources) + { + return var != 0 ? var->size(resources) : 0; + } + ArrayData& toArray() + { + setType(VALUE_IS_ARRAY); + new (&content_.asArray) ArrayData(); + return content_.asArray; + } + ArrayData& toArray(ResourceManager* resources) + { + release(resources); + return toArray(); + } + static ArrayData* toArray(VariantData* var, ResourceManager* resources) + { + if (!var) { + return 0; + } + return &var->toArray(resources); + } + ObjectData& toObject() + { + setType(VALUE_IS_OBJECT); + new (&content_.asObject) ObjectData(); + return content_.asObject; + } + ObjectData& toObject(ResourceManager* resources) + { + release(resources); + return toObject(); + } + static ObjectData* toObject(VariantData* var, ResourceManager* resources) + { + if (!var) { + return 0; + } + return &var->toObject(resources); + } + uint8_t type() const + { + return flags_ & VALUE_MASK; + } +private: + void release(ResourceManager* resources) + { + if (flags_ & OWNED_VALUE_BIT) { + resources->dereferenceString(content_.asOwnedString->data); + } + auto collection = asCollection(); + if (collection) { + collection->clear(resources); + } + } + void setType(uint8_t t) + { + flags_ &= OWNED_KEY_BIT; + flags_ |= t; + } +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class JsonArray; +class JsonObject; +class JsonVariant; +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct VariantTo {}; +template<> +struct VariantTo { + typedef JsonArray type; +}; +template<> +struct VariantTo { + typedef JsonObject type; +}; +template<> +struct VariantTo { + typedef JsonVariant type; +}; +class VariantAttorney { +public: + template + static auto getResourceManager(TClient& client) + -> decltype(client.getResourceManager()) + { + return client.getResourceManager(); + } + template + static auto getData(TClient& client) -> decltype(client.getData()) + { + return client.getData(); + } + template + static VariantData* getOrCreateData(TClient& client) + { + return client.getOrCreateData(); + } +}; +enum CompareResult { + COMPARE_RESULT_DIFFER = 0, + COMPARE_RESULT_EQUAL = 1, + COMPARE_RESULT_GREATER = 2, + COMPARE_RESULT_LESS = 4, + COMPARE_RESULT_GREATER_OR_EQUAL = 3, + COMPARE_RESULT_LESS_OR_EQUAL = 5 +}; +template +CompareResult arithmeticCompare(const T& lhs, const T& rhs) +{ + if (lhs < rhs) { + return COMPARE_RESULT_LESS; + } else if (lhs > rhs) { + return COMPARE_RESULT_GREATER; + } else { + return COMPARE_RESULT_EQUAL; + } +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value && is_integral::value && sizeof(T1) < sizeof(T2)>::type* = 0) +{ + return arithmeticCompare(static_cast(lhs), rhs); +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value && is_integral::value && sizeof(T2) < sizeof(T1)>::type* = 0) +{ + return arithmeticCompare(lhs, static_cast(rhs)); +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value && is_integral::value && is_signed::value == is_signed::value && sizeof(T2) == sizeof(T1)>::type* = 0) +{ + return arithmeticCompare(lhs, static_cast(rhs)); +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value && is_integral::value && is_unsigned::value && is_signed::value && sizeof(T2) == sizeof(T1)>::type* = 0) +{ + if (rhs < 0) { + return COMPARE_RESULT_GREATER; + } + return arithmeticCompare(lhs, static_cast(rhs)); +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value && is_integral::value && is_signed::value && is_unsigned::value && sizeof(T2) == sizeof(T1)>::type* = 0) +{ + if (lhs < 0) { + return COMPARE_RESULT_LESS; + } + return arithmeticCompare(static_cast(lhs), rhs); +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value || is_floating_point::value>::type* = 0) +{ + return arithmeticCompare(static_cast(lhs), + static_cast(rhs)); +} +template +CompareResult arithmeticCompareNegateLeft( + JsonUInt, const T2&, + typename enable_if::value>::type* = 0) +{ + return COMPARE_RESULT_LESS; +} +template +CompareResult arithmeticCompareNegateLeft( + JsonUInt lhs, const T2& rhs, + typename enable_if::value>::type* = 0) +{ + if (rhs > 0) { + return COMPARE_RESULT_LESS; + } + return arithmeticCompare(-rhs, static_cast(lhs)); +} +template +CompareResult arithmeticCompareNegateRight( + const T1&, JsonUInt, + typename enable_if::value>::type* = 0) +{ + return COMPARE_RESULT_GREATER; +} +template +CompareResult arithmeticCompareNegateRight( + const T1& lhs, JsonUInt rhs, + typename enable_if::value>::type* = 0) +{ + if (lhs > 0) { + return COMPARE_RESULT_GREATER; + } + return arithmeticCompare(static_cast(rhs), -lhs); +} +struct VariantTag {}; +template +struct IsVariant: is_base_of {}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class JsonVariantConst; +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +CompareResult compare(JsonVariantConst lhs, + const T& rhs); // VariantCompare.cpp +struct VariantOperatorTag {}; +template +struct VariantOperators: VariantOperatorTag { + template + friend + typename enable_if::value && !is_array::value, T>::type + operator|(const TVariant& variant, const T& defaultValue) + { + if (variant.template is()) { + return variant.template as(); + } else { + return defaultValue; + } + } + friend const char* operator|(const TVariant& variant, + const char* defaultValue) + { + if (variant.template is()) { + return variant.template as(); + } else { + return defaultValue; + } + } + template + friend typename enable_if::value, JsonVariantConst>::type + operator|(const TVariant& variant, T defaultValue) + { + if (variant) { + return variant; + } else { + return defaultValue; + } + } + template + friend bool operator==(T* lhs, TVariant rhs) + { + return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; + } + template + friend bool operator==(const T& lhs, TVariant rhs) + { + return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; + } + template + friend bool operator==(TVariant lhs, T* rhs) + { + return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; + } + template + friend + typename enable_if::value, bool>::type + operator==(TVariant lhs, const T& rhs) + { + return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; + } + template + friend bool operator!=(T* lhs, TVariant rhs) + { + return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; + } + template + friend bool operator!=(const T& lhs, TVariant rhs) + { + return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; + } + template + friend bool operator!=(TVariant lhs, T* rhs) + { + return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; + } + template + friend + typename enable_if::value, bool>::type + operator!=(TVariant lhs, const T& rhs) + { + return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; + } + template + friend bool operator<(T* lhs, TVariant rhs) + { + return compare(rhs, lhs) == COMPARE_RESULT_GREATER; + } + template + friend bool operator<(const T& lhs, TVariant rhs) + { + return compare(rhs, lhs) == COMPARE_RESULT_GREATER; + } + template + friend bool operator<(TVariant lhs, T* rhs) + { + return compare(lhs, rhs) == COMPARE_RESULT_LESS; + } + template + friend + typename enable_if::value, bool>::type + operator<(TVariant lhs, const T& rhs) + { + return compare(lhs, rhs) == COMPARE_RESULT_LESS; + } + template + friend bool operator<=(T* lhs, TVariant rhs) + { + return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } + template + friend bool operator<=(const T& lhs, TVariant rhs) + { + return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } + template + friend bool operator<=(TVariant lhs, T* rhs) + { + return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend + typename enable_if::value, bool>::type + operator<=(TVariant lhs, const T& rhs) + { + return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend bool operator>(T* lhs, TVariant rhs) + { + return compare(rhs, lhs) == COMPARE_RESULT_LESS; + } + template + friend bool operator>(const T& lhs, TVariant rhs) + { + return compare(rhs, lhs) == COMPARE_RESULT_LESS; + } + template + friend bool operator>(TVariant lhs, T* rhs) + { + return compare(lhs, rhs) == COMPARE_RESULT_GREATER; + } + template + friend + typename enable_if::value, bool>::type + operator>(TVariant lhs, const T& rhs) + { + return compare(lhs, rhs) == COMPARE_RESULT_GREATER; + } + template + friend bool operator>=(T* lhs, TVariant rhs) + { + return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend bool operator>=(const T& lhs, TVariant rhs) + { + return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend bool operator>=(TVariant lhs, T* rhs) + { + return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } + template + friend + typename enable_if::value, bool>::type + operator>=(TVariant lhs, const T& rhs) + { + return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class JsonArray; +class JsonObject; +class JsonVariantConst: public detail::VariantTag, public detail::VariantOperators { + friend class detail::VariantAttorney; +public: + JsonVariantConst(): + data_(nullptr), + resources_(nullptr) + {} + explicit JsonVariantConst(const detail::VariantData* data, + const detail::ResourceManager* resources): + data_(data), + resources_(resources) + {} + bool isNull() const + { + return detail::VariantData::isNull(data_); + } + bool isUnbound() const + { + return !data_; + } + size_t nesting() const + { + return detail::VariantData::nesting(data_, resources_); + } + size_t size() const + { + return detail::VariantData::size(data_, resources_); + } + template + typename detail::enable_if::value && !detail::is_same::value, + T>::type + as() const + { + return Converter::fromJson(*this); + } + template + typename detail::enable_if::value && !detail::is_same::value, + bool>::type + is() const + { + return Converter::checkJson(*this); + } + template + operator T() const + { + return as(); + } + JsonVariantConst operator[](size_t index) const + { + return JsonVariantConst( + detail::VariantData::getElement(data_, index, resources_), resources_); + } + template + typename detail::enable_if::value, + JsonVariantConst>::type + operator[](const TString& key) const + { + return JsonVariantConst(detail::VariantData::getMember( + data_, detail::adaptString(key), resources_), + resources_); + } + template + typename detail::enable_if::value, + JsonVariantConst>::type + operator[](TChar* key) const + { + return JsonVariantConst(detail::VariantData::getMember( + data_, detail::adaptString(key), resources_), + resources_); + } + template + typename detail::enable_if::value, bool>::type + containsKey(const TString& key) const + { + return detail::VariantData::getMember(getData(), detail::adaptString(key), + resources_) + != 0; + } + template + typename detail::enable_if::value, bool>::type + containsKey(TChar* key) const + { + return detail::VariantData::getMember(getData(), detail::adaptString(key), + resources_) + != 0; + } + ARDUINOJSON_DEPRECATED("always returns zero") + size_t memoryUsage() const + { + return 0; + } +protected: + const detail::VariantData* getData() const + { + return data_; + } + const detail::ResourceManager* getResourceManager() const + { + return resources_; + } +private: + const detail::VariantData* data_; + const detail::ResourceManager* resources_; +}; +class JsonVariant; +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +class ElementProxy; +template +class MemberProxy; +template +class VariantRefBase: public VariantTag { + friend class VariantAttorney; +public: + void clear() const + { + VariantData::setNull(getOrCreateData(), getResourceManager()); + } + bool isNull() const + { + return VariantData::isNull(getData()); + } + bool isUnbound() const + { + return !getData(); + } + template + typename enable_if::value, T>::type as() + const + { + return Converter::fromJson(getVariantConst()); + } + template + typename enable_if::value, T>::type as() const; + template::value>::type> + operator T() const + { + return as(); + } + template + typename enable_if::value, JsonArray>::type to() const; + template + typename enable_if::value, JsonObject>::type to() + const; + template + typename enable_if::value, JsonVariant>::type to() + const; + template + FORCE_INLINE + typename enable_if::value, bool>::type + is() const; + template + FORCE_INLINE + typename enable_if::value, bool>::type + is() const + { + return Converter::checkJson(getVariantConst()); + } + template + bool set(const T& value) const; + template + bool set(T* value) const; + size_t size() const + { + return VariantData::size(getData(), getResourceManager()); + } + size_t nesting() const + { + return VariantData::nesting(getData(), getResourceManager()); + } + template + typename enable_if::value, T>::type add() const + { + return add().template to(); + } + template + typename enable_if::value, T>::type add() const; + template + bool add(const T& value) const + { + return add().set(value); + } + template + bool add(T* value) const + { + return add().set(value); + } + void remove(size_t index) const + { + VariantData::removeElement(getData(), index, getResourceManager()); + } + template + typename enable_if::value>::type remove(TChar* key) const + { + VariantData::removeMember(getData(), adaptString(key), + getResourceManager()); + } + template + typename enable_if::value>::type remove( + const TString& key) const + { + VariantData::removeMember(getData(), adaptString(key), + getResourceManager()); + } + ElementProxy operator[](size_t index) const; + template + typename enable_if::value, bool>::type containsKey( + const TString& key) const; + template + typename enable_if::value, bool>::type containsKey( + TChar* key) const; + template + FORCE_INLINE typename enable_if::value, + MemberProxy>::type + operator[](const TString& key) const; + template + FORCE_INLINE typename enable_if::value, + MemberProxy>::type + operator[](TChar* key) const; + ARDUINOJSON_DEPRECATED("use add() instead") + JsonVariant add() const; + ARDUINOJSON_DEPRECATED("use add() instead") + JsonArray createNestedArray() const; + template + ARDUINOJSON_DEPRECATED("use var[key].to() instead") + JsonArray createNestedArray(TChar* key) const; + template + ARDUINOJSON_DEPRECATED("use var[key].to() instead") + JsonArray createNestedArray(const TString& key) const; + ARDUINOJSON_DEPRECATED("use add() instead") + JsonObject createNestedObject() const; + template + ARDUINOJSON_DEPRECATED("use var[key].to() instead") + JsonObject createNestedObject(TChar* key) const; + template + ARDUINOJSON_DEPRECATED("use var[key].to() instead") + JsonObject createNestedObject(const TString& key) const; + ARDUINOJSON_DEPRECATED("always returns zero") + size_t memoryUsage() const + { + return 0; + } + ARDUINOJSON_DEPRECATED("performs a deep copy") + void shallowCopy(JsonVariantConst src) const + { + set(src); + } +private: + TDerived& derived() + { + return static_cast(*this); + } + const TDerived& derived() const + { + return static_cast(*this); + } + ResourceManager* getResourceManager() const + { + return VariantAttorney::getResourceManager(derived()); + } + VariantData* getData() const + { + return VariantAttorney::getData(derived()); + } + VariantData* getOrCreateData() const + { + return VariantAttorney::getOrCreateData(derived()); + } + FORCE_INLINE ArduinoJson::JsonVariant getVariant() const; + FORCE_INLINE ArduinoJson::JsonVariantConst getVariantConst() const + { + return ArduinoJson::JsonVariantConst(getData(), getResourceManager()); + } + ArduinoJson::JsonVariant getOrCreateVariant() const; +}; +template +class ElementProxy: public VariantRefBase>, public VariantOperators> { + friend class VariantAttorney; +public: + ElementProxy(TUpstream upstream, size_t index): + upstream_(upstream), + index_(index) + {} + ElementProxy(const ElementProxy& src): + upstream_(src.upstream_), + index_(src.index_) + {} + ElementProxy& operator=(const ElementProxy& src) + { + this->set(src); + return *this; + } + template + ElementProxy& operator=(const T& src) + { + this->set(src); + return *this; + } + template + ElementProxy& operator=(T* src) + { + this->set(src); + return *this; + } +private: + ResourceManager* getResourceManager() const + { + return VariantAttorney::getResourceManager(upstream_); + } + FORCE_INLINE VariantData* getData() const + { + return VariantData::getElement( + VariantAttorney::getData(upstream_), index_, + VariantAttorney::getResourceManager(upstream_)); + } + VariantData* getOrCreateData() const + { + auto data = VariantAttorney::getOrCreateData(upstream_); + if (!data) { + return nullptr; + } + return data->getOrAddElement( + index_, VariantAttorney::getResourceManager(upstream_)); + } + TUpstream upstream_; + size_t index_; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class JsonVariant: public detail::VariantRefBase, public detail::VariantOperators { + friend class detail::VariantAttorney; +public: + JsonVariant(): + data_(0), + resources_(0) + {} + JsonVariant(detail::VariantData* data, detail::ResourceManager* resources): + data_(data), + resources_(resources) + {} +private: + detail::ResourceManager* getResourceManager() const + { + return resources_; + } + detail::VariantData* getData() const + { + return data_; + } + detail::VariantData* getOrCreateData() const + { + return data_; + } + detail::VariantData* data_; + detail::ResourceManager* resources_; +}; +namespace detail { + bool copyVariant(JsonVariant dst, JsonVariantConst src); +} +template<> +struct Converter: private detail::VariantAttorney { + static void toJson(JsonVariantConst src, JsonVariant dst) + { + copyVariant(dst, src); + } + static JsonVariant fromJson(JsonVariant src) + { + return src; + } + static detail::InvalidConversion fromJson( + JsonVariantConst); + static bool checkJson(JsonVariant src) + { + auto data = getData(src); + return !!data; + } + static bool checkJson(JsonVariantConst) + { + return false; + } +}; +template<> +struct Converter: private detail::VariantAttorney { + static void toJson(JsonVariantConst src, JsonVariant dst) + { + copyVariant(dst, src); + } + static JsonVariantConst fromJson(JsonVariantConst src) + { + return JsonVariantConst(getData(src), getResourceManager(src)); + } + static bool checkJson(JsonVariantConst src) + { + auto data = getData(src); + return !!data; + } +}; +template +class Ptr { +public: + Ptr(T value): + value_(value) + {} + T* operator->() + { + return &value_; + } + T& operator*() + { + return value_; + } +private: + T value_; +}; +class JsonArrayIterator { + friend class JsonArray; +public: + JsonArrayIterator() + {} + explicit JsonArrayIterator(detail::ArrayData::iterator iterator, + detail::ResourceManager* resources): + iterator_(iterator), + resources_(resources) + {} + JsonVariant operator*() + { + return JsonVariant(iterator_.data(), resources_); + } + Ptr operator->() + { + return operator*(); + } + bool operator==(const JsonArrayIterator& other) const + { + return iterator_ == other.iterator_; + } + bool operator!=(const JsonArrayIterator& other) const + { + return iterator_ != other.iterator_; + } + JsonArrayIterator& operator++() + { + iterator_.next(resources_); + return *this; + } +private: + detail::ArrayData::iterator iterator_; + detail::ResourceManager* resources_; +}; +class JsonArrayConstIterator { + friend class JsonArray; +public: + JsonArrayConstIterator() + {} + explicit JsonArrayConstIterator(detail::ArrayData::iterator iterator, + const detail::ResourceManager* resources): + iterator_(iterator), + resources_(resources) + {} + JsonVariantConst operator*() const + { + return JsonVariantConst(iterator_.data(), resources_); + } + Ptr operator->() + { + return operator*(); + } + bool operator==(const JsonArrayConstIterator& other) const + { + return iterator_ == other.iterator_; + } + bool operator!=(const JsonArrayConstIterator& other) const + { + return iterator_ != other.iterator_; + } + JsonArrayConstIterator& operator++() + { + iterator_.next(resources_); + return *this; + } +private: + detail::ArrayData::iterator iterator_; + const detail::ResourceManager* resources_; +}; +class JsonObject; +class JsonArrayConst: public detail::VariantOperators { + friend class JsonArray; + friend class detail::VariantAttorney; +public: + typedef JsonArrayConstIterator iterator; + iterator begin() const + { + if (!data_) { + return iterator(); + } + return iterator(data_->createIterator(resources_), resources_); + } + iterator end() const + { + return iterator(); + } + JsonArrayConst(): + data_(0) + {} + JsonArrayConst(const detail::ArrayData* data, + const detail::ResourceManager* resources): + data_(data), + resources_(resources) + {} + JsonVariantConst operator[](size_t index) const + { + return JsonVariantConst( + detail::ArrayData::getElement(data_, index, resources_), resources_); + } + operator JsonVariantConst() const + { + return JsonVariantConst(getData(), resources_); + } + bool isNull() const + { + return data_ == 0; + } + operator bool() const + { + return data_ != 0; + } + size_t nesting() const + { + return detail::VariantData::nesting(getData(), resources_); + } + size_t size() const + { + return data_ ? data_->size(resources_) : 0; + } + ARDUINOJSON_DEPRECATED("always returns zero") + size_t memoryUsage() const + { + return 0; + } +private: + const detail::VariantData* getData() const + { + return collectionToVariant(data_); + } + const detail::ArrayData* data_; + const detail::ResourceManager* resources_; +}; +inline bool operator==(JsonArrayConst lhs, JsonArrayConst rhs) +{ + if (!lhs && !rhs) { + return true; + } + if (!lhs || !rhs) { + return false; + } + auto a = lhs.begin(); + auto b = rhs.begin(); + for (;;) { + if (a == b) { // same pointer or both null + return true; + } + if (a == lhs.end() || b == rhs.end()) { + return false; + } + if (*a != *b) { + return false; + } + ++a; + ++b; + } +} +class JsonObject; +class JsonArray: public detail::VariantOperators { + friend class detail::VariantAttorney; +public: + typedef JsonArrayIterator iterator; + JsonArray(): + data_(0), + resources_(0) + {} + JsonArray(detail::ArrayData* data, detail::ResourceManager* resources): + data_(data), + resources_(resources) + {} + operator JsonVariant() + { + void* data = data_; // prevent warning cast-align + return JsonVariant(reinterpret_cast(data), + resources_); + } + operator JsonArrayConst() const + { + return JsonArrayConst(data_, resources_); + } + template + typename detail::enable_if::value, T>::type + add() const + { + return add().to(); + } + template + typename detail::enable_if::value, T>::type + add() const + { + return JsonVariant(detail::ArrayData::addElement(data_, resources_), + resources_); + } + template + bool add(const T& value) const + { + return add().set(value); + } + template + bool add(T* value) const + { + return add().set(value); + } + iterator begin() const + { + if (!data_) { + return iterator(); + } + return iterator(data_->createIterator(resources_), resources_); + } + iterator end() const + { + return iterator(); + } + bool set(JsonArrayConst src) const + { + if (!data_) { + return false; + } + clear(); + for (auto element : src) { + if (!add(element)) { + return false; + } + } + return true; + } + void remove(iterator it) const + { + detail::ArrayData::remove(data_, it.iterator_, resources_); + } + void remove(size_t index) const + { + detail::ArrayData::removeElement(data_, index, resources_); + } + void clear() const + { + detail::ArrayData::clear(data_, resources_); + } + detail::ElementProxy operator[](size_t index) const + { + return {*this, index}; + } + operator JsonVariantConst() const + { + return JsonVariantConst(collectionToVariant(data_), resources_); + } + bool isNull() const + { + return data_ == 0; + } + operator bool() const + { + return data_ != 0; + } + size_t nesting() const + { + return detail::VariantData::nesting(collectionToVariant(data_), resources_); + } + size_t size() const + { + return data_ ? data_->size(resources_) : 0; + } + ARDUINOJSON_DEPRECATED("use add() instead") + JsonVariant add() const + { + return add(); + } + ARDUINOJSON_DEPRECATED("use add() instead") + JsonArray createNestedArray() const + { + return add(); + } + ARDUINOJSON_DEPRECATED("use add() instead") + JsonObject createNestedObject() const; + ARDUINOJSON_DEPRECATED("always returns zero") + size_t memoryUsage() const + { + return 0; + } +private: + detail::ResourceManager* getResourceManager() const + { + return resources_; + } + detail::VariantData* getData() const + { + return collectionToVariant(data_); + } + detail::VariantData* getOrCreateData() const + { + return collectionToVariant(data_); + } + detail::ArrayData* data_; + detail::ResourceManager* resources_; +}; +class JsonPair { +public: + JsonPair(detail::ObjectData::iterator iterator, + detail::ResourceManager* resources): + iterator_(iterator), + resources_(resources) + {} + JsonString key() const + { + if (!iterator_.done()) { + return JsonString(iterator_.key(), iterator_.ownsKey() + ? JsonString::Copied + : JsonString::Linked); + } else { + return JsonString(); + } + } + JsonVariant value() + { + return JsonVariant(iterator_.data(), resources_); + } +private: + detail::ObjectData::iterator iterator_; + detail::ResourceManager* resources_; +}; +class JsonPairConst { +public: + JsonPairConst(detail::ObjectData::iterator iterator, + const detail::ResourceManager* resources): + iterator_(iterator), + resources_(resources) + {} + JsonString key() const + { + if (!iterator_.done()) { + return JsonString(iterator_.key(), iterator_.ownsKey() + ? JsonString::Copied + : JsonString::Linked); + } else { + return JsonString(); + } + } + JsonVariantConst value() const + { + return JsonVariantConst(iterator_.data(), resources_); + } +private: + detail::ObjectData::iterator iterator_; + const detail::ResourceManager* resources_; +}; +class JsonObjectIterator { + friend class JsonObject; +public: + JsonObjectIterator() + {} + explicit JsonObjectIterator(detail::ObjectData::iterator iterator, + detail::ResourceManager* resources): + iterator_(iterator), + resources_(resources) + {} + JsonPair operator*() const + { + return JsonPair(iterator_, resources_); + } + Ptr operator->() + { + return operator*(); + } + bool operator==(const JsonObjectIterator& other) const + { + return iterator_ == other.iterator_; + } + bool operator!=(const JsonObjectIterator& other) const + { + return iterator_ != other.iterator_; + } + JsonObjectIterator& operator++() + { + iterator_.next(resources_); + return *this; + } +private: + detail::ObjectData::iterator iterator_; + detail::ResourceManager* resources_; +}; +class JsonObjectConstIterator { + friend class JsonObject; +public: + JsonObjectConstIterator() + {} + explicit JsonObjectConstIterator(detail::ObjectData::iterator iterator, + const detail::ResourceManager* resources): + iterator_(iterator), + resources_(resources) + {} + JsonPairConst operator*() const + { + return JsonPairConst(iterator_, resources_); + } + Ptr operator->() + { + return operator*(); + } + bool operator==(const JsonObjectConstIterator& other) const + { + return iterator_ == other.iterator_; + } + bool operator!=(const JsonObjectConstIterator& other) const + { + return iterator_ != other.iterator_; + } + JsonObjectConstIterator& operator++() + { + iterator_.next(resources_); + return *this; + } +private: + detail::ObjectData::iterator iterator_; + const detail::ResourceManager* resources_; +}; +class JsonObjectConst: public detail::VariantOperators { + friend class JsonObject; + friend class detail::VariantAttorney; +public: + typedef JsonObjectConstIterator iterator; + JsonObjectConst(): + data_(0) + {} + JsonObjectConst(const detail::ObjectData* data, + const detail::ResourceManager* resources): + data_(data), + resources_(resources) + {} + operator JsonVariantConst() const + { + return JsonVariantConst(getData(), resources_); + } + bool isNull() const + { + return data_ == 0; + } + operator bool() const + { + return data_ != 0; + } + size_t nesting() const + { + return detail::VariantData::nesting(getData(), resources_); + } + size_t size() const + { + return data_ ? data_->size(resources_) : 0; + } + iterator begin() const + { + if (!data_) { + return iterator(); + } + return iterator(data_->createIterator(resources_), resources_); + } + iterator end() const + { + return iterator(); + } + template + bool containsKey(const TString& key) const + { + return detail::ObjectData::getMember(data_, detail::adaptString(key), + resources_) + != 0; + } + template + bool containsKey(TChar* key) const + { + return detail::ObjectData::getMember(data_, detail::adaptString(key), + resources_) + != 0; + } + template + typename detail::enable_if::value, + JsonVariantConst>::type + operator[](const TString& key) const + { + return JsonVariantConst(detail::ObjectData::getMember( + data_, detail::adaptString(key), resources_), + resources_); + } + template + typename detail::enable_if::value, + JsonVariantConst>::type + operator[](TChar* key) const + { + return JsonVariantConst(detail::ObjectData::getMember( + data_, detail::adaptString(key), resources_), + resources_); + } + ARDUINOJSON_DEPRECATED("always returns zero") + size_t memoryUsage() const + { + return 0; + } +private: + const detail::VariantData* getData() const + { + return collectionToVariant(data_); + } + const detail::ObjectData* data_; + const detail::ResourceManager* resources_; +}; +inline bool operator==(JsonObjectConst lhs, JsonObjectConst rhs) +{ + if (!lhs && !rhs) { // both are null + return true; + } + if (!lhs || !rhs) { // only one is null + return false; + } + size_t count = 0; + for (auto kvp : lhs) { + auto rhsValue = rhs[kvp.key()]; + if (rhsValue.isUnbound()) { + return false; + } + if (kvp.value() != rhsValue) { + return false; + } + count++; + } + return count == rhs.size(); +} +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +class MemberProxy: public VariantRefBase>, public VariantOperators> { + friend class VariantAttorney; +public: + MemberProxy(TUpstream upstream, TStringRef key): + upstream_(upstream), + key_(key) + {} + MemberProxy(const MemberProxy& src): + upstream_(src.upstream_), + key_(src.key_) + {} + MemberProxy& operator=(const MemberProxy& src) + { + this->set(src); + return *this; + } + template + MemberProxy& operator=(const T& src) + { + this->set(src); + return *this; + } + template + MemberProxy& operator=(T* src) + { + this->set(src); + return *this; + } +private: + ResourceManager* getResourceManager() const + { + return VariantAttorney::getResourceManager(upstream_); + } + VariantData* getData() const + { + return VariantData::getMember( + VariantAttorney::getData(upstream_), adaptString(key_), + VariantAttorney::getResourceManager(upstream_)); + } + VariantData* getOrCreateData() const + { + auto data = VariantAttorney::getOrCreateData(upstream_); + if (!data) { + return nullptr; + } + return data->getOrAddMember(adaptString(key_), + VariantAttorney::getResourceManager(upstream_)); + } +private: + TUpstream upstream_; + TStringRef key_; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class JsonArray; +class JsonObject: public detail::VariantOperators { + friend class detail::VariantAttorney; +public: + typedef JsonObjectIterator iterator; + JsonObject(): + data_(0), + resources_(0) + {} + JsonObject(detail::ObjectData* data, detail::ResourceManager* resource): + data_(data), + resources_(resource) + {} + operator JsonVariant() const + { + void* data = data_; // prevent warning cast-align + return JsonVariant(reinterpret_cast(data), + resources_); + } + operator JsonObjectConst() const + { + return JsonObjectConst(data_, resources_); + } + operator JsonVariantConst() const + { + return JsonVariantConst(collectionToVariant(data_), resources_); + } + bool isNull() const + { + return data_ == 0; + } + operator bool() const + { + return data_ != 0; + } + size_t nesting() const + { + return detail::VariantData::nesting(collectionToVariant(data_), resources_); + } + size_t size() const + { + return data_ ? data_->size(resources_) : 0; + } + iterator begin() const + { + if (!data_) { + return iterator(); + } + return iterator(data_->createIterator(resources_), resources_); + } + iterator end() const + { + return iterator(); + } + void clear() const + { + detail::ObjectData::clear(data_, resources_); + } + bool set(JsonObjectConst src) + { + if (!data_ || !src.data_) { + return false; + } + clear(); + for (auto kvp : src) { + if (!operator[](kvp.key()).set(kvp.value())) { + return false; + } + } + return true; + } + template + typename detail::enable_if::value, + detail::MemberProxy>::type + operator[](const TString& key) const + { + return {*this, key}; + } + template + typename detail::enable_if::value, + detail::MemberProxy>::type + operator[](TChar* key) const + { + return {*this, key}; + } + FORCE_INLINE void remove(iterator it) const + { + detail::ObjectData::remove(data_, it.iterator_, resources_); + } + template + FORCE_INLINE void remove(const TString& key) const + { + detail::ObjectData::removeMember(data_, detail::adaptString(key), + resources_); + } + template + FORCE_INLINE void remove(TChar* key) const + { + detail::ObjectData::removeMember(data_, detail::adaptString(key), + resources_); + } + template + typename detail::enable_if::value, bool>::type + containsKey(const TString& key) const + { + return detail::ObjectData::getMember(data_, detail::adaptString(key), + resources_) + != 0; + } + template + typename detail::enable_if::value, bool>::type + containsKey(TChar* key) const + { + return detail::ObjectData::getMember(data_, detail::adaptString(key), + resources_) + != 0; + } + template + ARDUINOJSON_DEPRECATED("use obj[key].to() instead") + JsonArray createNestedArray(TChar* key) const + { + return operator[](key).template to(); + } + template + ARDUINOJSON_DEPRECATED("use obj[key].to() instead") + JsonArray createNestedArray(const TString& key) const + { + return operator[](key).template to(); + } + template + ARDUINOJSON_DEPRECATED("use obj[key].to() instead") + JsonObject createNestedObject(TChar* key) + { + return operator[](key).template to(); + } + template + ARDUINOJSON_DEPRECATED("use obj[key].to() instead") + JsonObject createNestedObject(const TString& key) + { + return operator[](key).template to(); + } + ARDUINOJSON_DEPRECATED("always returns zero") + size_t memoryUsage() const + { + return 0; + } +private: + detail::ResourceManager* getResourceManager() const + { + return resources_; + } + detail::VariantData* getData() const + { + return detail::collectionToVariant(data_); + } + detail::VariantData* getOrCreateData() const + { + return detail::collectionToVariant(data_); + } + detail::ObjectData* data_; + detail::ResourceManager* resources_; +}; +class JsonDocument: public detail::VariantOperators { + friend class detail::VariantAttorney; +public: + explicit JsonDocument(Allocator* alloc = detail::DefaultAllocator::instance()): + resources_(alloc) + {} + JsonDocument(const JsonDocument& src): + JsonDocument(src.allocator()) + { + set(src); + } + JsonDocument(JsonDocument&& src): + JsonDocument(detail::DefaultAllocator::instance()) + { + swap(*this, src); + } + template + JsonDocument(const T& src, + Allocator* alloc = detail::DefaultAllocator::instance(), + typename detail::enable_if< + detail::is_same::value || detail::is_same::value || detail::is_same::value || detail::is_same::value || detail::is_same::value || detail::is_same::value>::type* = 0): + JsonDocument(alloc) + { + set(src); + } + JsonDocument& operator=(JsonDocument src) + { + swap(*this, src); + return *this; + } + template + JsonDocument& operator=(const T& src) + { + set(src); + return *this; + } + Allocator* allocator() const + { + return resources_.allocator(); + } + void shrinkToFit() + { + resources_.shrinkToFit(); + } + template + T as() + { + return getVariant().template as(); + } + template + T as() const + { + return getVariant().template as(); + } + void clear() + { + resources_.clear(); + data_.reset(); + } + template + bool is() + { + return getVariant().template is(); + } + template + bool is() const + { + return getVariant().template is(); + } + bool isNull() const + { + return getVariant().isNull(); + } + bool overflowed() const + { + return resources_.overflowed(); + } + size_t nesting() const + { + return data_.nesting(&resources_); + } + size_t size() const + { + return data_.size(&resources_); + } + bool set(const JsonDocument& src) + { + return to().set(src.as()); + } + template + typename detail::enable_if::value, + bool>::type + set(const T& src) + { + return to().set(src); + } + template + typename detail::VariantTo::type to() + { + clear(); + return getVariant().template to(); + } + template + bool containsKey(TChar* key) const + { + return data_.getMember(detail::adaptString(key), &resources_) != 0; + } + template + bool containsKey(const TString& key) const + { + return data_.getMember(detail::adaptString(key), &resources_) != 0; + } + template + typename detail::enable_if::value, + detail::MemberProxy>::type + operator[](const TString& key) + { + return {*this, key}; + } + template + typename detail::enable_if::value, + detail::MemberProxy>::type + operator[](TChar* key) + { + return {*this, key}; + } + template + typename detail::enable_if::value, + JsonVariantConst>::type + operator[](const TString& key) const + { + return JsonVariantConst( + data_.getMember(detail::adaptString(key), &resources_), &resources_); + } + template + typename detail::enable_if::value, + JsonVariantConst>::type + operator[](TChar* key) const + { + return JsonVariantConst( + data_.getMember(detail::adaptString(key), &resources_), &resources_); + } + detail::ElementProxy operator[](size_t index) + { + return {*this, index}; + } + JsonVariantConst operator[](size_t index) const + { + return JsonVariantConst(data_.getElement(index, &resources_), &resources_); + } + template + typename detail::enable_if::value, T>::type + add() + { + return add().to(); + } + template + typename detail::enable_if::value, T>::type + add() + { + return JsonVariant(data_.addElement(&resources_), &resources_); + } + template + bool add(const TValue& value) + { + return add().set(value); + } + template + bool add(TChar* value) + { + return add().set(value); + } + void remove(size_t index) + { + detail::VariantData::removeElement(getData(), index, getResourceManager()); + } + template + typename detail::enable_if::value>::type remove( + TChar* key) + { + detail::VariantData::removeMember(getData(), detail::adaptString(key), + getResourceManager()); + } + template + typename detail::enable_if::value>::type remove( + const TString& key) + { + detail::VariantData::removeMember(getData(), detail::adaptString(key), + getResourceManager()); + } + operator JsonVariant() + { + return getVariant(); + } + operator JsonVariantConst() const + { + return getVariant(); + } + friend void swap(JsonDocument& a, JsonDocument& b) + { + swap(a.resources_, b.resources_); + swap_(a.data_, b.data_); + } + ARDUINOJSON_DEPRECATED("use add() instead") + JsonVariant add() + { + return add(); + } + ARDUINOJSON_DEPRECATED("use add() instead") + JsonArray createNestedArray() + { + return add(); + } + template + ARDUINOJSON_DEPRECATED("use doc[key].to() instead") + JsonArray createNestedArray(TChar* key) + { + return operator[](key).template to(); + } + template + ARDUINOJSON_DEPRECATED("use doc[key].to() instead") + JsonArray createNestedArray(const TString& key) + { + return operator[](key).template to(); + } + ARDUINOJSON_DEPRECATED("use add() instead") + JsonObject createNestedObject() + { + return add(); + } + template + ARDUINOJSON_DEPRECATED("use doc[key].to() instead") + JsonObject createNestedObject(TChar* key) + { + return operator[](key).template to(); + } + template + ARDUINOJSON_DEPRECATED("use doc[key].to() instead") + JsonObject createNestedObject(const TString& key) + { + return operator[](key).template to(); + } + ARDUINOJSON_DEPRECATED("always returns zero") + size_t memoryUsage() const + { + return 0; + } +private: + JsonVariant getVariant() + { + return JsonVariant(&data_, &resources_); + } + JsonVariantConst getVariant() const + { + return JsonVariantConst(&data_, &resources_); + } + detail::ResourceManager* getResourceManager() + { + return &resources_; + } + detail::VariantData* getData() + { + return &data_; + } + const detail::VariantData* getData() const + { + return &data_; + } + detail::VariantData* getOrCreateData() + { + return &data_; + } + detail::ResourceManager resources_; + detail::VariantData data_; +}; +inline void convertToJson(const JsonDocument& src, JsonVariant dst) +{ + dst.set(src.as()); +} +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct VariantDataVisitor { + typedef TResult result_type; + template + TResult visit(const T&) + { + return TResult(); + } +}; +template +struct JsonVariantVisitor { + typedef TResult result_type; + template + TResult visit(const T&) + { + return TResult(); + } +}; +template +class VisitorAdapter { +public: + using result_type = typename TVisitor::result_type; + VisitorAdapter(TVisitor& visitor, const ResourceManager* resources): + visitor_(&visitor), + resources_(resources) + {} + result_type visit(const ArrayData& value) + { + return visitor_->visit(JsonArrayConst(&value, resources_)); + } + result_type visit(const ObjectData& value) + { + return visitor_->visit(JsonObjectConst(&value, resources_)); + } + template + result_type visit(const T& value) + { + return visitor_->visit(value); + } +private: + TVisitor* visitor_; + const ResourceManager* resources_; +}; +template +typename TVisitor::result_type accept(JsonVariantConst variant, + TVisitor& visit) +{ + auto data = VariantAttorney::getData(variant); + if (!data) { + return visit.visit(nullptr); + } + auto resources = VariantAttorney::getResourceManager(variant); + VisitorAdapter adapter(visit, resources); + return data->accept(adapter); +} +struct ComparerBase: JsonVariantVisitor {}; +template +struct Comparer; +template +struct Comparer::value>::type>: ComparerBase { + T rhs; // TODO: store adapted string? + explicit Comparer(T value): + rhs(value) + {} + CompareResult visit(JsonString lhs) + { + int i = stringCompare(adaptString(rhs), adaptString(lhs)); + if (i < 0) { + return COMPARE_RESULT_GREATER; + } else if (i > 0) { + return COMPARE_RESULT_LESS; + } else { + return COMPARE_RESULT_EQUAL; + } + } + CompareResult visit(nullptr_t) + { + if (adaptString(rhs).isNull()) { + return COMPARE_RESULT_EQUAL; + } else { + return COMPARE_RESULT_DIFFER; + } + } + using ComparerBase::visit; +}; +template +struct Comparer::value || is_floating_point::value>::type>: ComparerBase { + T rhs; + explicit Comparer(T value): + rhs(value) + {} + CompareResult visit(JsonFloat lhs) + { + return arithmeticCompare(lhs, rhs); + } + CompareResult visit(JsonInteger lhs) + { + return arithmeticCompare(lhs, rhs); + } + CompareResult visit(JsonUInt lhs) + { + return arithmeticCompare(lhs, rhs); + } + CompareResult visit(bool lhs) + { + return visit(static_cast(lhs)); + } + using ComparerBase::visit; +}; +struct NullComparer: ComparerBase { + CompareResult visit(nullptr_t) + { + return COMPARE_RESULT_EQUAL; + } + using ComparerBase::visit; +}; +template<> +struct Comparer: NullComparer { + explicit Comparer(nullptr_t): + NullComparer() + {} +}; +struct ArrayComparer: ComparerBase { + JsonArrayConst rhs_; + explicit ArrayComparer(JsonArrayConst rhs): + rhs_(rhs) + {} + CompareResult visit(JsonArrayConst lhs) + { + if (rhs_ == lhs) { + return COMPARE_RESULT_EQUAL; + } else { + return COMPARE_RESULT_DIFFER; + } + } + using ComparerBase::visit; +}; +struct ObjectComparer: ComparerBase { + JsonObjectConst rhs_; + explicit ObjectComparer(JsonObjectConst rhs): + rhs_(rhs) + {} + CompareResult visit(JsonObjectConst lhs) + { + if (lhs == rhs_) { + return COMPARE_RESULT_EQUAL; + } else { + return COMPARE_RESULT_DIFFER; + } + } + using ComparerBase::visit; +}; +struct RawComparer: ComparerBase { + RawString rhs_; + explicit RawComparer(RawString rhs): + rhs_(rhs) + {} + CompareResult visit(RawString lhs) + { + size_t size = rhs_.size() < lhs.size() ? rhs_.size() : lhs.size(); + int n = memcmp(lhs.data(), rhs_.data(), size); + if (n < 0) { + return COMPARE_RESULT_LESS; + } else if (n > 0) { + return COMPARE_RESULT_GREATER; + } else { + return COMPARE_RESULT_EQUAL; + } + } + using ComparerBase::visit; +}; +struct VariantComparer: ComparerBase { + JsonVariantConst rhs; + explicit VariantComparer(JsonVariantConst value): + rhs(value) + {} + CompareResult visit(JsonArrayConst lhs) + { + ArrayComparer comparer(lhs); + return reverseResult(comparer); + } + CompareResult visit(JsonObjectConst lhs) + { + ObjectComparer comparer(lhs); + return reverseResult(comparer); + } + CompareResult visit(JsonFloat lhs) + { + Comparer comparer(lhs); + return reverseResult(comparer); + } + CompareResult visit(JsonString lhs) + { + Comparer comparer(lhs); + return reverseResult(comparer); + } + CompareResult visit(RawString value) + { + RawComparer comparer(value); + return reverseResult(comparer); + } + CompareResult visit(JsonInteger lhs) + { + Comparer comparer(lhs); + return reverseResult(comparer); + } + CompareResult visit(JsonUInt lhs) + { + Comparer comparer(lhs); + return reverseResult(comparer); + } + CompareResult visit(bool lhs) + { + Comparer comparer(lhs); + return reverseResult(comparer); + } + CompareResult visit(nullptr_t) + { + NullComparer comparer; + return reverseResult(comparer); + } +private: + template + CompareResult reverseResult(TComparer& comparer) + { + CompareResult reversedResult = accept(rhs, comparer); + switch (reversedResult) { + case COMPARE_RESULT_GREATER: + return COMPARE_RESULT_LESS; + case COMPARE_RESULT_LESS: + return COMPARE_RESULT_GREATER; + default: + return reversedResult; + } + } +}; +template +struct Comparer::value>::type>: VariantComparer { + explicit Comparer(const T& value): + VariantComparer(static_cast(value)) + {} +}; +template +CompareResult compare(ArduinoJson::JsonVariantConst lhs, const T& rhs) +{ + Comparer comparer(rhs); + return accept(lhs, comparer); +} +inline ArrayData::iterator ArrayData::at( + size_t index, const ResourceManager* resources) const +{ + auto it = createIterator(resources); + while (!it.done() && index) { + it.next(resources); + --index; + } + return it; +} +inline VariantData* ArrayData::getOrAddElement(size_t index, + ResourceManager* resources) +{ + auto it = createIterator(resources); + while (!it.done() && index > 0) { + it.next(resources); + index--; + } + if (it.done()) { + index++; + } + VariantData* element = it.data(); + while (index > 0) { + element = addElement(resources); + if (!element) { + return nullptr; + } + index--; + } + return element; +} +inline VariantData* ArrayData::getElement( + size_t index, const ResourceManager* resources) const +{ + return at(index, resources).data(); +} +inline void ArrayData::removeElement(size_t index, ResourceManager* resources) +{ + remove(at(index, resources), resources); +} +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +template +inline typename detail::enable_if::value, bool>::type +copyArray(const T& src, JsonVariant dst) +{ + return dst.set(src); +} +template +inline typename detail::enable_if< + !detail::is_base_of::value, bool>::type +copyArray(T (&src)[N], const TDestination& dst) +{ + return copyArray(src, N, dst); +} +template +inline typename detail::enable_if< + !detail::is_base_of::value, bool>::type +copyArray(const T* src, size_t len, const TDestination& dst) +{ + bool ok = true; + for (size_t i = 0; i < len; i++) { + ok &= copyArray(src[i], dst.template add()); + } + return ok; +} +template +inline bool copyArray(const char* src, size_t, const TDestination& dst) +{ + return dst.set(src); +} +template +inline bool copyArray(const T& src, JsonDocument& dst) +{ + return copyArray(src, dst.to()); +} +template +inline bool copyArray(const T* src, size_t len, JsonDocument& dst) +{ + return copyArray(src, len, dst.to()); +} +template +inline typename detail::enable_if::value, size_t>::type +copyArray(JsonVariantConst src, T& dst) +{ + dst = src.as(); + return 1; +} +template +inline size_t copyArray(JsonArrayConst src, T (&dst)[N]) +{ + return copyArray(src, dst, N); +} +template +inline size_t copyArray(JsonArrayConst src, T* dst, size_t len) +{ + size_t i = 0; + for (JsonArrayConst::iterator it = src.begin(); it != src.end() && i < len; + ++it) { + copyArray(*it, dst[i++]); + } + return i; +} +template +inline size_t copyArray(JsonVariantConst src, char (&dst)[N]) +{ + JsonString s = src; + size_t len = N - 1; + if (len > s.size()) { + len = s.size(); + } + memcpy(dst, s.c_str(), len); + dst[len] = 0; + return 1; +} +template +inline typename detail::enable_if< + detail::is_array::value && detail::is_base_of::value, + size_t>::type +copyArray(const TSource& src, T& dst) +{ + return copyArray(src.template as(), dst); +} +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +#if ARDUINOJSON_ENABLE_ALIGNMENT +inline bool isAligned(size_t value) +{ + const size_t mask = sizeof(void*) - 1; + size_t addr = value; + return (addr & mask) == 0; +} +inline size_t addPadding(size_t bytes) +{ + const size_t mask = sizeof(void*) - 1; + return (bytes + mask) & ~mask; +} +template +struct AddPadding { + static const size_t mask = sizeof(void*) - 1; + static const size_t value = (bytes + mask) & ~mask; +}; +#else +inline bool isAligned(size_t) +{ + return true; +} +inline size_t addPadding(size_t bytes) +{ + return bytes; +} +template +struct AddPadding { + static const size_t value = bytes; +}; +#endif +template +inline bool isAligned(T* ptr) +{ + return isAligned(reinterpret_cast(ptr)); +} +template +inline T* addPadding(T* p) +{ + size_t address = addPadding(reinterpret_cast(p)); + return reinterpret_cast(address); +} +inline CollectionIterator::CollectionIterator(VariantSlot* slot, SlotId slotId): + slot_(slot), + currentId_(slotId) +{ + nextId_ = slot_ ? slot_->next() : NULL_SLOT; +} +inline const char* CollectionIterator::key() const +{ + ARDUINOJSON_ASSERT(slot_ != nullptr); + return slot_->key(); +} +inline void CollectionIterator::setKey(const char* s) +{ + ARDUINOJSON_ASSERT(slot_ != nullptr); + ARDUINOJSON_ASSERT(s != nullptr); + return slot_->setKey(s); +} +inline void CollectionIterator::setKey(StringNode* s) +{ + ARDUINOJSON_ASSERT(slot_ != nullptr); + ARDUINOJSON_ASSERT(s != nullptr); + return slot_->setKey(s); +} +inline bool CollectionIterator::ownsKey() const +{ + ARDUINOJSON_ASSERT(slot_ != nullptr); + return slot_->ownsKey(); +} +inline void CollectionIterator::next(const ResourceManager* resources) +{ + ARDUINOJSON_ASSERT(currentId_ != NULL_SLOT); + slot_ = resources->getSlot(nextId_); + currentId_ = nextId_; + if (slot_) { + nextId_ = slot_->next(); + } +} +inline CollectionData::iterator CollectionData::addSlot( + ResourceManager* resources) +{ + auto slot = resources->allocSlot(); + if (!slot) { + return {}; + } + if (tail_ != NULL_SLOT) { + auto tail = resources->getSlot(tail_); + tail->setNext(slot.id()); + tail_ = slot.id(); + } else { + head_ = slot.id(); + tail_ = slot.id(); + } + return iterator(slot, slot.id()); +} +inline void CollectionData::clear(ResourceManager* resources) +{ + auto next = head_; + while (next != NULL_SLOT) { + auto currId = next; + auto slot = resources->getSlot(next); + next = slot->next(); + releaseSlot(SlotWithId(slot, currId), resources); + } + head_ = NULL_SLOT; + tail_ = NULL_SLOT; +} +inline SlotWithId CollectionData::getPreviousSlot( + VariantSlot* target, const ResourceManager* resources) const +{ + auto prev = SlotWithId(); + auto currentId = head_; + while (currentId != NULL_SLOT) { + auto currentSlot = resources->getSlot(currentId); + if (currentSlot == target) { + return prev; + } + prev = SlotWithId(currentSlot, currentId); + currentId = currentSlot->next(); + } + return SlotWithId(); +} +inline void CollectionData::remove(iterator it, ResourceManager* resources) +{ + if (it.done()) { + return; + } + auto curr = it.slot_; + auto prev = getPreviousSlot(curr, resources); + auto next = curr->next(); + if (prev) { + prev->setNext(next); + } else { + head_ = next; + } + if (next == NULL_SLOT) { + tail_ = prev.id(); + } + releaseSlot({it.slot_, it.currentId_}, resources); +} +inline size_t CollectionData::nesting(const ResourceManager* resources) const +{ + size_t maxChildNesting = 0; + for (auto it = createIterator(resources); !it.done(); it.next(resources)) { + size_t childNesting = it->nesting(resources); + if (childNesting > maxChildNesting) { + maxChildNesting = childNesting; + } + } + return maxChildNesting + 1; +} +inline size_t CollectionData::size(const ResourceManager* resources) const +{ + size_t count = 0; + for (auto it = createIterator(resources); !it.done(); it.next(resources)) { + count++; + } + return count; +} +inline void CollectionData::releaseSlot(SlotWithId slot, + ResourceManager* resources) +{ + if (slot->ownsKey()) { + resources->dereferenceString(slot->key()); + } + slot->data()->setNull(resources); + resources->freeSlot(slot); +} +inline void VariantPool::create(SlotCount cap, Allocator* allocator) +{ + ARDUINOJSON_ASSERT(cap > 0); + slots_ = + reinterpret_cast(allocator->allocate(slotsToBytes(cap))); + capacity_ = slots_ ? cap : 0; + usage_ = 0; +} +inline void VariantPool::destroy(Allocator* allocator) +{ + if (slots_) { + allocator->deallocate(slots_); + } + slots_ = nullptr; + capacity_ = 0; + usage_ = 0; +} +inline void VariantPool::shrinkToFit(Allocator* allocator) +{ + auto newSlots = reinterpret_cast( + allocator->reallocate(slots_, slotsToBytes(usage_))); + if (newSlots) { + slots_ = newSlots; + capacity_ = usage_; + } +} +inline SlotWithId VariantPool::allocSlot() +{ + if (!slots_) { + return {}; + } + if (usage_ >= capacity_) { + return {}; + } + auto index = usage_++; + auto slot = &slots_[index]; + return {new (slot) VariantSlot, SlotId(index)}; +} +inline VariantSlot* VariantPool::getSlot(SlotId id) const +{ + ARDUINOJSON_ASSERT(id < usage_); + return &slots_[id]; +} +inline SlotCount VariantPool::usage() const +{ + return usage_; +} +inline void VariantPool::clear() +{ + usage_ = 0; +} +inline SlotCount VariantPool::bytesToSlots(size_t n) +{ + return static_cast(n / sizeof(VariantSlot)); +} +inline size_t VariantPool::slotsToBytes(SlotCount n) +{ + return n * sizeof(VariantSlot); +} +inline SlotWithId VariantPoolList::allocFromFreeList() +{ + ARDUINOJSON_ASSERT(freeList_ != NULL_SLOT); + auto id = freeList_; + auto slot = getSlot(freeList_); + freeList_ = slot->next(); + return {new (slot) VariantSlot, id}; +} +inline void VariantPoolList::freeSlot(SlotWithId slot) +{ + slot->setNext(freeList_); + freeList_ = slot.id(); +} +template +inline VariantData* ObjectData::getMember( + TAdaptedString key, const ResourceManager* resources) const +{ + return findKey(key, resources).data(); +} +template +VariantData* ObjectData::getOrAddMember(TAdaptedString key, + ResourceManager* resources) +{ + auto it = findKey(key, resources); + if (!it.done()) { + return it.data(); + } + return addMember(key, resources); +} +template +inline ObjectData::iterator ObjectData::findKey( + TAdaptedString key, const ResourceManager* resources) const +{ + if (key.isNull()) { + return iterator(); + } + for (auto it = createIterator(resources); !it.done(); it.next(resources)) { + if (stringEquals(key, adaptString(it.key()))) { + return it; + } + } + return iterator(); +} +template +inline void ObjectData::removeMember(TAdaptedString key, + ResourceManager* resources) +{ + remove(findKey(key, resources), resources); +} +class EscapeSequence { +public: + static char escapeChar(char c) + { + const char* p = escapeTable(true); + while (p[0] && p[1] != c) { + p += 2; + } + return p[0]; + } + static char unescapeChar(char c) + { + const char* p = escapeTable(false); + for (;;) { + if (p[0] == '\0') { + return 0; + } + if (p[0] == c) { + return p[1]; + } + p += 2; + } + } +private: + static const char* escapeTable(bool excludeSolidus) + { + return &"//\"\"\\\\b\bf\fn\nr\rt\t"[excludeSolidus ? 2 : 0]; + } +}; +template +struct FloatParts { + uint32_t integral; + uint32_t decimal; + int16_t exponent; + int8_t decimalPlaces; + FloatParts(TFloat value) + { + uint32_t maxDecimalPart = sizeof(TFloat) >= 8 ? 1000000000 : 1000000; + decimalPlaces = sizeof(TFloat) >= 8 ? 9 : 6; + exponent = normalize(value); + integral = uint32_t(value); + for (uint32_t tmp = integral; tmp >= 10; tmp /= 10) { + maxDecimalPart /= 10; + decimalPlaces--; + } + TFloat remainder = (value - TFloat(integral)) * TFloat(maxDecimalPart); + decimal = uint32_t(remainder); + remainder = remainder - TFloat(decimal); + decimal += uint32_t(remainder * 2); + if (decimal >= maxDecimalPart) { + decimal = 0; + integral++; + if (exponent && integral >= 10) { + exponent++; + integral = 1; + } + } + while (decimal % 10 == 0 && decimalPlaces > 0) { + decimal /= 10; + decimalPlaces--; + } + } + static int16_t normalize(TFloat& value) + { + typedef FloatTraits traits; + int16_t powersOf10 = 0; + int8_t index = sizeof(TFloat) == 8 ? 8 : 5; + int bit = 1 << index; + if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) { + for (; index >= 0; index--) { + if (value >= traits::positiveBinaryPowersOfTen()[index]) { + value *= traits::negativeBinaryPowersOfTen()[index]; + powersOf10 = int16_t(powersOf10 + bit); + } + bit >>= 1; + } + } + if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) { + for (; index >= 0; index--) { + if (value < traits::negativeBinaryPowersOfTen()[index] * 10) { + value *= traits::positiveBinaryPowersOfTen()[index]; + powersOf10 = int16_t(powersOf10 - bit); + } + bit >>= 1; + } + } + return powersOf10; + } +}; +template +class CountingDecorator { +public: + explicit CountingDecorator(TWriter& writer): + writer_(writer), + count_(0) + {} + void write(uint8_t c) + { + count_ += writer_.write(c); + } + void write(const uint8_t* s, size_t n) + { + count_ += writer_.write(s, n); + } + size_t count() const + { + return count_; + } +private: + TWriter writer_; + size_t count_; +}; +template +class TextFormatter { +public: + explicit TextFormatter(TWriter writer): + writer_(writer) + {} + TextFormatter& operator=(const TextFormatter&) = delete; + size_t bytesWritten() const + { + return writer_.count(); + } + void writeBoolean(bool value) + { + if (value) { + writeRaw("true"); + } else { + writeRaw("false"); + } + } + void writeString(const char* value) + { + ARDUINOJSON_ASSERT(value != NULL); + writeRaw('\"'); + while (*value) { + writeChar(*value++); + } + writeRaw('\"'); + } + void writeString(const char* value, size_t n) + { + ARDUINOJSON_ASSERT(value != NULL); + writeRaw('\"'); + while (n--) { + writeChar(*value++); + } + writeRaw('\"'); + } + void writeChar(char c) + { + char specialChar = EscapeSequence::escapeChar(c); + if (specialChar) { + writeRaw('\\'); + writeRaw(specialChar); + } else if (c) { + writeRaw(c); + } else { + writeRaw("\\u0000"); + } + } + template + void writeFloat(T value) + { + if (isnan(value)) { + return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null"); + } +#if ARDUINOJSON_ENABLE_INFINITY + if (value < 0.0) { + writeRaw('-'); + value = -value; + } + if (isinf(value)) { + return writeRaw("Infinity"); + } +#else + if (isinf(value)) { + return writeRaw("null"); + } + if (value < 0.0) { + writeRaw('-'); + value = -value; + } +#endif + FloatParts parts(value); + writeInteger(parts.integral); + if (parts.decimalPlaces) { + writeDecimals(parts.decimal, parts.decimalPlaces); + } + if (parts.exponent) { + writeRaw('e'); + writeInteger(parts.exponent); + } + } + template + typename enable_if::value>::type writeInteger(T value) + { + typedef typename make_unsigned::type unsigned_type; + unsigned_type unsigned_value; + if (value < 0) { + writeRaw('-'); + unsigned_value = unsigned_type(unsigned_type(~value) + 1); + } else { + unsigned_value = unsigned_type(value); + } + writeInteger(unsigned_value); + } + template + typename enable_if::value>::type writeInteger(T value) + { + char buffer[22]; + char* end = buffer + sizeof(buffer); + char* begin = end; + do { + *--begin = char(value % 10 + '0'); + value = T(value / 10); + } while (value); + writeRaw(begin, end); + } + void writeDecimals(uint32_t value, int8_t width) + { + char buffer[16]; + char* end = buffer + sizeof(buffer); + char* begin = end; + while (width--) { + *--begin = char(value % 10 + '0'); + value /= 10; + } + *--begin = '.'; + writeRaw(begin, end); + } + void writeRaw(const char* s) + { + writer_.write(reinterpret_cast(s), strlen(s)); + } + void writeRaw(const char* s, size_t n) + { + writer_.write(reinterpret_cast(s), n); + } + void writeRaw(const char* begin, const char* end) + { + writer_.write(reinterpret_cast(begin), + static_cast(end - begin)); + } + template + void writeRaw(const char (&s)[N]) + { + writer_.write(reinterpret_cast(s), N - 1); + } + void writeRaw(char c) + { + writer_.write(static_cast(c)); + } +protected: + CountingDecorator writer_; +}; +class DummyWriter { +public: + size_t write(uint8_t) + { + return 1; + } + size_t write(const uint8_t*, size_t n) + { + return n; + } +}; +template class TSerializer> +size_t measure(ArduinoJson::JsonVariantConst source) +{ + DummyWriter dp; + TSerializer serializer( + dp, VariantAttorney::getResourceManager(source)); + return VariantData::accept(VariantAttorney::getData(source), serializer); +} +template +class Writer { +public: + explicit Writer(TDestination& dest): + dest_(&dest) + {} + size_t write(uint8_t c) + { + return dest_->write(c); + } + size_t write(const uint8_t* s, size_t n) + { + return dest_->write(s, n); + } +private: + TDestination* dest_; +}; +class StaticStringWriter { +public: + StaticStringWriter(char* buf, size_t size): + end(buf + size), + p(buf) + {} + size_t write(uint8_t c) + { + if (p >= end) { + return 0; + } + *p++ = static_cast(c); + return 1; + } + size_t write(const uint8_t* s, size_t n) + { + char* begin = p; + while (p < end && n > 0) { + *p++ = static_cast(*s++); + n--; + } + return size_t(p - begin); + } +private: + char* end; + char* p; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#if ARDUINOJSON_ENABLE_STD_STRING +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct is_std_string: false_type {}; +template +struct is_std_string< + T, typename enable_if::value && is_same::value>::type>: true_type {}; +template +class Writer::value>::type> { +public: + Writer(TDestination& str): + str_(&str) + { + str.clear(); + } + size_t write(uint8_t c) + { + str_->push_back(static_cast(c)); + return 1; + } + size_t write(const uint8_t* s, size_t n) + { + str_->append(reinterpret_cast(s), n); + return n; + } +private: + TDestination* str_; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#endif +#if ARDUINOJSON_ENABLE_ARDUINO_STRING +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template<> +class Writer<::String, void> { + static const size_t bufferCapacity = ARDUINOJSON_STRING_BUFFER_SIZE; +public: + explicit Writer(::String& str): + destination_(&str), + size_(0) + { + str = static_cast(0); + } + ~Writer() + { + flush(); + } + size_t write(uint8_t c) + { + if (size_ + 1 >= bufferCapacity) { + if (flush() != 0) { + return 0; + } + } + buffer_[size_++] = static_cast(c); + return 1; + } + size_t write(const uint8_t* s, size_t n) + { + for (size_t i = 0; i < n; i++) { + write(s[i]); + } + return n; + } + size_t flush() + { + ARDUINOJSON_ASSERT(size_ < bufferCapacity); + buffer_[size_] = 0; + if (destination_->concat(buffer_)) { + size_ = 0; + } + return size_; + } +private: + ::String* destination_; + char buffer_[bufferCapacity]; + size_t size_; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#endif +#if ARDUINOJSON_ENABLE_STD_STREAM +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +class Writer< + TDestination, + typename enable_if::value>::type> { +public: + explicit Writer(std::ostream& os): + os_(&os) + {} + size_t write(uint8_t c) + { + os_->put(static_cast(c)); + return 1; + } + size_t write(const uint8_t* s, size_t n) + { + os_->write(reinterpret_cast(s), + static_cast(n)); + return n; + } +private: + std::ostream* os_; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#endif +#if ARDUINOJSON_ENABLE_ARDUINO_PRINT +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +class Writer< + TDestination, + typename enable_if::value>::type> { +public: + explicit Writer(::Print& print): + print_(&print) + {} + size_t write(uint8_t c) + { + return print_->write(c); + } + size_t write(const uint8_t* s, size_t n) + { + return print_->write(s, n); + } +private: + ::Print* print_; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template class TSerializer, typename TWriter> +size_t doSerialize(ArduinoJson::JsonVariantConst source, TWriter writer) +{ + TSerializer serializer(writer, + VariantAttorney::getResourceManager(source)); + return VariantData::accept(VariantAttorney::getData(source), serializer); +} +template class TSerializer, typename TDestination> +size_t serialize(ArduinoJson::JsonVariantConst source, + TDestination& destination) +{ + Writer writer(destination); + return doSerialize(source, writer); +} +template class TSerializer> +typename enable_if::producesText, size_t>::type +serialize(ArduinoJson::JsonVariantConst source, void* buffer, + size_t bufferSize) +{ + StaticStringWriter writer(reinterpret_cast(buffer), bufferSize); + return doSerialize(source, writer); +} +template class TSerializer> +typename enable_if::producesText, size_t>::type +serialize(ArduinoJson::JsonVariantConst source, void* buffer, + size_t bufferSize) +{ + StaticStringWriter writer(reinterpret_cast(buffer), bufferSize); + size_t n = doSerialize(source, writer); + if (n < bufferSize) { + reinterpret_cast(buffer)[n] = 0; + } + return n; +} +template class TSerializer, typename TChar, size_t N> +typename enable_if::value, size_t>::type serialize( + ArduinoJson::JsonVariantConst source, TChar (&buffer)[N]) +{ + return serialize(source, buffer, N); +} +template +class JsonSerializer: public VariantDataVisitor { +public: + static const bool producesText = true; + JsonSerializer(TWriter writer, const ResourceManager* resources): + formatter_(writer), + resources_(resources) + {} + size_t visit(const ArrayData& array) + { + write('['); + auto slotId = array.head(); + while (slotId != NULL_SLOT) { + auto slot = resources_->getSlot(slotId); + slot->data()->accept(*this); + slotId = slot->next(); + if (slotId != NULL_SLOT) { + write(','); + } + } + write(']'); + return bytesWritten(); + } + size_t visit(const ObjectData& object) + { + write('{'); + auto slotId = object.head(); + while (slotId != NULL_SLOT) { + auto slot = resources_->getSlot(slotId); + formatter_.writeString(slot->key()); + write(':'); + slot->data()->accept(*this); + slotId = slot->next(); + if (slotId != NULL_SLOT) { + write(','); + } + } + write('}'); + return bytesWritten(); + } + size_t visit(JsonFloat value) + { + formatter_.writeFloat(value); + return bytesWritten(); + } + size_t visit(const char* value) + { + formatter_.writeString(value); + return bytesWritten(); + } + size_t visit(JsonString value) + { + formatter_.writeString(value.c_str(), value.size()); + return bytesWritten(); + } + size_t visit(RawString value) + { + formatter_.writeRaw(value.data(), value.size()); + return bytesWritten(); + } + size_t visit(JsonInteger value) + { + formatter_.writeInteger(value); + return bytesWritten(); + } + size_t visit(JsonUInt value) + { + formatter_.writeInteger(value); + return bytesWritten(); + } + size_t visit(bool value) + { + formatter_.writeBoolean(value); + return bytesWritten(); + } + size_t visit(nullptr_t) + { + formatter_.writeRaw("null"); + return bytesWritten(); + } +protected: + size_t bytesWritten() const + { + return formatter_.bytesWritten(); + } + void write(char c) + { + formatter_.writeRaw(c); + } + void write(const char* s) + { + formatter_.writeRaw(s); + } +private: + TextFormatter formatter_; +protected: + const ResourceManager* resources_; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +template +size_t serializeJson(JsonVariantConst source, TDestination& destination) +{ + using namespace detail; + return serialize(source, destination); +} +inline size_t serializeJson(JsonVariantConst source, void* buffer, + size_t bufferSize) +{ + using namespace detail; + return serialize(source, buffer, bufferSize); +} +inline size_t measureJson(JsonVariantConst source) +{ + using namespace detail; + return measure(source); +} +#if ARDUINOJSON_ENABLE_STD_STREAM +template +inline typename detail::enable_if< + detail::is_convertible::value, std::ostream&>::type +operator<<(std::ostream& os, const T& source) +{ + serializeJson(source, os); + return os; +} +#endif +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +class StringBuilder { +public: + static const size_t initialCapacity = 31; + StringBuilder(ResourceManager* resources): + resources_(resources) + {} + ~StringBuilder() + { + if (node_) { + resources_->destroyString(node_); + } + } + void startString() + { + size_ = 0; + if (!node_) { + node_ = resources_->createString(initialCapacity); + } + } + StringNode* save() + { + ARDUINOJSON_ASSERT(node_ != nullptr); + node_->data[size_] = 0; + StringNode* node = resources_->getString(adaptString(node_->data, size_)); + if (!node) { + node = resources_->resizeString(node_, size_); + ARDUINOJSON_ASSERT(node != nullptr); // realloc to smaller can't fail + resources_->saveString(node); + node_ = nullptr; // next time we need a new string + } else { + node->references++; + } + return node; + } + void append(const char* s) + { + while (*s) { + append(*s++); + } + } + void append(const char* s, size_t n) + { + while (n-- > 0) { // TODO: memcpy + append(*s++); + } + } + void append(char c) + { + if (node_ && size_ == node_->length) { + node_ = resources_->resizeString(node_, size_ * 2U + 1); + } + if (node_) { + node_->data[size_++] = c; + } + } + bool isValid() const + { + return node_ != nullptr; + } + size_t size() const + { + return size_; + } + JsonString str() const + { + ARDUINOJSON_ASSERT(node_ != nullptr); + node_->data[size_] = 0; + return JsonString(node_->data, size_, JsonString::Copied); + } +private: + ResourceManager* resources_; + StringNode* node_ = nullptr; + size_t size_ = 0; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#if ARDUINOJSON_ENABLE_STD_STRING +#include +#endif +#if ARDUINOJSON_ENABLE_STRING_VIEW +#include +#endif +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +template +struct Converter { + static_assert(!detail::is_same::value, + "type 'char' is not supported, use 'signed char', 'unsigned " + "char' or another integer type instead"); + static void toJson(const T& src, JsonVariant dst) + { + convertToJson(src, dst); // Error here? See https://arduinojson.org/v7/unsupported-set/ + } + static T fromJson(JsonVariantConst src) + { + static_assert(!detail::is_same::value, + "type 'char*' is not supported, use 'const char*' instead"); + T result; // Error here? See https://arduinojson.org/v7/non-default-constructible/ + convertFromJson(src, result); // Error here? See https://arduinojson.org/v7/unsupported-as/ + return result; + } + static bool checkJson(JsonVariantConst src) + { + static_assert(!detail::is_same::value, + "type 'char*' is not supported, use 'const char*' instead"); + T dummy = T(); + return canConvertFromJson(src, dummy); // Error here? See https://arduinojson.org/v7/unsupported-is/ + } +}; +template +struct Converter< + T, typename detail::enable_if::value && !detail::is_same::value && !detail::is_same::value>::type>: private detail::VariantAttorney { + static void toJson(T src, JsonVariant dst) + { + ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T); + auto data = getData(dst); + if (data) { + data->setInteger(src, getResourceManager(dst)); + } + } + static T fromJson(JsonVariantConst src) + { + ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T); + auto data = getData(src); + return data ? data->template asIntegral() : T(); + } + static bool checkJson(JsonVariantConst src) + { + auto data = getData(src); + return data && data->template isInteger(); + } +}; +template +struct Converter::value>::type>: private detail::VariantAttorney { + static void toJson(T src, JsonVariant dst) + { + dst.set(static_cast(src)); + } + static T fromJson(JsonVariantConst src) + { + auto data = getData(src); + return data ? static_cast(data->template asIntegral()) : T(); + } + static bool checkJson(JsonVariantConst src) + { + auto data = getData(src); + return data && data->template isInteger(); + } +}; +template<> +struct Converter: private detail::VariantAttorney { + static void toJson(bool src, JsonVariant dst) + { + auto data = getData(dst); + if (data) { + data->setBoolean(src, getResourceManager(dst)); + } + } + static bool fromJson(JsonVariantConst src) + { + auto data = getData(src); + return data ? data->asBoolean() : false; + } + static bool checkJson(JsonVariantConst src) + { + auto data = getData(src); + return data && data->isBoolean(); + } +}; +template +struct Converter< + T, typename detail::enable_if::value>::type>: private detail::VariantAttorney { + static void toJson(T src, JsonVariant dst) + { + auto data = getData(dst); + if (data) { + data->setFloat(static_cast(src), getResourceManager(dst)); + } + } + static T fromJson(JsonVariantConst src) + { + auto data = getData(src); + return data ? data->template asFloat() : 0; + } + static bool checkJson(JsonVariantConst src) + { + auto data = getData(src); + return data && data->isFloat(); + } +}; +template<> +struct Converter: private detail::VariantAttorney { + static void toJson(const char* src, JsonVariant dst) + { + detail::VariantData::setString(getData(dst), detail::adaptString(src), + getResourceManager(dst)); + } + static const char* fromJson(JsonVariantConst src) + { + auto data = getData(src); + return data ? data->asString().c_str() : 0; + } + static bool checkJson(JsonVariantConst src) + { + auto data = getData(src); + return data && data->isString(); + } +}; +template<> +struct Converter: private detail::VariantAttorney { + static void toJson(JsonString src, JsonVariant dst) + { + detail::VariantData::setString(getData(dst), detail::adaptString(src), + getResourceManager(dst)); + } + static JsonString fromJson(JsonVariantConst src) + { + auto data = getData(src); + return data ? data->asString() : 0; + } + static bool checkJson(JsonVariantConst src) + { + auto data = getData(src); + return data && data->isString(); + } +}; +template +inline typename detail::enable_if::value>::type +convertToJson(const T& src, JsonVariant dst) +{ + using namespace detail; + auto data = VariantAttorney::getData(dst); + auto resources = VariantAttorney::getResourceManager(dst); + detail::VariantData::setString(data, adaptString(src), resources); +} +template +struct Converter>: private detail::VariantAttorney { + static void toJson(SerializedValue src, JsonVariant dst) + { + detail::VariantData::setRawString(getData(dst), src, + getResourceManager(dst)); + } +}; +template<> +struct Converter: private detail::VariantAttorney { + static void toJson(detail::nullptr_t, JsonVariant dst) + { + detail::VariantData::setNull(getData(dst), getResourceManager(dst)); + } + static detail::nullptr_t fromJson(JsonVariantConst) + { + return nullptr; + } + static bool checkJson(JsonVariantConst src) + { + auto data = getData(src); + return data == 0 || data->isNull(); + } +}; +#if ARDUINOJSON_ENABLE_ARDUINO_STREAM +namespace detail { + class StringBuilderPrint: public Print { + public: + StringBuilderPrint(ResourceManager* resources): + copier_(resources) + { + copier_.startString(); + } + StringNode* save() + { + ARDUINOJSON_ASSERT(!overflowed()); + return copier_.save(); + } + size_t write(uint8_t c) + { + copier_.append(char(c)); + return copier_.isValid() ? 1 : 0; + } + size_t write(const uint8_t* buffer, size_t size) + { + for (size_t i = 0; i < size; i++) { + copier_.append(char(buffer[i])); + if (!copier_.isValid()) { + return i; + } + } + return size; + } + bool overflowed() const + { + return !copier_.isValid(); + } + private: + StringBuilder copier_; + }; +} // namespace detail +inline void convertToJson(const ::Printable& src, JsonVariant dst) +{ + auto resources = detail::VariantAttorney::getResourceManager(dst); + auto data = detail::VariantAttorney::getData(dst); + if (!resources || !data) { + return; + } + detail::StringBuilderPrint print(resources); + src.printTo(print); + if (print.overflowed()) { + data->setNull(); + return; + } + data->setOwnedString(print.save()); +} +#endif +#if ARDUINOJSON_ENABLE_ARDUINO_STRING +inline void convertFromJson(JsonVariantConst src, ::String& dst) +{ + JsonString str = src.as(); + if (str) { + dst = str.c_str(); + } else { + serializeJson(src, dst); + } +} +inline bool canConvertFromJson(JsonVariantConst src, const ::String&) +{ + return src.is(); +} +#endif +#if ARDUINOJSON_ENABLE_STD_STRING +inline void convertFromJson(JsonVariantConst src, std::string& dst) +{ + JsonString str = src.as(); + if (str) { + dst.assign(str.c_str(), str.size()); + } else { + serializeJson(src, dst); + } +} +inline bool canConvertFromJson(JsonVariantConst src, const std::string&) +{ + return src.is(); +} +#endif +#if ARDUINOJSON_ENABLE_STRING_VIEW +inline void convertFromJson(JsonVariantConst src, std::string_view& dst) +{ + JsonString str = src.as(); + if (str) { // the standard doesn't allow passing null to the constructor + dst = std::string_view(str.c_str(), str.size()); + } +} +inline bool canConvertFromJson(JsonVariantConst src, const std::string_view&) +{ + return src.is(); +} +#endif +namespace detail { + template + struct ConverterNeedsWriteableRef { + protected: // <- to avoid GCC's "all member functions in class are private" + static int probe(T (*f)(ArduinoJson::JsonVariant)); + static char probe(T (*f)(ArduinoJson::JsonVariantConst)); + public: + static const bool value = + sizeof(probe(Converter::fromJson)) == sizeof(int); + }; +} // namespace detail +template<> +struct Converter: private detail::VariantAttorney { + static void toJson(JsonArrayConst src, JsonVariant dst) + { + if (src.isNull()) { + dst.set(nullptr); + } else { + dst.to().set(src); + } + } + static JsonArrayConst fromJson(JsonVariantConst src) + { + auto data = getData(src); + auto array = data ? data->asArray() : nullptr; + return JsonArrayConst(array, getResourceManager(src)); + } + static bool checkJson(JsonVariantConst src) + { + auto data = getData(src); + return data && data->isArray(); + } +}; +template<> +struct Converter: private detail::VariantAttorney { + static void toJson(JsonVariantConst src, JsonVariant dst) + { + if (src.isNull()) { + dst.set(nullptr); + } else { + dst.to().set(src); + } + } + static JsonArray fromJson(JsonVariant src) + { + auto data = getData(src); + auto resources = getResourceManager(src); + return JsonArray(data != 0 ? data->asArray() : 0, resources); + } + static detail::InvalidConversion fromJson( + JsonVariantConst); + static bool checkJson(JsonVariantConst) + { + return false; + } + static bool checkJson(JsonVariant src) + { + auto data = getData(src); + return data && data->isArray(); + } +}; +template<> +struct Converter: private detail::VariantAttorney { + static void toJson(JsonVariantConst src, JsonVariant dst) + { + if (src.isNull()) { + dst.set(nullptr); + } else { + dst.to().set(src); + } + } + static JsonObjectConst fromJson(JsonVariantConst src) + { + auto data = getData(src); + auto object = data != 0 ? data->asObject() : nullptr; + return JsonObjectConst(object, getResourceManager(src)); + } + static bool checkJson(JsonVariantConst src) + { + auto data = getData(src); + return data && data->isObject(); + } +}; +template<> +struct Converter: private detail::VariantAttorney { + static void toJson(JsonVariantConst src, JsonVariant dst) + { + if (src.isNull()) { + dst.set(nullptr); + } else { + dst.to().set(src); + } + } + static JsonObject fromJson(JsonVariant src) + { + auto data = getData(src); + auto resources = getResourceManager(src); + return JsonObject(data != 0 ? data->asObject() : 0, resources); + } + static detail::InvalidConversion fromJson( + JsonVariantConst); + static bool checkJson(JsonVariantConst) + { + return false; + } + static bool checkJson(JsonVariant src) + { + auto data = getData(src); + return data && data->isObject(); + } +}; +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +class JsonVariantCopier { +public: + typedef bool result_type; + JsonVariantCopier(JsonVariant dst): + dst_(dst) + {} + template + bool visit(T src) + { + return dst_.set(src); + } +private: + JsonVariant dst_; +}; +inline bool copyVariant(JsonVariant dst, JsonVariantConst src) +{ + if (dst.isUnbound()) { + return false; + } + JsonVariantCopier copier(dst); + return accept(src, copier); +} +template +inline JsonVariant VariantRefBase::add() const +{ + return add(); +} +template +template +inline typename enable_if::value, T>::type +VariantRefBase::as() const +{ + return Converter::fromJson(getVariant()); +} +template +inline JsonArray VariantRefBase::createNestedArray() const +{ + return add(); +} +template +template +inline JsonArray VariantRefBase::createNestedArray(TChar* key) const +{ + return operator[](key).template to(); +} +template +template +inline JsonArray VariantRefBase::createNestedArray( + const TString& key) const +{ + return operator[](key).template to(); +} +template +inline JsonObject VariantRefBase::createNestedObject() const +{ + return add(); +} +template +template +inline JsonObject VariantRefBase::createNestedObject( + TChar* key) const +{ + return operator[](key).template to(); +} +template +template +inline JsonObject VariantRefBase::createNestedObject( + const TString& key) const +{ + return operator[](key).template to(); +} +template +inline void convertToJson(const VariantRefBase& src, + JsonVariant dst) +{ + dst.set(src.template as()); +} +template +template +inline typename enable_if::value, T>::type +VariantRefBase::add() const +{ + return JsonVariant( + detail::VariantData::addElement(getOrCreateData(), getResourceManager()), + getResourceManager()); +} +template +template +inline typename enable_if::value, bool>::type +VariantRefBase::containsKey(const TString& key) const +{ + return VariantData::getMember(getData(), adaptString(key), + getResourceManager()) + != 0; +} +template +template +inline typename enable_if::value, bool>::type +VariantRefBase::containsKey(TChar* key) const +{ + return VariantData::getMember(getData(), adaptString(key), + getResourceManager()) + != 0; +} +template +inline JsonVariant VariantRefBase::getVariant() const +{ + return JsonVariant(getData(), getResourceManager()); +} +template +inline JsonVariant VariantRefBase::getOrCreateVariant() const +{ + return JsonVariant(getOrCreateData(), getResourceManager()); +} +template +template +inline typename enable_if::value, bool>::type +VariantRefBase::is() const +{ + return Converter::checkJson(getVariant()); +} +template +inline ElementProxy VariantRefBase::operator[]( + size_t index) const +{ + return ElementProxy(derived(), index); +} +template +template +inline typename enable_if::value, + MemberProxy>::type +VariantRefBase::operator[](TString* key) const +{ + return MemberProxy(derived(), key); +} +template +template +inline typename enable_if::value, + MemberProxy>::type +VariantRefBase::operator[](const TString& key) const +{ + return MemberProxy(derived(), key); +} +template +template +inline bool VariantRefBase::set(const T& value) const +{ + Converter::type>::toJson(value, + getOrCreateVariant()); + auto resources = getResourceManager(); + return resources && !resources->overflowed(); +} +template +template +inline bool VariantRefBase::set(T* value) const +{ + Converter::toJson(value, getOrCreateVariant()); + auto resources = getResourceManager(); + return resources && !resources->overflowed(); +} +template +template +inline typename enable_if::value, JsonArray>::type +VariantRefBase::to() const +{ + return JsonArray( + VariantData::toArray(getOrCreateData(), getResourceManager()), + getResourceManager()); +} +template +template +typename enable_if::value, JsonObject>::type +VariantRefBase::to() const +{ + return JsonObject( + VariantData::toObject(getOrCreateData(), getResourceManager()), + getResourceManager()); +} +template +template +typename enable_if::value, JsonVariant>::type +VariantRefBase::to() const +{ + auto data = getOrCreateData(); + auto resources = getResourceManager(); + detail::VariantData::setNull(data, resources); + return JsonVariant(data, resources); +} +ARDUINOJSON_END_PRIVATE_NAMESPACE +#if ARDUINOJSON_ENABLE_STD_STREAM +#endif +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class DeserializationError { +public: + enum Code { + Ok, + EmptyInput, + IncompleteInput, + InvalidInput, + NoMemory, + TooDeep + }; + DeserializationError() + {} + DeserializationError(Code c): + code_(c) + {} + friend bool operator==(const DeserializationError& lhs, + const DeserializationError& rhs) + { + return lhs.code_ == rhs.code_; + } + friend bool operator!=(const DeserializationError& lhs, + const DeserializationError& rhs) + { + return lhs.code_ != rhs.code_; + } + friend bool operator==(const DeserializationError& lhs, Code rhs) + { + return lhs.code_ == rhs; + } + friend bool operator==(Code lhs, const DeserializationError& rhs) + { + return lhs == rhs.code_; + } + friend bool operator!=(const DeserializationError& lhs, Code rhs) + { + return lhs.code_ != rhs; + } + friend bool operator!=(Code lhs, const DeserializationError& rhs) + { + return lhs != rhs.code_; + } + explicit operator bool() const + { + return code_ != Ok; + } + Code code() const + { + return code_; + } + const char* c_str() const + { + static const char* messages[] = { + "Ok", "EmptyInput", "IncompleteInput", + "InvalidInput", "NoMemory", "TooDeep"}; + ARDUINOJSON_ASSERT(static_cast(code_) < sizeof(messages) / sizeof(messages[0])); + return messages[code_]; + } +#if ARDUINOJSON_ENABLE_PROGMEM + const __FlashStringHelper* f_str() const + { + ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s0, "Ok"); + ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s1, "EmptyInput"); + ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s2, "IncompleteInput"); + ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s3, "InvalidInput"); + ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s4, "NoMemory"); + ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s5, "TooDeep"); + ARDUINOJSON_DEFINE_PROGMEM_ARRAY(const char*, messages, + {s0, s1, s2, s3, s4, s5}); + return reinterpret_cast( + detail::pgm_read(messages + code_)); + } +#endif +private: + Code code_; +}; +#if ARDUINOJSON_ENABLE_STD_STREAM +inline std::ostream& operator<<(std::ostream& s, + const DeserializationError& e) +{ + s << e.c_str(); + return s; +} +inline std::ostream& operator<<(std::ostream& s, DeserializationError::Code c) +{ + s << DeserializationError(c).c_str(); + return s; +} +#endif +namespace DeserializationOption { + class Filter { + public: +#if ARDUINOJSON_AUTO_SHRINK + explicit Filter(JsonDocument& doc): + variant_(doc) + { + doc.shrinkToFit(); + } +#endif + explicit Filter(JsonVariantConst variant): + variant_(variant) + {} + bool allow() const + { + return variant_; + } + bool allowArray() const + { + return variant_ == true || variant_.is(); + } + bool allowObject() const + { + return variant_ == true || variant_.is(); + } + bool allowValue() const + { + return variant_ == true; + } + template + Filter operator[](const TKey& key) const + { + if (variant_ == true) { // "true" means "allow recursively" + return *this; + } + JsonVariantConst member = variant_[key]; + return Filter(member.isNull() ? variant_["*"] : member); + } + private: + JsonVariantConst variant_; + }; +} // namespace DeserializationOption +namespace detail { + struct AllowAllFilter { + bool allow() const + { + return true; + } + bool allowArray() const + { + return true; + } + bool allowObject() const + { + return true; + } + bool allowValue() const + { + return true; + } + template + AllowAllFilter operator[](const TKey&) const + { + return AllowAllFilter(); + } + }; +} // namespace detail +namespace DeserializationOption { + class NestingLimit { + public: + NestingLimit(): + value_(ARDUINOJSON_DEFAULT_NESTING_LIMIT) + {} + explicit NestingLimit(uint8_t n): + value_(n) + {} + NestingLimit decrement() const + { + ARDUINOJSON_ASSERT(value_ > 0); + return NestingLimit(static_cast(value_ - 1)); + } + bool reached() const + { + return value_ == 0; + } + private: + uint8_t value_; + }; +} // namespace DeserializationOption +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct DeserializationOptions { + TFilter filter; + DeserializationOption::NestingLimit nestingLimit; +}; +template +inline DeserializationOptions makeDeserializationOptions( + TFilter filter, DeserializationOption::NestingLimit nestingLimit = {}) +{ + return {filter, nestingLimit}; +} +template +inline DeserializationOptions makeDeserializationOptions( + DeserializationOption::NestingLimit nestingLimit, TFilter filter) +{ + return {filter, nestingLimit}; +} +inline DeserializationOptions makeDeserializationOptions( + DeserializationOption::NestingLimit nestingLimit = {}) +{ + return {{}, nestingLimit}; +} +template +struct Reader { +public: + Reader(TSource& source): + source_(&source) + {} + int read() + { + return source_->read(); // Error here? See https://arduinojson.org/v7/invalid-input/ + } + size_t readBytes(char* buffer, size_t length) + { + return source_->readBytes(buffer, length); + } +private: + TSource* source_; +}; +template +struct BoundedReader { +}; +template +class IteratorReader { + TIterator ptr_, end_; +public: + explicit IteratorReader(TIterator begin, TIterator end): + ptr_(begin), + end_(end) + {} + int read() + { + if (ptr_ < end_) { + return static_cast(*ptr_++); + } else { + return -1; + } + } + size_t readBytes(char* buffer, size_t length) + { + size_t i = 0; + while (i < length && ptr_ < end_) { + buffer[i++] = *ptr_++; + } + return i; + } +}; +template +struct void_ { + typedef void type; +}; +template +struct Reader::type>: IteratorReader { + explicit Reader(const TSource& source): + IteratorReader(source.begin(), + source.end()) + {} +}; +template +struct IsCharOrVoid { + static const bool value = + is_same::value || is_same::value || is_same::value || is_same::value; +}; +template +struct IsCharOrVoid: IsCharOrVoid {}; +template +struct Reader::value>::type> { + const char* ptr_; +public: + explicit Reader(const void* ptr): + ptr_(ptr ? reinterpret_cast(ptr) : "") + {} + int read() + { + return static_cast(*ptr_++); + } + size_t readBytes(char* buffer, size_t length) + { + for (size_t i = 0; i < length; i++) { + buffer[i] = *ptr_++; + } + return length; + } +}; +template +struct BoundedReader::value>::type>: public IteratorReader { +public: + explicit BoundedReader(const void* ptr, size_t len): + IteratorReader(reinterpret_cast(ptr), + reinterpret_cast(ptr) + len) + {} +}; +template +struct Reader::value>::type>: Reader { + explicit Reader(const TVariant& x): + Reader(x.template as()) + {} +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#if ARDUINOJSON_ENABLE_ARDUINO_STREAM +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct Reader::value>::type> { +public: + explicit Reader(Stream& stream): + stream_(&stream) + {} + int read() + { + char c; + return stream_->readBytes(&c, 1) ? static_cast(c) : -1; + } + size_t readBytes(char* buffer, size_t length) + { + return stream_->readBytes(buffer, length); + } +private: + Stream* stream_; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#endif +#if ARDUINOJSON_ENABLE_ARDUINO_STRING +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct Reader::value>::type>: BoundedReader { + explicit Reader(const ::String& s): + BoundedReader(s.c_str(), s.length()) + {} +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#endif +#if ARDUINOJSON_ENABLE_PROGMEM +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template<> +struct Reader { + const char* ptr_; +public: + explicit Reader(const __FlashStringHelper* ptr): + ptr_(reinterpret_cast(ptr)) + {} + int read() + { + return pgm_read_byte(ptr_++); + } + size_t readBytes(char* buffer, size_t length) + { + memcpy_P(buffer, ptr_, length); + ptr_ += length; + return length; + } +}; +template<> +struct BoundedReader { + const char* ptr_; + const char* end_; +public: + explicit BoundedReader(const __FlashStringHelper* ptr, size_t size): + ptr_(reinterpret_cast(ptr)), + end_(ptr_ + size) + {} + int read() + { + if (ptr_ < end_) { + return pgm_read_byte(ptr_++); + } else { + return -1; + } + } + size_t readBytes(char* buffer, size_t length) + { + size_t available = static_cast(end_ - ptr_); + if (available < length) { + length = available; + } + memcpy_P(buffer, ptr_, length); + ptr_ += length; + return length; + } +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#endif +#if ARDUINOJSON_ENABLE_STD_STREAM +#include +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct Reader::value>::type> { +public: + explicit Reader(std::istream& stream): + stream_(&stream) + {} + int read() + { + return stream_->get(); + } + size_t readBytes(char* buffer, size_t length) + { + stream_->read(buffer, static_cast(length)); + return static_cast(stream_->gcount()); + } +private: + std::istream* stream_; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +Reader::type> makeReader(TInput&& input) +{ + return Reader::type>{ + detail::forward(input)}; +} +template +BoundedReader makeReader(TChar* input, size_t inputSize) +{ + return BoundedReader{input, inputSize}; +} +template +struct first_or_void { + using type = void; +}; +template +struct first_or_void { + using type = T; +}; +template +struct is_deserialize_destination: false_type {}; +template +struct is_deserialize_destination< + T, typename enable_if())), + ResourceManager*>::value>::type>: true_type { +}; +template +inline void shrinkJsonDocument(TDestination&) +{ +} +#if ARDUINOJSON_AUTO_SHRINK +inline void shrinkJsonDocument(JsonDocument& doc) +{ + doc.shrinkToFit(); +} +#endif +template class TDeserializer, typename TDestination, + typename TReader, typename TOptions> +DeserializationError doDeserialize(TDestination&& dst, TReader reader, + TOptions options) +{ + auto data = VariantAttorney::getOrCreateData(dst); + if (!data) { + return DeserializationError::NoMemory; + } + auto resources = VariantAttorney::getResourceManager(dst); + dst.clear(); + auto err = TDeserializer(resources, reader) + .parse(*data, options.filter, options.nestingLimit); + shrinkJsonDocument(dst); + return err; +} +template class TDeserializer, typename TDestination, + typename TStream, typename... Args, + typename = typename enable_if< // issue #1897 + !is_integral::type>::value>::type> +DeserializationError deserialize(TDestination&& dst, TStream&& input, + Args... args) +{ + return doDeserialize( + dst, makeReader(detail::forward(input)), + makeDeserializationOptions(args...)); +} +template class TDeserializer, typename TDestination, + typename TChar, typename Size, typename... Args, + typename = typename enable_if::value>::type> +DeserializationError deserialize(TDestination&& dst, TChar* input, + Size inputSize, Args... args) +{ + return doDeserialize(dst, makeReader(input, size_t(inputSize)), + makeDeserializationOptions(args...)); +} +template +class Latch { +public: + Latch(TReader reader): + reader_(reader), + loaded_(false) + { +#if ARDUINOJSON_DEBUG + ended_ = false; +#endif + } + void clear() + { + loaded_ = false; + } + int last() const + { + return current_; + } + FORCE_INLINE char current() + { + if (!loaded_) { + load(); + } + return current_; + } +private: + void load() + { + ARDUINOJSON_ASSERT(!ended_); + int c = reader_.read(); +#if ARDUINOJSON_DEBUG + if (c <= 0) { + ended_ = true; + } +#endif + current_ = static_cast(c > 0 ? c : 0); + loaded_ = true; + } + TReader reader_; + char current_; // NOLINT(clang-analyzer-optin.cplusplus.UninitializedObject) + bool loaded_; +#if ARDUINOJSON_DEBUG + bool ended_; +#endif +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#if defined(__GNUC__) +#if __GNUC__ >= 7 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +namespace Utf16 { + inline bool isHighSurrogate(uint16_t codeunit) + { + return codeunit >= 0xD800 && codeunit < 0xDC00; + } + inline bool isLowSurrogate(uint16_t codeunit) + { + return codeunit >= 0xDC00 && codeunit < 0xE000; + } + class Codepoint { + public: + Codepoint(): + highSurrogate_(0), + codepoint_(0) + {} + bool append(uint16_t codeunit) + { + if (isHighSurrogate(codeunit)) { + highSurrogate_ = codeunit & 0x3FF; + return false; + } + if (isLowSurrogate(codeunit)) { + codepoint_ = + uint32_t(0x10000 + ((highSurrogate_ << 10) | (codeunit & 0x3FF))); + return true; + } + codepoint_ = codeunit; + return true; + } + uint32_t value() const + { + return codepoint_; + } + private: + uint16_t highSurrogate_; + uint32_t codepoint_; + }; +} // namespace Utf16 +ARDUINOJSON_END_PRIVATE_NAMESPACE +#if defined(__GNUC__) +#if __GNUC__ >= 8 +#pragma GCC diagnostic pop +#endif +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +namespace Utf8 { + template + inline void encodeCodepoint(uint32_t codepoint32, TStringBuilder& str) + { + if (codepoint32 < 0x80) { + str.append(char(codepoint32)); + } else { + char buf[5]; + char* p = buf; + *(p++) = 0; + *(p++) = char((codepoint32 | 0x80) & 0xBF); + uint16_t codepoint16 = uint16_t(codepoint32 >> 6); + if (codepoint16 < 0x20) { // 0x800 + *(p++) = char(codepoint16 | 0xC0); + } else { + *(p++) = char((codepoint16 | 0x80) & 0xBF); + codepoint16 = uint16_t(codepoint16 >> 6); + if (codepoint16 < 0x10) { // 0x10000 + *(p++) = char(codepoint16 | 0xE0); + } else { + *(p++) = char((codepoint16 | 0x80) & 0xBF); + codepoint16 = uint16_t(codepoint16 >> 6); + *(p++) = char(codepoint16 | 0xF0); + } + } + while (*(--p)) { + str.append(*p); + } + } + } +} // namespace Utf8 +#ifndef isdigit +inline bool isdigit(char c) +{ + return '0' <= c && c <= '9'; +} +#endif +inline bool issign(char c) +{ + return '-' == c || c == '+'; +} +template +struct choose_largest: conditional<(sizeof(A) > sizeof(B)), A, B> {}; +inline bool parseNumber(const char* s, VariantData& result) +{ + typedef FloatTraits traits; + typedef choose_largest::type mantissa_t; + typedef traits::exponent_type exponent_t; + ARDUINOJSON_ASSERT(s != 0); + bool is_negative = false; + switch (*s) { + case '-': + is_negative = true; + s++; + break; + case '+': + s++; + break; + } +#if ARDUINOJSON_ENABLE_NAN + if (*s == 'n' || *s == 'N') { + result.setFloat(traits::nan()); + return true; + } +#endif +#if ARDUINOJSON_ENABLE_INFINITY + if (*s == 'i' || *s == 'I') { + result.setFloat(is_negative ? -traits::inf() : traits::inf()); + return true; + } +#endif + if (!isdigit(*s) && *s != '.') { + return false; + } + mantissa_t mantissa = 0; + exponent_t exponent_offset = 0; + const mantissa_t maxUint = JsonUInt(-1); + while (isdigit(*s)) { + uint8_t digit = uint8_t(*s - '0'); + if (mantissa > maxUint / 10) { + break; + } + mantissa *= 10; + if (mantissa > maxUint - digit) { + break; + } + mantissa += digit; + s++; + } + if (*s == '\0') { + if (is_negative) { + const mantissa_t sintMantissaMax = mantissa_t(1) + << (sizeof(JsonInteger) * 8 - 1); + if (mantissa <= sintMantissaMax) { + result.setInteger(JsonInteger(~mantissa + 1)); + return true; + } + } else { + result.setInteger(JsonUInt(mantissa)); + return true; + } + } + while (mantissa > traits::mantissa_max) { + mantissa /= 10; + exponent_offset++; + } + while (isdigit(*s)) { + exponent_offset++; + s++; + } + if (*s == '.') { + s++; + while (isdigit(*s)) { + if (mantissa < traits::mantissa_max / 10) { + mantissa = mantissa * 10 + uint8_t(*s - '0'); + exponent_offset--; + } + s++; + } + } + int exponent = 0; + if (*s == 'e' || *s == 'E') { + s++; + bool negative_exponent = false; + if (*s == '-') { + negative_exponent = true; + s++; + } else if (*s == '+') { + s++; + } + while (isdigit(*s)) { + exponent = exponent * 10 + (*s - '0'); + if (exponent + exponent_offset > traits::exponent_max) { + if (negative_exponent) { + result.setFloat(is_negative ? -0.0f : 0.0f); + } else { + result.setFloat(is_negative ? -traits::inf() : traits::inf()); + } + return true; + } + s++; + } + if (negative_exponent) { + exponent = -exponent; + } + } + exponent += exponent_offset; + if (*s != '\0') { + return false; + } + JsonFloat final_result = + make_float(static_cast(mantissa), exponent); + result.setFloat(is_negative ? -final_result : final_result); + return true; +} +template +inline T parseNumber(const char* s) +{ + VariantData value; + parseNumber(s, value); + return Converter::fromJson(JsonVariantConst(&value, nullptr)); +} +template +class JsonDeserializer { +public: + JsonDeserializer(ResourceManager* resources, TReader reader): + stringBuilder_(resources), + foundSomething_(false), + latch_(reader), + resources_(resources) + {} + template + DeserializationError parse(VariantData& variant, TFilter filter, + DeserializationOption::NestingLimit nestingLimit) + { + DeserializationError::Code err; + err = parseVariant(variant, filter, nestingLimit); + if (!err && latch_.last() != 0 && variant.isFloat()) { + return DeserializationError::InvalidInput; + } + return err; + } +private: + char current() + { + return latch_.current(); + } + void move() + { + latch_.clear(); + } + bool eat(char charToSkip) + { + if (current() != charToSkip) { + return false; + } + move(); + return true; + } + template + DeserializationError::Code parseVariant( + VariantData& variant, TFilter filter, + DeserializationOption::NestingLimit nestingLimit) + { + DeserializationError::Code err; + err = skipSpacesAndComments(); + if (err) { + return err; + } + switch (current()) { + case '[': + if (filter.allowArray()) { + return parseArray(variant.toArray(), filter, nestingLimit); + } else { + return skipArray(nestingLimit); + } + case '{': + if (filter.allowObject()) { + return parseObject(variant.toObject(), filter, nestingLimit); + } else { + return skipObject(nestingLimit); + } + case '\"': + case '\'': + if (filter.allowValue()) { + return parseStringValue(variant); + } else { + return skipQuotedString(); + } + case 't': + if (filter.allowValue()) { + variant.setBoolean(true); + } + return skipKeyword("true"); + case 'f': + if (filter.allowValue()) { + variant.setBoolean(false); + } + return skipKeyword("false"); + case 'n': + return skipKeyword("null"); + default: + if (filter.allowValue()) { + return parseNumericValue(variant); + } else { + return skipNumericValue(); + } + } + } + DeserializationError::Code skipVariant( + DeserializationOption::NestingLimit nestingLimit) + { + DeserializationError::Code err; + err = skipSpacesAndComments(); + if (err) { + return err; + } + switch (current()) { + case '[': + return skipArray(nestingLimit); + case '{': + return skipObject(nestingLimit); + case '\"': + case '\'': + return skipQuotedString(); + case 't': + return skipKeyword("true"); + case 'f': + return skipKeyword("false"); + case 'n': + return skipKeyword("null"); + default: + return skipNumericValue(); + } + } + template + DeserializationError::Code parseArray( + ArrayData& array, TFilter filter, + DeserializationOption::NestingLimit nestingLimit) + { + DeserializationError::Code err; + if (nestingLimit.reached()) { + return DeserializationError::TooDeep; + } + ARDUINOJSON_ASSERT(current() == '['); + move(); + err = skipSpacesAndComments(); + if (err) { + return err; + } + if (eat(']')) { + return DeserializationError::Ok; + } + TFilter elementFilter = filter[0UL]; + for (;;) { + if (elementFilter.allow()) { + VariantData* value = array.addElement(resources_); + if (!value) { + return DeserializationError::NoMemory; + } + err = parseVariant(*value, elementFilter, nestingLimit.decrement()); + if (err) { + return err; + } + } else { + err = skipVariant(nestingLimit.decrement()); + if (err) { + return err; + } + } + err = skipSpacesAndComments(); + if (err) { + return err; + } + if (eat(']')) { + return DeserializationError::Ok; + } + if (!eat(',')) { + return DeserializationError::InvalidInput; + } + } + } + DeserializationError::Code skipArray( + DeserializationOption::NestingLimit nestingLimit) + { + DeserializationError::Code err; + if (nestingLimit.reached()) { + return DeserializationError::TooDeep; + } + ARDUINOJSON_ASSERT(current() == '['); + move(); + for (;;) { + err = skipVariant(nestingLimit.decrement()); + if (err) { + return err; + } + err = skipSpacesAndComments(); + if (err) { + return err; + } + if (eat(']')) { + return DeserializationError::Ok; + } + if (!eat(',')) { + return DeserializationError::InvalidInput; + } + } + } + template + DeserializationError::Code parseObject( + ObjectData& object, TFilter filter, + DeserializationOption::NestingLimit nestingLimit) + { + DeserializationError::Code err; + if (nestingLimit.reached()) { + return DeserializationError::TooDeep; + } + ARDUINOJSON_ASSERT(current() == '{'); + move(); + err = skipSpacesAndComments(); + if (err) { + return err; + } + if (eat('}')) { + return DeserializationError::Ok; + } + for (;;) { + err = parseKey(); + if (err) { + return err; + } + err = skipSpacesAndComments(); + if (err) { + return err; + } + if (!eat(':')) { + return DeserializationError::InvalidInput; + } + JsonString key = stringBuilder_.str(); + TFilter memberFilter = filter[key.c_str()]; + if (memberFilter.allow()) { + auto member = object.getMember(adaptString(key.c_str()), resources_); + if (!member) { + auto savedKey = stringBuilder_.save(); + member = object.addMember(savedKey, resources_); + if (!member) { + return DeserializationError::NoMemory; + } + } else { + member->setNull(resources_); + } + err = parseVariant(*member, memberFilter, nestingLimit.decrement()); + if (err) { + return err; + } + } else { + err = skipVariant(nestingLimit.decrement()); + if (err) { + return err; + } + } + err = skipSpacesAndComments(); + if (err) { + return err; + } + if (eat('}')) { + return DeserializationError::Ok; + } + if (!eat(',')) { + return DeserializationError::InvalidInput; + } + err = skipSpacesAndComments(); + if (err) { + return err; + } + } + } + DeserializationError::Code skipObject( + DeserializationOption::NestingLimit nestingLimit) + { + DeserializationError::Code err; + if (nestingLimit.reached()) { + return DeserializationError::TooDeep; + } + ARDUINOJSON_ASSERT(current() == '{'); + move(); + err = skipSpacesAndComments(); + if (err) { + return err; + } + if (eat('}')) { + return DeserializationError::Ok; + } + for (;;) { + err = skipKey(); + if (err) { + return err; + } + err = skipSpacesAndComments(); + if (err) { + return err; + } + if (!eat(':')) { + return DeserializationError::InvalidInput; + } + err = skipVariant(nestingLimit.decrement()); + if (err) { + return err; + } + err = skipSpacesAndComments(); + if (err) { + return err; + } + if (eat('}')) { + return DeserializationError::Ok; + } + if (!eat(',')) { + return DeserializationError::InvalidInput; + } + err = skipSpacesAndComments(); + if (err) { + return err; + } + } + } + DeserializationError::Code parseKey() + { + stringBuilder_.startString(); + if (isQuote(current())) { + return parseQuotedString(); + } else { + return parseNonQuotedString(); + } + } + DeserializationError::Code parseStringValue(VariantData& variant) + { + DeserializationError::Code err; + stringBuilder_.startString(); + err = parseQuotedString(); + if (err) { + return err; + } + variant.setOwnedString(stringBuilder_.save()); + return DeserializationError::Ok; + } + DeserializationError::Code parseQuotedString() + { +#if ARDUINOJSON_DECODE_UNICODE + Utf16::Codepoint codepoint; + DeserializationError::Code err; +#endif + const char stopChar = current(); + move(); + for (;;) { + char c = current(); + move(); + if (c == stopChar) { + break; + } + if (c == '\0') { + return DeserializationError::IncompleteInput; + } + if (c == '\\') { + c = current(); + if (c == '\0') { + return DeserializationError::IncompleteInput; + } + if (c == 'u') { +#if ARDUINOJSON_DECODE_UNICODE + move(); + uint16_t codeunit; + err = parseHex4(codeunit); + if (err) { + return err; + } + if (codepoint.append(codeunit)) { + Utf8::encodeCodepoint(codepoint.value(), stringBuilder_); + } +#else + stringBuilder_.append('\\'); +#endif + continue; + } + c = EscapeSequence::unescapeChar(c); + if (c == '\0') { + return DeserializationError::InvalidInput; + } + move(); + } + stringBuilder_.append(c); + } + if (!stringBuilder_.isValid()) { + return DeserializationError::NoMemory; + } + return DeserializationError::Ok; + } + DeserializationError::Code parseNonQuotedString() + { + char c = current(); + ARDUINOJSON_ASSERT(c); + if (canBeInNonQuotedString(c)) { // no quotes + do { + move(); + stringBuilder_.append(c); + c = current(); + } while (canBeInNonQuotedString(c)); + } else { + return DeserializationError::InvalidInput; + } + if (!stringBuilder_.isValid()) { + return DeserializationError::NoMemory; + } + return DeserializationError::Ok; + } + DeserializationError::Code skipKey() + { + if (isQuote(current())) { + return skipQuotedString(); + } else { + return skipNonQuotedString(); + } + } + DeserializationError::Code skipQuotedString() + { + const char stopChar = current(); + move(); + for (;;) { + char c = current(); + move(); + if (c == stopChar) { + break; + } + if (c == '\0') { + return DeserializationError::IncompleteInput; + } + if (c == '\\') { + if (current() != '\0') { + move(); + } + } + } + return DeserializationError::Ok; + } + DeserializationError::Code skipNonQuotedString() + { + char c = current(); + while (canBeInNonQuotedString(c)) { + move(); + c = current(); + } + return DeserializationError::Ok; + } + DeserializationError::Code parseNumericValue(VariantData& result) + { + uint8_t n = 0; + char c = current(); + while (canBeInNumber(c) && n < 63) { + move(); + buffer_[n++] = c; + c = current(); + } + buffer_[n] = 0; + if (!parseNumber(buffer_, result)) { + return DeserializationError::InvalidInput; + } + return DeserializationError::Ok; + } + DeserializationError::Code skipNumericValue() + { + char c = current(); + while (canBeInNumber(c)) { + move(); + c = current(); + } + return DeserializationError::Ok; + } + DeserializationError::Code parseHex4(uint16_t& result) + { + result = 0; + for (uint8_t i = 0; i < 4; ++i) { + char digit = current(); + if (!digit) { + return DeserializationError::IncompleteInput; + } + uint8_t value = decodeHex(digit); + if (value > 0x0F) { + return DeserializationError::InvalidInput; + } + result = uint16_t((result << 4) | value); + move(); + } + return DeserializationError::Ok; + } + static inline bool isBetween(char c, char min, char max) + { + return min <= c && c <= max; + } + static inline bool canBeInNumber(char c) + { + return isBetween(c, '0', '9') || c == '+' || c == '-' || c == '.' || +#if ARDUINOJSON_ENABLE_NAN || ARDUINOJSON_ENABLE_INFINITY + isBetween(c, 'A', 'Z') || isBetween(c, 'a', 'z'); +#else + c == 'e' || c == 'E'; +#endif + } + static inline bool canBeInNonQuotedString(char c) + { + return isBetween(c, '0', '9') || isBetween(c, '_', 'z') || isBetween(c, 'A', 'Z'); + } + static inline bool isQuote(char c) + { + return c == '\'' || c == '\"'; + } + static inline uint8_t decodeHex(char c) + { + if (c < 'A') { + return uint8_t(c - '0'); + } + c = char(c & ~0x20); // uppercase + return uint8_t(c - 'A' + 10); + } + DeserializationError::Code skipSpacesAndComments() + { + for (;;) { + switch (current()) { + case '\0': + return foundSomething_ ? DeserializationError::IncompleteInput + : DeserializationError::EmptyInput; + case ' ': + case '\t': + case '\r': + case '\n': + move(); + continue; +#if ARDUINOJSON_ENABLE_COMMENTS + case '/': + move(); // skip '/' + switch (current()) { + case '*': { + move(); // skip '*' + bool wasStar = false; + for (;;) { + char c = current(); + if (c == '\0') { + return DeserializationError::IncompleteInput; + } + if (c == '/' && wasStar) { + move(); + break; + } + wasStar = c == '*'; + move(); + } + break; + } + case '/': + for (;;) { + move(); + char c = current(); + if (c == '\0') { + return DeserializationError::IncompleteInput; + } + if (c == '\n') { + break; + } + } + break; + default: + return DeserializationError::InvalidInput; + } + break; +#endif + default: + foundSomething_ = true; + return DeserializationError::Ok; + } + } + } + DeserializationError::Code skipKeyword(const char* s) + { + while (*s) { + char c = current(); + if (c == '\0') { + return DeserializationError::IncompleteInput; + } + if (*s != c) { + return DeserializationError::InvalidInput; + } + ++s; + move(); + } + return DeserializationError::Ok; + } + StringBuilder stringBuilder_; + bool foundSomething_; + Latch latch_; + ResourceManager* resources_; + char buffer_[64]; // using a member instead of a local variable because it +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +template +typename detail::enable_if< + detail::is_deserialize_destination::value, + DeserializationError>::type +deserializeJson(TDestination&& dst, Args&&... args) +{ + using namespace detail; + return deserialize(detail::forward(dst), + detail::forward(args)...); +} +template +typename detail::enable_if< + detail::is_deserialize_destination::value, + DeserializationError>::type +deserializeJson(TDestination&& dst, TChar* input, Args&&... args) +{ + using namespace detail; + return deserialize(detail::forward(dst), + input, detail::forward(args)...); +} +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +class PrettyJsonSerializer: public JsonSerializer { + typedef JsonSerializer base; +public: + PrettyJsonSerializer(TWriter writer, const ResourceManager* resources): + base(writer, resources), + nesting_(0) + {} + size_t visit(const ArrayData& array) + { + auto it = array.createIterator(base::resources_); + if (!it.done()) { + base::write("[\r\n"); + nesting_++; + while (!it.done()) { + indent(); + it->accept(*this); + it.next(base::resources_); + base::write(it.done() ? "\r\n" : ",\r\n"); + } + nesting_--; + indent(); + base::write("]"); + } else { + base::write("[]"); + } + return this->bytesWritten(); + } + size_t visit(const ObjectData& object) + { + auto it = object.createIterator(base::resources_); + if (!it.done()) { + base::write("{\r\n"); + nesting_++; + while (!it.done()) { + indent(); + base::visit(it.key()); + base::write(": "); + it->accept(*this); + it.next(base::resources_); + base::write(it.done() ? "\r\n" : ",\r\n"); + } + nesting_--; + indent(); + base::write("}"); + } else { + base::write("{}"); + } + return this->bytesWritten(); + } + using base::visit; +private: + void indent() + { + for (uint8_t i = 0; i < nesting_; i++) { + base::write(ARDUINOJSON_TAB); + } + } + uint8_t nesting_; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +template +size_t serializeJsonPretty(JsonVariantConst source, TDestination& destination) +{ + using namespace ArduinoJson::detail; + return serialize(source, destination); +} +inline size_t serializeJsonPretty(JsonVariantConst source, void* buffer, + size_t bufferSize) +{ + using namespace ArduinoJson::detail; + return serialize(source, buffer, bufferSize); +} +inline size_t measureJsonPretty(JsonVariantConst source) +{ + using namespace ArduinoJson::detail; + return measure(source); +} +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +#if ARDUINOJSON_LITTLE_ENDIAN +inline void swapBytes(uint8_t& a, uint8_t& b) +{ + uint8_t t(a); + a = b; + b = t; +} +inline void fixEndianess(uint8_t* p, integral_constant) +{ + swapBytes(p[0], p[7]); + swapBytes(p[1], p[6]); + swapBytes(p[2], p[5]); + swapBytes(p[3], p[4]); +} +inline void fixEndianess(uint8_t* p, integral_constant) +{ + swapBytes(p[0], p[3]); + swapBytes(p[1], p[2]); +} +inline void fixEndianess(uint8_t* p, integral_constant) +{ + swapBytes(p[0], p[1]); +} +inline void fixEndianess(uint8_t*, integral_constant) +{} +template +inline void fixEndianess(T& value) +{ + fixEndianess(reinterpret_cast(&value), + integral_constant()); +} +#else +template +inline void fixEndianess(T&) +{} +#endif +inline void doubleToFloat(const uint8_t d[8], uint8_t f[4]) +{ + f[0] = uint8_t((d[0] & 0xC0) | (d[0] << 3 & 0x3f) | (d[1] >> 5)); + f[1] = uint8_t((d[1] << 3) | (d[2] >> 5)); + f[2] = uint8_t((d[2] << 3) | (d[3] >> 5)); + f[3] = uint8_t((d[3] << 3) | (d[4] >> 5)); +} +template +class MsgPackDeserializer { +public: + MsgPackDeserializer(ResourceManager* resources, TReader reader): + resources_(resources), + reader_(reader), + stringBuilder_(resources), + foundSomething_(false) + {} + template + DeserializationError parse(VariantData& variant, TFilter filter, + DeserializationOption::NestingLimit nestingLimit) + { + DeserializationError::Code err; + err = parseVariant(&variant, filter, nestingLimit); + return foundSomething_ ? err : DeserializationError::EmptyInput; + } +private: + template + DeserializationError::Code parseVariant( + VariantData* variant, TFilter filter, + DeserializationOption::NestingLimit nestingLimit) + { + DeserializationError::Code err; + uint8_t code = 0; // TODO: why do we need to initialize this variable? + err = readByte(code); + if (err) { + return err; + } + foundSomething_ = true; + bool allowValue = filter.allowValue(); + if (allowValue) { + ARDUINOJSON_ASSERT(variant != 0); + } + switch (code) { + case 0xc0: + return DeserializationError::Ok; + case 0xc1: + return DeserializationError::InvalidInput; + case 0xc2: + if (allowValue) { + variant->setBoolean(false); + } + return DeserializationError::Ok; + case 0xc3: + if (allowValue) { + variant->setBoolean(true); + } + return DeserializationError::Ok; + case 0xc4: // bin 8 (not supported) + return skipString(); + case 0xc5: // bin 16 (not supported) + return skipString(); + case 0xc6: // bin 32 (not supported) + return skipString(); + case 0xc7: // ext 8 (not supported) + return skipExt(); + case 0xc8: // ext 16 (not supported) + return skipExt(); + case 0xc9: // ext 32 (not supported) + return skipExt(); + case 0xca: + if (allowValue) { + return readFloat(variant); + } else { + return skipBytes(4); + } + case 0xcb: + if (allowValue) { + return readDouble(variant); + } else { + return skipBytes(8); + } + case 0xcc: + if (allowValue) { + return readInteger(variant); + } else { + return skipBytes(1); + } + case 0xcd: + if (allowValue) { + return readInteger(variant); + } else { + return skipBytes(2); + } + case 0xce: + if (allowValue) { + return readInteger(variant); + } else { + return skipBytes(4); + } + case 0xcf: +#if ARDUINOJSON_USE_LONG_LONG + if (allowValue) { + return readInteger(variant); + } else { + return skipBytes(8); + } +#else + return skipBytes(8); // not supported +#endif + case 0xd0: + if (allowValue) { + return readInteger(variant); + } else { + return skipBytes(1); + } + case 0xd1: + if (allowValue) { + return readInteger(variant); + } else { + return skipBytes(2); + } + case 0xd2: + if (allowValue) { + return readInteger(variant); + } else { + return skipBytes(4); + } + case 0xd3: +#if ARDUINOJSON_USE_LONG_LONG + if (allowValue) { + return readInteger(variant); + } else { + return skipBytes(8); // not supported + } +#else + return skipBytes(8); +#endif + case 0xd4: // fixext 1 (not supported) + return skipBytes(2); + case 0xd5: // fixext 2 (not supported) + return skipBytes(3); + case 0xd6: // fixext 4 (not supported) + return skipBytes(5); + case 0xd7: // fixext 8 (not supported) + return skipBytes(9); + case 0xd8: // fixext 16 (not supported) + return skipBytes(17); + case 0xd9: + if (allowValue) { + return readString(variant); + } else { + return skipString(); + } + case 0xda: + if (allowValue) { + return readString(variant); + } else { + return skipString(); + } + case 0xdb: + if (allowValue) { + return readString(variant); + } else { + return skipString(); + } + case 0xdc: + return readArray(variant, filter, nestingLimit); + case 0xdd: + return readArray(variant, filter, nestingLimit); + case 0xde: + return readObject(variant, filter, nestingLimit); + case 0xdf: + return readObject(variant, filter, nestingLimit); + } + switch (code & 0xf0) { + case 0x80: + return readObject(variant, code & 0x0F, filter, nestingLimit); + case 0x90: + return readArray(variant, code & 0x0F, filter, nestingLimit); + } + if ((code & 0xe0) == 0xa0) { + if (allowValue) { + return readString(variant, code & 0x1f); + } else { + return skipBytes(code & 0x1f); + } + } + if (allowValue) { + variant->setInteger(static_cast(code)); + } + return DeserializationError::Ok; + } + DeserializationError::Code readByte(uint8_t& value) + { + int c = reader_.read(); + if (c < 0) { + return DeserializationError::IncompleteInput; + } + value = static_cast(c); + return DeserializationError::Ok; + } + DeserializationError::Code readBytes(uint8_t* p, size_t n) + { + if (reader_.readBytes(reinterpret_cast(p), n) == n) { + return DeserializationError::Ok; + } + return DeserializationError::IncompleteInput; + } + template + DeserializationError::Code readBytes(T& value) + { + return readBytes(reinterpret_cast(&value), sizeof(value)); + } + DeserializationError::Code skipBytes(size_t n) + { + for (; n; --n) { + if (reader_.read() < 0) { + return DeserializationError::IncompleteInput; + } + } + return DeserializationError::Ok; + } + template + DeserializationError::Code readInteger(T& value) + { + DeserializationError::Code err; + err = readBytes(value); + if (err) { + return err; + } + fixEndianess(value); + return DeserializationError::Ok; + } + template + DeserializationError::Code readInteger(VariantData* variant) + { + DeserializationError::Code err; + T value; + err = readInteger(value); + if (err) { + return err; + } + variant->setInteger(value); + return DeserializationError::Ok; + } + template + typename enable_if::type + readFloat(VariantData* variant) + { + DeserializationError::Code err; + T value; + err = readBytes(value); + if (err) { + return err; + } + fixEndianess(value); + variant->setFloat(value); + return DeserializationError::Ok; + } + template + typename enable_if::type + readDouble(VariantData* variant) + { + DeserializationError::Code err; + T value; + err = readBytes(value); + if (err) { + return err; + } + fixEndianess(value); + variant->setFloat(value); + return DeserializationError::Ok; + } + template + typename enable_if::type + readDouble(VariantData* variant) + { + DeserializationError::Code err; + uint8_t i[8]; // input is 8 bytes + T value; // output is 4 bytes + uint8_t* o = reinterpret_cast(&value); + err = readBytes(i, 8); + if (err) { + return err; + } + doubleToFloat(i, o); + fixEndianess(value); + variant->setFloat(value); + return DeserializationError::Ok; + } + template + DeserializationError::Code readString(VariantData* variant) + { + DeserializationError::Code err; + T size; + err = readInteger(size); + if (err) { + return err; + } + return readString(variant, size); + } + template + DeserializationError::Code readString() + { + DeserializationError::Code err; + T size; + err = readInteger(size); + if (err) { + return err; + } + return readString(size); + } + template + DeserializationError::Code skipString() + { + DeserializationError::Code err; + T size; + err = readInteger(size); + if (err) { + return err; + } + return skipBytes(size); + } + DeserializationError::Code readString(VariantData* variant, size_t n) + { + DeserializationError::Code err; + err = readString(n); + if (err) { + return err; + } + variant->setOwnedString(stringBuilder_.save()); + return DeserializationError::Ok; + } + DeserializationError::Code readString(size_t n) + { + DeserializationError::Code err; + stringBuilder_.startString(); + for (; n; --n) { + uint8_t c; + err = readBytes(c); + if (err) { + return err; + } + stringBuilder_.append(static_cast(c)); + } + if (!stringBuilder_.isValid()) { + return DeserializationError::NoMemory; + } + return DeserializationError::Ok; + } + template + DeserializationError::Code readArray( + VariantData* variant, TFilter filter, + DeserializationOption::NestingLimit nestingLimit) + { + DeserializationError::Code err; + TSize size; + err = readInteger(size); + if (err) { + return err; + } + return readArray(variant, size, filter, nestingLimit); + } + template + DeserializationError::Code readArray( + VariantData* variant, size_t n, TFilter filter, + DeserializationOption::NestingLimit nestingLimit) + { + DeserializationError::Code err; + if (nestingLimit.reached()) { + return DeserializationError::TooDeep; + } + bool allowArray = filter.allowArray(); + ArrayData* array; + if (allowArray) { + ARDUINOJSON_ASSERT(variant != 0); + array = &variant->toArray(); + } else { + array = 0; + } + TFilter elementFilter = filter[0U]; + for (; n; --n) { + VariantData* value; + if (elementFilter.allow()) { + ARDUINOJSON_ASSERT(array != 0); + value = array->addElement(resources_); + if (!value) { + return DeserializationError::NoMemory; + } + } else { + value = 0; + } + err = parseVariant(value, elementFilter, nestingLimit.decrement()); + if (err) { + return err; + } + } + return DeserializationError::Ok; + } + template + DeserializationError::Code readObject( + VariantData* variant, TFilter filter, + DeserializationOption::NestingLimit nestingLimit) + { + DeserializationError::Code err; + TSize size; + err = readInteger(size); + if (err) { + return err; + } + return readObject(variant, size, filter, nestingLimit); + } + template + DeserializationError::Code readObject( + VariantData* variant, size_t n, TFilter filter, + DeserializationOption::NestingLimit nestingLimit) + { + DeserializationError::Code err; + if (nestingLimit.reached()) { + return DeserializationError::TooDeep; + } + ObjectData* object; + if (filter.allowObject()) { + ARDUINOJSON_ASSERT(variant != 0); + object = &variant->toObject(); + } else { + object = 0; + } + for (; n; --n) { + err = readKey(); + if (err) { + return err; + } + JsonString key = stringBuilder_.str(); + TFilter memberFilter = filter[key.c_str()]; + VariantData* member; + if (memberFilter.allow()) { + ARDUINOJSON_ASSERT(object != 0); + auto savedKey = stringBuilder_.save(); + member = object->addMember(savedKey, resources_); + if (!member) { + return DeserializationError::NoMemory; + } + } else { + member = 0; + } + err = parseVariant(member, memberFilter, nestingLimit.decrement()); + if (err) { + return err; + } + } + return DeserializationError::Ok; + } + DeserializationError::Code readKey() + { + DeserializationError::Code err; + uint8_t code; + err = readByte(code); + if (err) { + return err; + } + if ((code & 0xe0) == 0xa0) { + return readString(code & 0x1f); + } + switch (code) { + case 0xd9: + return readString(); + case 0xda: + return readString(); + case 0xdb: + return readString(); + default: + return DeserializationError::InvalidInput; + } + } + template + DeserializationError::Code skipExt() + { + DeserializationError::Code err; + T size; + err = readInteger(size); + if (err) { + return err; + } + return skipBytes(size + 1U); + } + ResourceManager* resources_; + TReader reader_; + StringBuilder stringBuilder_; + bool foundSomething_; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +template +typename detail::enable_if< + detail::is_deserialize_destination::value, + DeserializationError>::type +deserializeMsgPack(TDestination&& dst, Args&&... args) +{ + using namespace detail; + return deserialize(detail::forward(dst), + detail::forward(args)...); +} +template +typename detail::enable_if< + detail::is_deserialize_destination::value, + DeserializationError>::type +deserializeMsgPack(TDestination&& dst, TChar* input, Args&&... args) +{ + using namespace detail; + return deserialize(detail::forward(dst), + input, + detail::forward(args)...); +} +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +class MsgPackSerializer: public VariantDataVisitor { +public: + static const bool producesText = false; + MsgPackSerializer(TWriter writer, const ResourceManager* resources): + writer_(writer), + resources_(resources) + {} + template + typename enable_if::value && sizeof(T) == 4, + size_t>::type + visit(T value32) + { + if (canConvertNumber(value32)) { + JsonInteger truncatedValue = JsonInteger(value32); + if (value32 == T(truncatedValue)) { + return visit(truncatedValue); + } + } + writeByte(0xCA); + writeInteger(value32); + return bytesWritten(); + } + template + ARDUINOJSON_NO_SANITIZE("float-cast-overflow") + typename enable_if::value && sizeof(T) == 8, + size_t>::type visit(T value64) + { + float value32 = float(value64); + if (value32 == value64) { + return visit(value32); + } + writeByte(0xCB); + writeInteger(value64); + return bytesWritten(); + } + size_t visit(const ArrayData& array) + { + size_t n = array.size(resources_); + if (n < 0x10) { + writeByte(uint8_t(0x90 + n)); + } else if (n < 0x10000) { + writeByte(0xDC); + writeInteger(uint16_t(n)); + } else { + writeByte(0xDD); + writeInteger(uint32_t(n)); + } + auto slotId = array.head(); + while (slotId != NULL_SLOT) { + auto slot = resources_->getSlot(slotId); + slot->data()->accept(*this); + slotId = slot->next(); + } + return bytesWritten(); + } + size_t visit(const ObjectData& object) + { + size_t n = object.size(resources_); + if (n < 0x10) { + writeByte(uint8_t(0x80 + n)); + } else if (n < 0x10000) { + writeByte(0xDE); + writeInteger(uint16_t(n)); + } else { + writeByte(0xDF); + writeInteger(uint32_t(n)); + } + auto slotId = object.head(); + while (slotId != NULL_SLOT) { + auto slot = resources_->getSlot(slotId); + visit(slot->key()); + slot->data()->accept(*this); + slotId = slot->next(); + } + return bytesWritten(); + } + size_t visit(const char* value) + { + return visit(JsonString(value)); + } + size_t visit(JsonString value) + { + ARDUINOJSON_ASSERT(value != NULL); + auto n = value.size(); + if (n < 0x20) { + writeByte(uint8_t(0xA0 + n)); + } else if (n < 0x100) { + writeByte(0xD9); + writeInteger(uint8_t(n)); + } else if (n < 0x10000) { + writeByte(0xDA); + writeInteger(uint16_t(n)); + } else { + writeByte(0xDB); + writeInteger(uint32_t(n)); + } + writeBytes(reinterpret_cast(value.c_str()), n); + return bytesWritten(); + } + size_t visit(RawString value) + { + writeBytes(reinterpret_cast(value.data()), value.size()); + return bytesWritten(); + } + size_t visit(JsonInteger value) + { + if (value > 0) { + visit(static_cast(value)); + } else if (value >= -0x20) { + writeInteger(int8_t(value)); + } else if (value >= -0x80) { + writeByte(0xD0); + writeInteger(int8_t(value)); + } else if (value >= -0x8000) { + writeByte(0xD1); + writeInteger(int16_t(value)); + } +#if ARDUINOJSON_USE_LONG_LONG + else if (value >= -0x80000000LL) +#else + else +#endif + { + writeByte(0xD2); + writeInteger(int32_t(value)); + } +#if ARDUINOJSON_USE_LONG_LONG + else { + writeByte(0xD3); + writeInteger(int64_t(value)); + } +#endif + return bytesWritten(); + } + size_t visit(JsonUInt value) + { + if (value <= 0x7F) { + writeInteger(uint8_t(value)); + } else if (value <= 0xFF) { + writeByte(0xCC); + writeInteger(uint8_t(value)); + } else if (value <= 0xFFFF) { + writeByte(0xCD); + writeInteger(uint16_t(value)); + } +#if ARDUINOJSON_USE_LONG_LONG + else if (value <= 0xFFFFFFFF) +#else + else +#endif + { + writeByte(0xCE); + writeInteger(uint32_t(value)); + } +#if ARDUINOJSON_USE_LONG_LONG + else { + writeByte(0xCF); + writeInteger(uint64_t(value)); + } +#endif + return bytesWritten(); + } + size_t visit(bool value) + { + writeByte(value ? 0xC3 : 0xC2); + return bytesWritten(); + } + size_t visit(nullptr_t) + { + writeByte(0xC0); + return bytesWritten(); + } +private: + size_t bytesWritten() const + { + return writer_.count(); + } + void writeByte(uint8_t c) + { + writer_.write(c); + } + void writeBytes(const uint8_t* p, size_t n) + { + writer_.write(p, n); + } + template + void writeInteger(T value) + { + fixEndianess(value); + writeBytes(reinterpret_cast(&value), sizeof(value)); + } + CountingDecorator writer_; + const ResourceManager* resources_; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +template +inline size_t serializeMsgPack(JsonVariantConst source, TDestination& output) +{ + using namespace ArduinoJson::detail; + return serialize(source, output); +} +inline size_t serializeMsgPack(JsonVariantConst source, void* output, + size_t size) +{ + using namespace ArduinoJson::detail; + return serialize(source, output, size); +} +inline size_t measureMsgPack(JsonVariantConst source) +{ + using namespace ArduinoJson::detail; + return measure(source); +} +ARDUINOJSON_END_PUBLIC_NAMESPACE +#ifdef ARDUINOJSON_SLOT_OFFSET_SIZE +#error ARDUINOJSON_SLOT_OFFSET_SIZE has been removed, use ARDUINOJSON_SLOT_ID_SIZE instead +#endif +#ifdef ARDUINOJSON_ENABLE_STRING_DEDUPLICATION +#warning "ARDUINOJSON_ENABLE_STRING_DEDUPLICATION has been removed, string deduplication is now always enabled" +#endif +#ifdef __GNUC__ +#define ARDUINOJSON_PRAGMA(x) _Pragma(#x) +#define ARDUINOJSON_COMPILE_ERROR(msg) ARDUINOJSON_PRAGMA(GCC error msg) +#define ARDUINOJSON_STRINGIFY(S) #S +#define ARDUINOJSON_DEPRECATION_ERROR(X, Y) \ + ARDUINOJSON_COMPILE_ERROR(ARDUINOJSON_STRINGIFY(X is a Y from ArduinoJson 5. Please see https : /\/ arduinojson.org / v7 / upgrade - from - v5 / to learn how to upgrade to ArduinoJson 7)) +#define StaticJsonBuffer ARDUINOJSON_DEPRECATION_ERROR(StaticJsonBuffer, class) +#define DynamicJsonBuffer ARDUINOJSON_DEPRECATION_ERROR(DynamicJsonBuffer, class) +#define JsonBuffer ARDUINOJSON_DEPRECATION_ERROR(JsonBuffer, class) +#define RawJson ARDUINOJSON_DEPRECATION_ERROR(RawJson, function) +#define ARDUINOJSON_NAMESPACE _Pragma("GCC warning \"ARDUINOJSON_NAMESPACE is deprecated, use ArduinoJson instead\"") ArduinoJson +#define JSON_ARRAY_SIZE(N) _Pragma("GCC warning \"JSON_ARRAY_SIZE is deprecated, you don't need to compute the size anymore\"")(ArduinoJson::detail::sizeofArray(N)) +#define JSON_OBJECT_SIZE(N) _Pragma("GCC warning \"JSON_OBJECT_SIZE is deprecated, you don't need to compute the size anymore\"")(ArduinoJson::detail::sizeofObject(N)) +#define JSON_STRING_SIZE(N) _Pragma("GCC warning \"JSON_STRING_SIZE is deprecated, you don't need to compute the size anymore\"")(N + 1) +#else +#define JSON_ARRAY_SIZE(N) (ArduinoJson::detail::sizeofArray(N)) +#define JSON_OBJECT_SIZE(N) (ArduinoJson::detail::sizeofObject(N)) +#define JSON_STRING_SIZE(N) (N + 1) +#endif +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +template +class ARDUINOJSON_DEPRECATED("use JsonDocument instead") StaticJsonDocument: public JsonDocument { +public: + using JsonDocument::JsonDocument; + size_t capacity() const + { + return N; + } +}; +namespace detail { + template + class AllocatorAdapter: public Allocator { + public: + AllocatorAdapter(const AllocatorAdapter&) = delete; + AllocatorAdapter& operator=(const AllocatorAdapter&) = delete; + void* allocate(size_t size) override + { + return _allocator.allocate(size); + } + void deallocate(void* ptr) override + { + _allocator.deallocate(ptr); + } + void* reallocate(void* ptr, size_t new_size) override + { + return _allocator.reallocate(ptr, new_size); + } + static Allocator* instance() + { + static AllocatorAdapter instance; + return &instance; + } + private: + AllocatorAdapter() = default; + ~AllocatorAdapter() = default; + TAllocator _allocator; + }; +} // namespace detail +template +class ARDUINOJSON_DEPRECATED("use JsonDocument instead") BasicJsonDocument: public JsonDocument { +public: + BasicJsonDocument(size_t capacity): + JsonDocument(detail::AllocatorAdapter::instance()), + _capacity(capacity) + {} + size_t capacity() const + { + return _capacity; + } + void garbageCollect() + {} +private: + size_t _capacity; +}; +class ARDUINOJSON_DEPRECATED("use JsonDocument instead") DynamicJsonDocument: public JsonDocument { +public: + DynamicJsonDocument(size_t capacity): + _capacity(capacity) + {} + size_t capacity() const + { + return _capacity; + } + void garbageCollect() + {} +private: + size_t _capacity; +}; +inline JsonObject JsonArray::createNestedObject() const +{ + return add(); +} +ARDUINOJSON_END_PUBLIC_NAMESPACE + +using namespace ArduinoJson; + +#else + +#error ArduinoJson requires a C++ compiler, please change file extension to .cc or .cpp + +#endif diff --git a/Arduino_package/hardware/cores/ambd/ard_socket.c b/Arduino_package/hardware/cores/ambd/ard_socket.c index 32899f1a..7026f6d1 100644 --- a/Arduino_package/hardware/cores/ambd/ard_socket.c +++ b/Arduino_package/hardware/cores/ambd/ard_socket.c @@ -327,7 +327,7 @@ int get_available(int sock) { } while (client_fd < 0); if (client_fd < 0) { - printf("\r\n [ERROR] Accept connection failed\n"); + // printf("\r\n [ERROR] Accept connection failed\n"); return -1; } else { timeout = 3000; diff --git a/Arduino_package/hardware/cores/ambd/server_drv.cpp b/Arduino_package/hardware/cores/ambd/server_drv.cpp index e4290464..f8e80545 100644 --- a/Arduino_package/hardware/cores/ambd/server_drv.cpp +++ b/Arduino_package/hardware/cores/ambd/server_drv.cpp @@ -7,40 +7,31 @@ extern "C" { } #endif -int ServerDrv::startClient(uint32_t ipAddress, uint16_t port, uint8_t portMode) { - printf("\n\r[INFO]server_drv.cpp: start_client"); +int ServerDrv::startClient(uint32_t ipAddress, uint16_t port, uint8_t portMode, tBlockingMode blockMode) +{ + // printf("\n\r[INFO]server_drv.cpp: start_client"); int sock; - - sock = start_client(ipAddress, port, portMode); - - return sock; -} - -int ServerDrv::startClientV6(const char *ipv6Address, uint16_t port, uint8_t portMode) { - printf("\n\r[INFO]server_drv.cpp startClientV6() ipv6 addr: %s\n\r", ipv6Address); - int sock; - - sock = start_client_v6((char *)ipv6Address, port, portMode); - - return sock; -} - -int ServerDrv::startClientv6(uint32_t *ipv6Address, uint16_t port, uint8_t portMode) { - int sock; - sock = start_clientv6(ipv6Address, port, portMode); - printf("\n\r [INFO]server_drv.cpp: startClientv6() sock value: %x\n\r", sock); + if (blockMode == BLOCKING_MODE) { + // printf("\r\n[INFO] %s WiFi client is set to blocking mode \n", __FUNCTION__); + sock = start_client(ipAddress, port, portMode); + } else { + // printf("\r\n[INFO] %s WiFi client is set to non-blocking mode \n", __FUNCTION__); + sock = start_client(ipAddress, port, portMode); + set_nonblocking(sock); + } return sock; } -int ServerDrv::startServer(uint16_t port, uint8_t portMode, bool blockMode) { +int ServerDrv::startServer(uint16_t port, uint8_t portMode, tBlockingMode blockMode) +{ int sock; - if (blockMode) { + if (blockMode == BLOCKING_MODE) { printf("\r\n [INFO] server_drv.cpp: WiFi server is set to blocking mode\r\n"); if (getIPv6Status() == 0) { sock = start_server(port, portMode); if (sock >= 0) { if (portMode == TCP_MODE) { - //Make it listen to socket with max 20 connections + // Make it listen to socket with max 20 connections sock_listen(sock, 1); } } @@ -48,7 +39,7 @@ int ServerDrv::startServer(uint16_t port, uint8_t portMode, bool blockMode) { sock = start_server_v6(port, portMode); if (sock >= 0) { if (portMode == TCP_MODE) { - //Make it listen to socket with max 20 connections + // Make it listen to socket with max 20 connections sock_listen(sock, 20); } } @@ -60,7 +51,7 @@ int ServerDrv::startServer(uint16_t port, uint8_t portMode, bool blockMode) { set_nonblocking(sock); if (sock >= 0) { if (portMode == TCP_MODE) { - //Make it listen to socket with max 20 connections + // Make it listen to socket with max 20 connections sock_listen(sock, 1); } } @@ -69,7 +60,7 @@ int ServerDrv::startServer(uint16_t port, uint8_t portMode, bool blockMode) { set_nonblocking(sock); if (sock >= 0) { if (portMode == TCP_MODE) { - //Make it listen to socket with max 20 connections + // Make it listen to socket with max 20 connections sock_listen(sock, 20); } } @@ -78,7 +69,26 @@ int ServerDrv::startServer(uint16_t port, uint8_t portMode, bool blockMode) { return sock; } -int ServerDrv::getAvailable(int sock) { +int ServerDrv::startClientV6(const char *ipv6Address, uint16_t port, uint8_t portMode) +{ + printf("\n\r[INFO]server_drv.cpp startClientV6() ipv6 addr: %s\n\r", ipv6Address); + int sock; + + sock = start_client_v6((char *)ipv6Address, port, portMode); + + return sock; +} + +int ServerDrv::startClientv6(uint32_t *ipv6Address, uint16_t port, uint8_t portMode) +{ + int sock; + sock = start_clientv6(ipv6Address, port, portMode); + printf("\n\r [INFO]server_drv.cpp: startClientv6() sock value: %x\n\r", sock); + return sock; +} + +int ServerDrv::getAvailable(int sock) +{ if (getIPv6Status() == 0) { return get_available(sock); } else { @@ -86,7 +96,8 @@ int ServerDrv::getAvailable(int sock) { } } -int ServerDrv::availData(int sock) { +int ServerDrv::availData(int sock) +{ int ret; uint8_t c; if (sock < 0) { @@ -106,7 +117,8 @@ int ServerDrv::availData(int sock) { } } -bool ServerDrv::recvData(int sock, uint8_t *_data, uint16_t _dataLen) { +bool ServerDrv::recvData(int sock, uint8_t *_data, uint16_t _dataLen) +{ int ret; _available = false; @@ -115,7 +127,8 @@ bool ServerDrv::recvData(int sock, uint8_t *_data, uint16_t _dataLen) { return ret; } -bool ServerDrv::getData(int sock, uint8_t *data, uint8_t peek) { +bool ServerDrv::getData(int sock, uint8_t *data, uint8_t peek) +{ int ret = 0; int flag = 0; @@ -134,7 +147,8 @@ bool ServerDrv::getData(int sock, uint8_t *data, uint8_t peek) { return false; } -int ServerDrv::getDataBuf(int sock, uint8_t *_data, uint16_t _dataLen) { +int ServerDrv::getDataBuf(int sock, uint8_t *_data, uint16_t _dataLen) +{ int ret; _available = false; @@ -147,17 +161,20 @@ int ServerDrv::getDataBuf(int sock, uint8_t *_data, uint16_t _dataLen) { return ret; } -int ServerDrv::getLastErrno(int sock) { +int ServerDrv::getLastErrno(int sock) +{ return get_sock_errno(sock); } -void ServerDrv::stopSocket(int sock) { +void ServerDrv::stopSocket(int sock) +{ close_socket(sock); _available = false; } -bool ServerDrv::sendData(int sock, const uint8_t *data, uint16_t len) { - //printf("[info] server_drv.cpp sendData()"); +bool ServerDrv::sendData(int sock, const uint8_t *data, uint16_t len) +{ + // printf("[info] server_drv.cpp sendData()"); int ret; int flag = 0; @@ -173,7 +190,8 @@ bool ServerDrv::sendData(int sock, const uint8_t *data, uint16_t len) { return true; } -bool ServerDrv::sendtoData(int sock, const uint8_t *data, uint16_t len, uint32_t peer_ip, uint16_t peer_port) { +bool ServerDrv::sendtoData(int sock, const uint8_t *data, uint16_t len, uint32_t peer_ip, uint16_t peer_port) +{ int ret; if (sock < 0) { @@ -191,24 +209,29 @@ bool ServerDrv::sendtoData(int sock, const uint8_t *data, uint16_t len, uint32_t return true; } -void ServerDrv::getRemoteData(int sock, uint32_t *ip, uint16_t *port) { +void ServerDrv::getRemoteData(int sock, uint32_t *ip, uint16_t *port) +{ sock = sock; *ip = _peer_addr; *port = _peer_port; } -int ServerDrv::setSockRecvTimeout(int sock, int timeout) { +int ServerDrv::setSockRecvTimeout(int sock, int timeout) +{ return set_sock_recv_timeout(sock, timeout); } -int ServerDrv::enableIPv6() { +int ServerDrv::enableIPv6() +{ return enable_ipv6(); } -int ServerDrv::getIPv6Status() { +int ServerDrv::getIPv6Status() +{ return get_ipv6_status(); } -void ServerDrv::setIPv6UDPServer(void) { +void ServerDrv::setIPv6UDPServer(void) +{ ipv6_udp_server(); } diff --git a/Arduino_package/hardware/cores/ambd/server_drv.h b/Arduino_package/hardware/cores/ambd/server_drv.h index 86416e02..38a32674 100644 --- a/Arduino_package/hardware/cores/ambd/server_drv.h +++ b/Arduino_package/hardware/cores/ambd/server_drv.h @@ -21,37 +21,33 @@ #define SERVER_DRV_H #include - -typedef enum eProtMode { - TCP_MODE, - UDP_MODE -} tProtMode; +#include "wl_definitions.h" #define DATA_LENTH 128 class ServerDrv { - public: - int startClient(uint32_t ipAddress, uint16_t port, uint8_t portMode = TCP_MODE); - int startServer(uint16_t port, uint8_t portMode = TCP_MODE, bool blockMode = false); - int startClientv6(uint32_t *ipv6Address, uint16_t port, uint8_t portMode = TCP_MODE); - int startClientV6(const char *ipv6Address, uint16_t port, uint8_t portMode); - int getAvailable(int sock); - int availData(int sock); - bool recvData(int sock, uint8_t *_data, uint16_t _dataLen); - bool getData(int sock, uint8_t *data, uint8_t peek = 0); - int getDataBuf(int sock, uint8_t *_data, uint16_t _dataLen); - int getLastErrno(int sock); - void stopSocket(int sock); - bool sendData(int sock, const uint8_t *data, uint16_t len); - bool sendtoData(int sock, const uint8_t *data, uint16_t len, uint32_t peer_ip, uint16_t peer_port); - void getRemoteData(int sock, uint32_t *ip, uint16_t *port); - int setSockRecvTimeout(int sock, int timeout); - static int enableIPv6(); - static int getIPv6Status(); - void setIPv6UDPServer(void); - - private: - bool _available; - uint32_t _peer_addr; - uint16_t _peer_port; +public: + int startClient(uint32_t ipAddress, uint16_t port, uint8_t portMode = TCP_MODE, tBlockingMode blockMode = NON_BLOCKING_MODE); + int startServer(uint16_t port, uint8_t portMode = TCP_MODE, tBlockingMode blockMode = NON_BLOCKING_MODE); + int startClientv6(uint32_t *ipv6Address, uint16_t port, uint8_t portMode = TCP_MODE); + int startClientV6(const char *ipv6Address, uint16_t port, uint8_t portMode); + int getAvailable(int sock); + int availData(int sock); + bool recvData(int sock, uint8_t *_data, uint16_t _dataLen); + bool getData(int sock, uint8_t *data, uint8_t peek = 0); + int getDataBuf(int sock, uint8_t *_data, uint16_t _dataLen); + int getLastErrno(int sock); + void stopSocket(int sock); + bool sendData(int sock, const uint8_t *data, uint16_t len); + bool sendtoData(int sock, const uint8_t *data, uint16_t len, uint32_t peer_ip, uint16_t peer_port); + void getRemoteData(int sock, uint32_t *ip, uint16_t *port); + int setSockRecvTimeout(int sock, int timeout); + static int enableIPv6(); + static int getIPv6Status(); + void setIPv6UDPServer(void); + +private: + bool _available; + uint32_t _peer_addr; + uint16_t _peer_port; }; #endif diff --git a/Arduino_package/hardware/cores/ambd/wl_definitions.h b/Arduino_package/hardware/cores/ambd/wl_definitions.h index 9fd72106..71fa3998 100644 --- a/Arduino_package/hardware/cores/ambd/wl_definitions.h +++ b/Arduino_package/hardware/cores/ambd/wl_definitions.h @@ -27,25 +27,25 @@ #define WL_DEFINITIONS_H // Maximum size of a SSID -#define WL_SSID_MAX_LENGTH 32 +#define WL_SSID_MAX_LENGTH 32 // Length of passphrase. Valid lengths are 8-63. -#define WL_WPA_KEY_MAX_LENGTH 63 +#define WL_WPA_KEY_MAX_LENGTH 63 // Length of key in bytes. Valid values are 5 and 13. -#define WL_WEP_KEY_MAX_LENGTH 13 +#define WL_WEP_KEY_MAX_LENGTH 13 // Size of a MAC-address or BSSID -#define WL_MAC_ADDR_LENGTH 6 +#define WL_MAC_ADDR_LENGTH 6 // Size of a MAC-address or BSSID -#define WL_IPV4_LENGTH 4 +#define WL_IPV4_LENGTH 4 // Maximum size of a SSID list -#define WL_NETWORKS_LIST_MAXNUM 50 +#define WL_NETWORKS_LIST_MAXNUM 50 // Maximum number of socket -#define MAX_SOCK_NUM 4 +#define MAX_SOCK_NUM 4 // Socket not available constant -#define SOCK_NOT_AVAIL 255 +#define SOCK_NOT_AVAIL 255 // Default state value for Wifi state field -#define NA_STATE -1 +#define NA_STATE -1 // Maximum number of attempts to establish wifi connection -#define WL_MAX_ATTEMPT_CONNECTION 10 +#define WL_MAX_ATTEMPT_CONNECTION 10 typedef enum { WL_NO_SHIELD = 255, @@ -59,9 +59,9 @@ typedef enum { } wl_status_t; /* Encryption modes */ -enum wl_enc_type { /* Values map to 802.11 encryption suites... */ - ENC_TYPE_WEP = 5, - ENC_TYPE_WPA = 2, +enum wl_enc_type { /* Values map to 802.11 encryption suites... */ + ENC_TYPE_WEP = 5, + ENC_TYPE_WPA = 2, ENC_TYPE_WPA3 = 3, ENC_TYPE_WPA2 = 4, /* ... except these two, 7 and 8 are reserved in 802.11-2007 */ @@ -72,30 +72,40 @@ enum wl_enc_type { /* Values map to 802.11 encryption suites... */ /* RTK added type */ #ifndef WEP_ENABLED -#define WEP_ENABLED 0x0001 -#define TKIP_ENABLED 0x0002 -#define AES_ENABLED 0x0004 -#define WSEC_SWFLAG 0x0008 +#define WEP_ENABLED 0x0001 +#define TKIP_ENABLED 0x0002 +#define AES_ENABLED 0x0004 +#define WSEC_SWFLAG 0x0008 -#define SHARED_ENABLED 0x00008000 -#define WPA_SECURITY 0x00200000 -#define WPA2_SECURITY 0x00400000 -#define WPA3_SECURITY 0x00800000 -#define WPS_ENABLED 0x10000000 +#define SHARED_ENABLED 0x00008000 +#define WPA_SECURITY 0x00200000 +#define WPA2_SECURITY 0x00400000 +#define WPA3_SECURITY 0x00800000 +#define WPS_ENABLED 0x10000000 -#endif // #ifndef WEP_ENABLED +#endif // #ifndef WEP_ENABLED /* redefined from enum rtw_security_t */ #define SECURITY_OPEN (0) #define SECURITY_WEP_PSK (WEP_ENABLED) #define SECURITY_WEP_SHARED (WEP_ENABLED | SHARED_ENABLED) -#define SECURITY_WPA_TKIP_PSK (WPA_SECURITY | TKIP_ENABLED) -#define SECURITY_WPA_AES_PSK (WPA_SECURITY | AES_ENABLED) +#define SECURITY_WPA_TKIP_PSK (WPA_SECURITY | TKIP_ENABLED) +#define SECURITY_WPA_AES_PSK (WPA_SECURITY | AES_ENABLED) #define SECURITY_WPA2_AES_PSK (WPA2_SECURITY | AES_ENABLED) #define SECURITY_WPA2_TKIP_PSK (WPA2_SECURITY | TKIP_ENABLED) #define SECURITY_WPA2_MIXED_PSK (WPA2_SECURITY | AES_ENABLED | TKIP_ENABLED) -#define SECURITY_WPA_WPA2_MIXED (WPA_SECURITY | WPA2_SECURITY) +#define SECURITY_WPA_WPA2_MIXED (WPA_SECURITY | WPA2_SECURITY) #define SECURITY_WPA3_AES_PSK (WPA3_SECURITY | AES_ENABLED) #define SECURITY_WPA2_WPA3_MIXED (WPA2_SECURITY | WPA3_SECURITY | AES_ENABLED) +typedef enum eProtMode { + TCP_MODE, + UDP_MODE +} tPortMode; + +typedef enum eBlockingMode { + BLOCKING_MODE, + NON_BLOCKING_MODE +} tBlockingMode; + #endif /* WL_DEFINITIONS_H_ */ diff --git a/Arduino_package/hardware/libraries/OTA/examples/OTA_Http/OTA_Http.ino b/Arduino_package/hardware/libraries/OTA/examples/OTA_Http/OTA_Http.ino new file mode 100644 index 00000000..bef70f07 --- /dev/null +++ b/Arduino_package/hardware/libraries/OTA/examples/OTA_Http/OTA_Http.ino @@ -0,0 +1,32 @@ +#include "OTA.h" +#include "WiFi.h" + +char ssid[] = "Network_SSID5"; // your network SSID (name) +char pass[] = "Password"; // your network password +int status = WL_IDLE_STATUS; + +int PORT = 3000; // your server port number +char *SERVER = "192.168.3.14"; // your server ip address + +class OTA ota; + +void setup() +{ + Serial.begin(115200); + + // Connection to internet + while (status != WL_CONNECTED) { + Serial.print("Attempting to connect to WPA SSID: "); + Serial.println(ssid); + status = WiFi.begin(ssid, pass); + delay(2000); + } + + // Set up the threads + ota.start_OTA_threads(PORT, SERVER); +} + +void loop() +{ + // Empty or add non-blocking code here +} diff --git a/Arduino_package/hardware/libraries/OTA/keyword.txt b/Arduino_package/hardware/libraries/OTA/keyword.txt index 78f369f8..1b4cd3d0 100644 --- a/Arduino_package/hardware/libraries/OTA/keyword.txt +++ b/Arduino_package/hardware/libraries/OTA/keyword.txt @@ -17,8 +17,9 @@ OTA KEYWORD1 ota # Methods and Functions (KEYWORD2) ####################################### -beginOTA KEYWORD2 -syncOTAAddr KEYWORD2 +beginOTA KEYWORD2 +start_OTA_threads KEYWORD2 +syncOTAAddr KEYWORD2 ####################################### # Constants (LITERAL1) diff --git a/Arduino_package/hardware/libraries/OTA/src/OTA.cpp b/Arduino_package/hardware/libraries/OTA/src/OTA.cpp index b41ec3ef..0d2d1563 100644 --- a/Arduino_package/hardware/libraries/OTA/src/OTA.cpp +++ b/Arduino_package/hardware/libraries/OTA/src/OTA.cpp @@ -8,10 +8,33 @@ extern "C" { } #endif +// DO NOT MODIFY +char *OTA::OtaState[5] = {"OTA_STATE_IDLE", "OTA_STATE_RECEIVED_START_SIGNAL", + "OTA_STATE_DOWNLOAD_FIRMWARE_IN_PROGRESS", + "OTA_STATE_DOWNLOAD_FIRMWARE_COMPLETED", + "OTA_STATE_REBOOT"}; + +uint16_t OTA::_port = 3000; +char *OTA::_server = "192.168.13.14"; +char *OTA::g_otaState = OtaState[0]; +char *OTA::_resource = "api/uploadfile"; + +JsonDocument OTA::doc; +String OTA::jsonString; +char OTA::buffer[1024]; + OTA::OTA(){}; OTA::~OTA(){}; +/*********************************************************************************** + * + * + * MDNS OTA Update + * + * + ***********************************************************************************/ + void OTA::beginOTA(int port) { int server_socket = -1, client_socket = -1; @@ -22,7 +45,6 @@ void OTA::beginOTA(int port) update_ota_target_hdr OtaTargetHdr; uint32_t ota_target_index = OTA_INDEX_2; uint32_t file_info[3]; - u8 i = 0; printf("[%s] Alloc buffer Init\n", __FUNCTION__); alloc = (unsigned char *)ota_update_malloc(alloc_buf_size); @@ -43,14 +65,17 @@ void OTA::beginOTA(int port) if (ota_get_cur_index() == OTA_INDEX_1) { ota_target_index = OTA_INDEX_2; + printf("Current index = 1, Target index = 2\n"); } else { ota_target_index = OTA_INDEX_1; + printf("Current index = 2, Target index = 1\n"); } /*--------------------step2: receive firmware information-----------------*/ /* Receive file_info[] from server. */ memset(file_info, 0, sizeof(file_info)); - if (!recv_file_info_from_server((u8 *)file_info, sizeof(file_info), client_socket)) { + if (!recv_file_info_from_server((u8 *)file_info, sizeof(file_info), + client_socket)) { printf("[%s] [ERROR] Receive firmware file_info failed\n", __FUNCTION__); goto update_ota_exit; } @@ -63,35 +88,39 @@ void OTA::beginOTA(int port) } printf("[%s] Receive firmware header done\n", __FUNCTION__); - /* -----step3: parse firmware header and get target OTA image header-----*/ - if (!get_ota_tartget_header(alloc, RevHdrLen, &OtaTargetHdr, ota_target_index)) { + if (!get_ota_tartget_header(alloc, RevHdrLen, &OtaTargetHdr, + ota_target_index)) { printf("[%s] [ERROR] Get OTA header failed\n", __FUNCTION__); goto update_ota_exit; } printf("[%s] Get OTA header done\n", __FUNCTION__); - /*-----------------step4: erase flash space for new firmware-------------*/ printf("\tErase is ongoing...\n"); // for(i = 0; i < OtaTargetHdr.ValidImgCnt; i++) { - // erase_ota_target_flash(OtaTargetHdr.FileImgHdr[i].FlashAddr, OtaTargetHdr.FileImgHdr[i].ImgLen); + // erase_ota_target_flash(OtaTargetHdr.FileImgHdr[i].FlashAddr, + // OtaTargetHdr.FileImgHdr[i].ImgLen); // } // printf("[%s] Erase flash for new firmware done\n", __FUNCTION__); if (ota_target_index == 1) { + printf("\n\rErasing OTA target flash index 2..."); erase_ota_target_flash(0x08106000, OtaTargetHdr.FileImgHdr[0].ImgLen); } else { + printf("\n\rErasing OTA target flash index 1..."); erase_ota_target_flash(0x08006000, OtaTargetHdr.FileImgHdr[0].ImgLen); } - /*---------step5: download new firmware from server and write it to flash--------*/ - if (download_new_fw_from_server(client_socket, &OtaTargetHdr, ota_target_index) == _FALSE) { + /*---------step5: download new firmware from server and write it to + * flash--------*/ + if (download_new_fw_from_server(client_socket, &OtaTargetHdr, + ota_target_index) + == _FALSE) { printf("[%s] [ERROR] Download new firmware failed\n", __FUNCTION__); goto update_ota_exit; } - printf("[%s] Download new firmware done\n", __FUNCTION__); - + printf("\n\r[%s] Download new firmware done\n", __FUNCTION__); /*-------------step6: verify checksum and update signature-----------------*/ if (verify_ota_checksum(&OtaTargetHdr)) { @@ -103,7 +132,6 @@ void OTA::beginOTA(int port) ret = 0; } - /*-------------step7: OTA upgrade successfully, restart device------------*/ if (!ret) { printf("[%s] Ready to reboot\n", __FUNCTION__); @@ -129,5 +157,155 @@ void OTA::syncOTAAddr(u32 ota_target_index) } else { ota_target_index = OTA_INDEX_1; } - printf("[%s] OTA_INDEX_%d will be upgraded\n", __FUNCTION__, (int)ota_target_index); + printf("[%s] OTA_INDEX_%d will be upgraded\n", __FUNCTION__, + (int)ota_target_index); +} + +/*********************************************************************************** + * + * + * HTTP OTA Update + * + * + ***********************************************************************************/ + +/** + * @brief Start OTA Threads + * + * @param port: port number + * @param server: server ip address + * @return None + * + */ +void OTA::start_OTA_threads(int port, char *server) +{ + _port = port; // user defined port number + _server = server; // user defined server ip addr + + // thread 1 - to keep alive connectivity check (post request every 3.5 + // seconds) + if (xTaskCreate(thread1_task, (const char *)"thread1_task", 1024, NULL, + tskIDLE_PRIORITY + 1, NULL) + != pdPASS) { + printf("\n\r[%s] Create task 1 failed", __FUNCTION__); + } + + // thread 2 - to receive signal to start OTA process + if (xTaskCreate(thread2_task, (const char *)"thread2_task", 2048, NULL, + tskIDLE_PRIORITY + 1, NULL) + != pdPASS) { + printf("\n\r[%s] Create task 2 failed", __FUNCTION__); + } +} + +/** + * @brief OTA Thread 1 - to keep alive connectivity check + * + * @param param: void parameter + * + * @return None + * + */ +void OTA::thread1_task(void *param) +{ + (void)param; + while (1) { + WiFiClient wifiClient; + doc["OTA_state"] = g_otaState; + serializeJson(doc, jsonString); + if (wifiClient.connect(_server, _port)) { + // Send POST request + wifiClient.println("POST /api/connectedclients HTTP/1.1"); + wifiClient.println("Host: " + String(_server)); + wifiClient.println( + "Content-Type: application/json"); // Use appropriate content type + wifiClient.println( + "Content-Length: " + String(jsonString.length())); // Specify the length of the content + wifiClient.println("Connection: keep-alive"); + wifiClient.println(); // Empty line indicates the end of headers + wifiClient.print(jsonString); + } else { + Serial.println("Connection to server failed"); + } + delay(500); + wifiClient.stop(); + vTaskDelay(3000 / portTICK_PERIOD_MS); // Sleep for 3 second + } +} + +/** + * @brief OTA Thread 2 - to receive signal to start OTA process + * + * @param param: void parameter + * + * @return None + * + */ +void OTA::thread2_task(void *param) +{ + (void)param; + WiFiServer server(5000); + server.begin(); + while (1) { + WiFiClient client = server.available(); + while (client.connected()) { + memset(buffer, 0, 1024); + int n = client.read((uint8_t *)(&buffer[0]), sizeof(buffer)); + if (n > 0) { + for (int i = 0; i < n; i++) { + // Serial.print(buffer[i]); + } + n = client.write(buffer, n); + if (n <= 0) { + break; + } + if (strstr(buffer, "start_ota")) { + Serial.println("\n[OTA] Received start OTA signal from UI."); + if (g_otaState == OtaState[0]) { + ota_http(); + } + } + } + vTaskDelay(10000 / portTICK_PERIOD_MS); // Sleep for 10 second + } + } +} + +void OTA::http_update_ota_task(void *param) +{ + (void)param; + + int ret = -1; + + g_otaState = OtaState[2]; + + ret = http_update_ota(_server, _port, _resource); + + g_otaState = OtaState[3]; + + // printf("\n\r[%s] Update task exit", __FUNCTION__); + if (!ret) { + // printf("\n\r[%s] Ready to reboot", __FUNCTION__); + g_otaState = OtaState[4]; + ota_platform_reset(); + } + vTaskDelete(NULL); +} + +/** + * @brief OTA HTTP - to create a thread task using http_update_ota_task + * [FreeRTOS SDK HTTP OTA Example] + * + * @param //None + * + * @return None + * + */ +void OTA::ota_http(void) +{ + if (xTaskCreate(http_update_ota_task, (const char *)"http_update_ota_task", + 1024, NULL, tskIDLE_PRIORITY + 1, NULL) + != pdPASS) { + printf("\n\r[%s] Create update task failed", __FUNCTION__); + } } diff --git a/Arduino_package/hardware/libraries/OTA/src/OTA.h b/Arduino_package/hardware/libraries/OTA/src/OTA.h index e845ea97..614f0683 100644 --- a/Arduino_package/hardware/libraries/OTA/src/OTA.h +++ b/Arduino_package/hardware/libraries/OTA/src/OTA.h @@ -2,23 +2,46 @@ #define __OTA_H__ #include "Arduino.h" +#include #include +#include +#include +#include +#include +#include +#include -#define KM0_OTA1_ADDR 0x00006000 -#define OTA2_ADDR 0X00106000 - -class OTA -{ +class OTA { public: OTA(void); ~OTA(void); // Starts to connect to OTA server and receive the new firmware + // To begin OTA firmware update process via MDNS void beginOTA(int port); + // To start OTA firmware update process via HTTP + void start_OTA_threads(int port, char *server); + private: // Check which OTA index to be updated void syncOTAAddr(u32 ota_target_index); -}; + static void thread1_task(void *param); + static void thread2_task(void *param); + static void http_update_ota_task(void *param); + static void ota_http(void); + + static uint16_t _port; + static char *_server; + + static char *g_otaState; + static char *_resource; + + static char *OtaState[5]; + + static JsonDocument doc; + static String jsonString; + static char buffer[1024]; +}; #endif diff --git a/Arduino_package/hardware/libraries/WiFi/src/WiFiClient.cpp b/Arduino_package/hardware/libraries/WiFi/src/WiFiClient.cpp index f2a47c45..496b3ea8 100644 --- a/Arduino_package/hardware/libraries/WiFi/src/WiFiClient.cpp +++ b/Arduino_package/hardware/libraries/WiFi/src/WiFiClient.cpp @@ -1,36 +1,78 @@ #include "WiFi.h" extern "C" { - #include "wl_definitions.h" - #include "wl_types.h" - #include "string.h" - #include "errno.h" - #include "update.h" +#include "wl_definitions.h" +#include "wl_types.h" +#include "string.h" +#include "errno.h" +#include "update.h" } #include "WiFiClient.h" #include "WiFiServer.h" #include "server_drv.h" -WiFiClient::WiFiClient() : _sock(MAX_SOCK_NUM) { +WiFiClient::WiFiClient(): + _sock(MAX_SOCK_NUM) +{ _is_connected = false; recvTimeout = 3000; } -WiFiClient::WiFiClient(uint8_t sock) { +WiFiClient::WiFiClient(uint8_t sock) +{ _sock = sock; - //if ((sock >= 0) && (sock != 0xFF)) { + // if ((sock >= 0) && (sock != 0xFF)) { if (sock != 0xFF) { _is_connected = true; } recvTimeout = 3000; } -WiFiClient::~WiFiClient() { +WiFiClient::WiFiClient(tPortMode portMode): + _sock(MAX_SOCK_NUM) +{ + _is_connected = false; + recvTimeout = 3000; + _portMode = portMode; +} + +WiFiClient::WiFiClient(tBlockingMode blockMode): + _sock(MAX_SOCK_NUM) +{ + _is_connected = false; + recvTimeout = 3000; + _is_blocked = blockMode; +} + +WiFiClient::WiFiClient(uint8_t sock, tPortMode portMode) +{ + _sock = sock; + if ((sock >= 0) && (sock != 0xFF)) { + _is_connected = true; + } + recvTimeout = 3000; + _portMode = portMode; +} + +WiFiClient::WiFiClient(uint8_t sock, tPortMode portMode, tBlockingMode blockMode) +{ + _sock = sock; + if ((sock >= 0) && (sock != 0xFF)) { + _is_connected = true; + } + recvTimeout = 3000; + _portMode = portMode; + _is_blocked = blockMode; +} + +WiFiClient::~WiFiClient() +{ stop(); } -uint8_t WiFiClient::connected() { +uint8_t WiFiClient::connected() +{ if ((_sock < 0) || (_sock == 0xFF)) { _is_connected = false; return 0; @@ -44,12 +86,13 @@ uint8_t WiFiClient::connected() { } } -int WiFiClient::available() { +int WiFiClient::available() +{ if (!_is_connected || _sock < 0) { return 0; } - - while(true){ + + while (true) { if (clientdrv.availData(_sock) > 0) { return 1; } else { @@ -65,7 +108,8 @@ int WiFiClient::available() { } } -int WiFiClient::read() { +int WiFiClient::read() +{ int ret; int err; uint8_t b[1]; @@ -86,7 +130,8 @@ int WiFiClient::read() { return ret; } -int WiFiClient::read(uint8_t* buf, size_t size) { +int WiFiClient::read(uint8_t *buf, size_t size) +{ uint16_t _size = size; int ret; int err; @@ -101,7 +146,8 @@ int WiFiClient::read(uint8_t* buf, size_t size) { return ret; } -int WiFiClient::recv(uint8_t* buf, size_t size) { +int WiFiClient::recv(uint8_t *buf, size_t size) +{ uint16_t _size = size; int ret; int err; @@ -116,7 +162,8 @@ int WiFiClient::recv(uint8_t* buf, size_t size) { return ret; } -void WiFiClient::stop() { +void WiFiClient::stop() +{ if (_sock < 0) { return; } @@ -125,11 +172,24 @@ void WiFiClient::stop() { _sock = -1; } -size_t WiFiClient::write(uint8_t b) { +size_t WiFiClient::write(uint8_t b) +{ return write(&b, 1); } -size_t WiFiClient::write(const uint8_t *buf, size_t size) { +// set WiFi client to blocking/non-blocking mode +void WiFiClient::setBlockingMode() +{ + _is_blocked = BLOCKING_MODE; +} + +void WiFiClient::setNonBlockingMode() +{ + _is_blocked = NON_BLOCKING_MODE; +} + +size_t WiFiClient::write(const uint8_t *buf, size_t size) +{ if (_sock < 0) { setWriteError(); return 0; @@ -147,11 +207,13 @@ size_t WiFiClient::write(const uint8_t *buf, size_t size) { return size; } -WiFiClient::operator bool() { +WiFiClient::operator bool() +{ return _sock >= 0; } -int WiFiClient::connect(const char* host, uint16_t port) { +int WiFiClient::connect(const char *host, uint16_t port) +{ IPAddress remote_addr; IPv6Address remote_addr_v6; @@ -160,9 +222,9 @@ int WiFiClient::connect(const char* host, uint16_t port) { return connect(remote_addr, port); } } else { - //printf("\n\r[INFO]WiFiClient.cpp: connect hostByNameV6() \n\r"); + // printf("\n\r[INFO]WiFiClient.cpp: connect hostByNameV6() \n\r"); if (WiFi.hostByNamev6(host, remote_addr_v6)) { - //printf("[INFO]WiFiClient.cpp: connect ipv6: %s\n\r", host); + // printf("[INFO]WiFiClient.cpp: connect ipv6: %s\n\r", host); _sock = clientdrv.startClientV6(host, port, TCP_MODE); } else { } @@ -179,9 +241,10 @@ int WiFiClient::connect(const char* host, uint16_t port) { return 0; } -int WiFiClient::connect(IPAddress ip, uint16_t port) { +int WiFiClient::connect(IPAddress ip, uint16_t port) +{ _is_connected = false; - _sock = clientdrv.startClient(ip, port); + _sock = clientdrv.startClient(ip, port, _portMode, _is_blocked); // whether sock is connected if (_sock < 0) { _is_connected = false; @@ -193,23 +256,25 @@ int WiFiClient::connect(IPAddress ip, uint16_t port) { return 1; } -int WiFiClient::connectv6(IPv6Address ipv6, uint16_t port) { +int WiFiClient::connectv6(IPv6Address ipv6, uint16_t port) +{ _is_connected = false; _sock = clientdrv.startClientv6(ipv6, port); - //printf("[INFO]wifiClient.cpp: connectv6 sock value: %x\n\r", _sock); + // printf("[INFO]wifiClient.cpp: connectv6 sock value: %x\n\r", _sock); if (_sock < 0) { _is_connected = false; - //printf("[INFO]wifiClient.cpp: connectv6 not connected\n\r"); + // printf("[INFO]wifiClient.cpp: connectv6 not connected\n\r"); return 0; } else { _is_connected = true; - //printf("[INFO]wifiClient.cpp: connectv6 connected\n\r"); + // printf("[INFO]wifiClient.cpp: connectv6 connected\n\r"); clientdrv.setSockRecvTimeout(_sock, recvTimeout); } return 1; } -int WiFiClient::peek() { +int WiFiClient::peek() +{ uint8_t b; if (!available()) { return -1; @@ -219,7 +284,8 @@ int WiFiClient::peek() { return b; } -void WiFiClient::flush() { +void WiFiClient::flush() +{ while (available()) { read(); } @@ -227,7 +293,8 @@ void WiFiClient::flush() { // extend API from RTK -int WiFiClient::setRecvTimeout(int timeout) { +int WiFiClient::setRecvTimeout(int timeout) +{ if (connected()) { recvTimeout = timeout; clientdrv.setSockRecvTimeout(_sock, recvTimeout); @@ -235,16 +302,19 @@ int WiFiClient::setRecvTimeout(int timeout) { return 0; } -int WiFiClient::read(char *buf, size_t size) { +int WiFiClient::read(char *buf, size_t size) +{ read(((uint8_t *)buf), size); return 0; } -int WiFiClient::enableIPv6() { +int WiFiClient::enableIPv6() +{ return clientdrv.enableIPv6(); } -int WiFiClient::getIPv6Status() { +int WiFiClient::getIPv6Status() +{ return clientdrv.getIPv6Status(); } diff --git a/Arduino_package/hardware/libraries/WiFi/src/WiFiClient.h b/Arduino_package/hardware/libraries/WiFi/src/WiFiClient.h index 1f82494f..6e653d3a 100644 --- a/Arduino_package/hardware/libraries/WiFi/src/WiFiClient.h +++ b/Arduino_package/hardware/libraries/WiFi/src/WiFiClient.h @@ -1,49 +1,58 @@ #ifndef WiFiClient_h #define WiFiClient_h -#include "Print.h" #include "Client.h" #include "IPAddress.h" #include "IPv6Address.h" +#include "Print.h" #include "server_drv.h" -class WiFiClient : public Client { - public: - WiFiClient(); - WiFiClient(uint8_t sock); - ~WiFiClient(); - - uint8_t status(); - virtual uint8_t connected(); - virtual int available(); - virtual int read(); - virtual int read(uint8_t *buf, size_t size); - virtual int recv(uint8_t *buf, size_t size); - virtual void stop(); - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *buf, size_t size); - virtual operator bool(); - virtual int connect(const char *host, uint16_t port); - virtual int connect(IPAddress ip, uint16_t port); - virtual int connectv6(IPv6Address ipv6, uint16_t port); - virtual int peek(); - virtual void flush(); - // extend API from RTK - int setRecvTimeout(int timeout); - int read(char *buf, size_t size); - // IPv6 related - int enableIPv6(); - int getIPv6Status(); - - friend class WiFiServer; - using Print::write; - - private: - uint8_t _sock; - ServerDrv clientdrv; - bool _is_connected; - uint8_t data[DATA_LENTH]; - int recvTimeout; + +class WiFiClient: public Client { +public: + WiFiClient(); + WiFiClient(uint8_t sock); + WiFiClient(tPortMode portMode); + WiFiClient(tBlockingMode blockMode); + WiFiClient(uint8_t sock, tPortMode portMode); + WiFiClient(uint8_t sock, tPortMode portMode, tBlockingMode blockMode); + ~WiFiClient(); + + uint8_t status(); + virtual uint8_t connected(); + virtual int available(); + virtual int read(); + virtual int read(uint8_t *buf, size_t size); + virtual int recv(uint8_t *buf, size_t size); + virtual void stop(); + virtual void setBlockingMode(); + virtual void setNonBlockingMode(); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + virtual operator bool(); + virtual int connect(const char *host, uint16_t port); + virtual int connect(IPAddress ip, uint16_t port); + virtual int connectv6(IPv6Address ipv6, uint16_t port); + virtual int peek(); + virtual void flush(); + // extend API from RTK + int setRecvTimeout(int timeout); + int read(char *buf, size_t size); + // IPv6 related + int enableIPv6(); + int getIPv6Status(); + + friend class WiFiServer; + using Print::write; + +private: + uint8_t _sock; + ServerDrv clientdrv; + bool _is_connected; + uint8_t data[DATA_LENTH]; + int recvTimeout; + tPortMode _portMode = TCP_MODE; + tBlockingMode _is_blocked = NON_BLOCKING_MODE; }; #ifdef __cplusplus diff --git a/Arduino_package/hardware/libraries/WiFi/src/WiFiServer.cpp b/Arduino_package/hardware/libraries/WiFi/src/WiFiServer.cpp index 63cee801..48dd72ea 100644 --- a/Arduino_package/hardware/libraries/WiFi/src/WiFiServer.cpp +++ b/Arduino_package/hardware/libraries/WiFi/src/WiFiServer.cpp @@ -17,26 +17,51 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include "server_drv.h" +#include "WiFiServer.h" #include "WiFi.h" #include "WiFiClient.h" -#include "WiFiServer.h" +#include "server_drv.h" +#include -WiFiServer::WiFiServer(uint16_t port) { + +WiFiServer::WiFiServer(uint16_t port) +{ _port = port; } -WiFiServer::WiFiServer(uint16_t port, tProtMode portMode) { +WiFiServer::WiFiServer(tPortMode portMode): + _port(1883) +{ + _portMode = portMode; +} + +WiFiServer::WiFiServer(tBlockingMode blockMode): + _port(1883) +{ + _is_blocked = blockMode; +} + +WiFiServer::WiFiServer(uint16_t port, tPortMode portMode) +{ + _port = port; + _portMode = portMode; +} + +WiFiServer::WiFiServer(uint16_t port, tPortMode portMode, + tBlockingMode blockMode) +{ _port = port; _portMode = portMode; + _is_blocked = blockMode; } -WiFiServer::~WiFiServer() { +WiFiServer::~WiFiServer() +{ close(); } -void WiFiServer::begin() { +void WiFiServer::begin() +{ _is_connected = false; _sock_ser = serverdrv.startServer(_port, _portMode, _is_blocked); if (_sock_ser < 0) { @@ -48,7 +73,8 @@ void WiFiServer::begin() { } } -WiFiClient WiFiServer::available(uint8_t* status) { +WiFiClient WiFiServer::available(uint8_t *status) +{ int client_fd = (int)(status); client_fd = serverdrv.getAvailable(_sock_ser); @@ -56,7 +82,8 @@ WiFiClient WiFiServer::available(uint8_t* status) { return WiFiClient(client_fd); } -int WiFiServer::available(int server_fd) { +int WiFiServer::available(int server_fd) +{ int client_fd; client_fd = serverdrv.getAvailable(server_fd); @@ -65,7 +92,8 @@ int WiFiServer::available(int server_fd) { return client_fd; } -uint8_t WiFiServer::connected() { +uint8_t WiFiServer::connected() +{ if ((_sock_ser < 0) || (_sock_ser == 0xFF)) { _is_connected = false; return 0; @@ -79,7 +107,8 @@ uint8_t WiFiServer::connected() { } } -int WiFiServer::recv(uint8_t* buf, size_t size) { +int WiFiServer::recv(uint8_t *buf, size_t size) +{ uint16_t _size = size; int ret; int err; @@ -94,11 +123,13 @@ int WiFiServer::recv(uint8_t* buf, size_t size) { return ret; } -size_t WiFiServer::write(uint8_t b) { +size_t WiFiServer::write(uint8_t b) +{ return write(&b, 1); } -size_t WiFiServer::write(const uint8_t *buf, size_t size) { +size_t WiFiServer::write(const uint8_t *buf, size_t size) +{ if (_sock_ser < 0) { setWriteError(); return 0; @@ -115,7 +146,8 @@ size_t WiFiServer::write(const uint8_t *buf, size_t size) { return size; } -void WiFiServer::stop() { +void WiFiServer::stop() +{ if (_sock_ser < 0) { return; } @@ -124,22 +156,36 @@ void WiFiServer::stop() { _sock_ser = -1; } -void WiFiServer::end() { - stop(); +// set WiFi server to blocking/non-blocking mode +void WiFiServer::setBlockingMode() +{ + _is_blocked = BLOCKING_MODE; +} + +void WiFiServer::setNonBlockingMode() +{ + _is_blocked = NON_BLOCKING_MODE; } -void WiFiServer::close() { +void WiFiServer::end() +{ stop(); } -// set WiFi server to blocking mode -void WiFiServer::setBlocking() { - // printf("WiFi server is set to blocking mode\r\n"); - _is_blocked = !_is_blocked; +void WiFiServer::close() +{ + stop(); } +// // set WiFi server to blocking mode +// void WiFiServer::setBlocking() { +// // printf("WiFi server is set to blocking mode\r\n"); +// _is_blocked = !_is_blocked; +// } + // extend API from RTK -int WiFiServer::setTimeout(int timeout) { +int WiFiServer::setTimeout(int timeout) +{ if (connected()) { recvTimeout = timeout; serverdrv.setSockRecvTimeout(_sock_ser, recvTimeout); @@ -148,10 +194,12 @@ int WiFiServer::setTimeout(int timeout) { } // IPv6 related -int WiFiServer::enableIPv6() { +int WiFiServer::enableIPv6() +{ return serverdrv.enableIPv6(); } -int WiFiServer::getIPv6Status() { +int WiFiServer::getIPv6Status() +{ return serverdrv.getIPv6Status(); } diff --git a/Arduino_package/hardware/libraries/WiFi/src/WiFiServer.h b/Arduino_package/hardware/libraries/WiFi/src/WiFiServer.h index 54225e1c..fc4441c3 100644 --- a/Arduino_package/hardware/libraries/WiFi/src/WiFiServer.h +++ b/Arduino_package/hardware/libraries/WiFi/src/WiFiServer.h @@ -6,39 +6,44 @@ class WiFiClient; -class WiFiServer : public Server { - public: - WiFiServer(uint16_t); - WiFiServer(uint16_t, tProtMode); - ~WiFiServer(); +class WiFiServer: public Server { +public: + WiFiServer(); + WiFiServer(uint16_t); + WiFiServer(tPortMode); + WiFiServer(tBlockingMode); + WiFiServer(uint16_t, tPortMode); + WiFiServer(uint16_t, tPortMode, tBlockingMode); + ~WiFiServer(); - virtual void begin(); - WiFiClient available(uint8_t* status = NULL); - virtual int available(int server_fd); - virtual uint8_t connected(); - virtual int recv(uint8_t* buf, size_t size); - virtual size_t write(uint8_t b); - virtual size_t write(const uint8_t* buf, size_t size); - virtual void stop(); - virtual void end(); - virtual void close(); - virtual void setBlocking(); - // extend API from RTK - virtual int setTimeout(int timeout); - // IPv6 related - int enableIPv6(); - int getIPv6Status(); - using Print::write; + virtual void begin(); + WiFiClient available(uint8_t *status = NULL); + virtual int available(int server_fd); + virtual uint8_t connected(); + virtual int recv(uint8_t *buf, size_t size); + virtual size_t write(uint8_t b); + virtual size_t write(const uint8_t *buf, size_t size); + virtual void stop(); + virtual void end(); + virtual void close(); + virtual void setBlockingMode(); + virtual void setNonBlockingMode(); + // extend API from RTK + virtual int setTimeout(int timeout); + // IPv6 related + int enableIPv6(); + int getIPv6Status(); + using Print::write; - private: - ServerDrv serverdrv; - uint16_t _port; - int _sock_ser; - bool _is_connected; - uint8_t data[DATA_LENTH]; - int recvTimeout; - bool _is_blocked = false; - tProtMode _portMode = TCP_MODE; +private: + ServerDrv serverdrv; + uint16_t _port; + int _sock_ser; + bool _is_connected; + uint8_t data[DATA_LENTH]; + int recvTimeout; + tPortMode _portMode = TCP_MODE; + tBlockingMode _is_blocked = NON_BLOCKING_MODE; }; #endif diff --git a/Arduino_package/hardware/system/component/soc/realtek/amebad/fwlib/usrcfg/rtl8721dlp_flashcfg.c b/Arduino_package/hardware/system/component/soc/realtek/amebad/fwlib/usrcfg/rtl8721dlp_flashcfg.c index b1c226f7..3462af8a 100644 --- a/Arduino_package/hardware/system/component/soc/realtek/amebad/fwlib/usrcfg/rtl8721dlp_flashcfg.c +++ b/Arduino_package/hardware/system/component/soc/realtek/amebad/fwlib/usrcfg/rtl8721dlp_flashcfg.c @@ -66,8 +66,8 @@ const FlashInfo_TypeDef Flash_AVL[] = { {0x20, 0x000000FF, FlashClass5, 0x000000FC, NULL}, /* Microm: MANUFACTURER_ID_MICRON */ /* case2: new flash, ID is not included in case1 list, but specification is compatible with FlashClass1~FlashClass6 */ - {0x85, 0x000000FF, FlashClass1, 0x000043FC, NULL}, /* Puya */ - + {0x85, 0x000000FF, FlashClass1, 0x000043FC, NULL}, /* Puya */ + /* case3: new flash, ID is not included in case1 list, and specification is not compatible with FlashClass1~FlashClass6 */ {0x00, 0x000000FF, FlashClassUser, 0xFFFFFFFF, &flash_init_userdef}, diff --git a/Arduino_package/hardware/system/project/realtek_amebaD_va0_example/inc/inc_hp/build_info.h b/Arduino_package/hardware/system/project/realtek_amebaD_va0_example/inc/inc_hp/build_info.h index 549bdb51..78e1324e 100644 --- a/Arduino_package/hardware/system/project/realtek_amebaD_va0_example/inc/inc_hp/build_info.h +++ b/Arduino_package/hardware/system/project/realtek_amebaD_va0_example/inc/inc_hp/build_info.h @@ -1,7 +1,7 @@ -#define UTS_VERSION "2022/11/29-16:07:55" -#define RTL_FW_COMPILE_TIME "2022/11/29-16:07:55" -#define RTL_FW_COMPILE_DATE "2022/11/29" -#define RTL_FW_COMPILE_BY "zhangzhenwu" -#define RTL_FW_COMPILE_HOST "SGSD279634-PC" +#define UTS_VERSION "2024/10/02-14:45:02" +#define RTL_FW_COMPILE_TIME "2024/10/02-14:45:02" +#define RTL_FW_COMPILE_DATE "2024/10/02" +#define RTL_FW_COMPILE_BY "kevinloo" +#define RTL_FW_COMPILE_HOST "" #define RTL_FW_COMPILE_DOMAIN "realtek-sg.com" -#define RTL_FW_COMPILER "gcc version 6.4.1 20180307 (Realtek ASDK-6.4.1 Build 2778) " +#define RTL_FW_COMPILER "gcc version 6.4.1 20180425 (Realtek ASDK-6.4.1 Build 3026) " diff --git a/Arduino_package/hardware/variants/common_libs/lib_arduino.a b/Arduino_package/hardware/variants/common_libs/lib_arduino.a index 404142db..248bd8ef 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_arduino.a and b/Arduino_package/hardware/variants/common_libs/lib_arduino.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_arduino_mbedtls240.a b/Arduino_package/hardware/variants/common_libs/lib_arduino_mbedtls240.a index 7661a061..178d6337 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_arduino_mbedtls240.a and b/Arduino_package/hardware/variants/common_libs/lib_arduino_mbedtls240.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_cmsis_dsp.a b/Arduino_package/hardware/variants/common_libs/lib_cmsis_dsp.a index 2f617195..bf69ae90 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_cmsis_dsp.a and b/Arduino_package/hardware/variants/common_libs/lib_cmsis_dsp.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_dct.a b/Arduino_package/hardware/variants/common_libs/lib_dct.a index 574571ed..949f9bb4 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_dct.a and b/Arduino_package/hardware/variants/common_libs/lib_dct.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_eap.a b/Arduino_package/hardware/variants/common_libs/lib_eap.a index 429b9e0a..f275ab04 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_eap.a and b/Arduino_package/hardware/variants/common_libs/lib_eap.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_httpc.a b/Arduino_package/hardware/variants/common_libs/lib_httpc.a index eaf8b9b1..1c751520 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_httpc.a and b/Arduino_package/hardware/variants/common_libs/lib_httpc.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_httpd.a b/Arduino_package/hardware/variants/common_libs/lib_httpd.a index a99456b2..4469b70c 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_httpd.a and b/Arduino_package/hardware/variants/common_libs/lib_httpd.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_pmc_hp.a b/Arduino_package/hardware/variants/common_libs/lib_pmc_hp.a index 744594b3..7820a0dc 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_pmc_hp.a and b/Arduino_package/hardware/variants/common_libs/lib_pmc_hp.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_pmc_lp.a b/Arduino_package/hardware/variants/common_libs/lib_pmc_lp.a index 267e792f..4327245a 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_pmc_lp.a and b/Arduino_package/hardware/variants/common_libs/lib_pmc_lp.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_tftp.a b/Arduino_package/hardware/variants/common_libs/lib_tftp.a index 94cef535..d2db8c9a 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_tftp.a and b/Arduino_package/hardware/variants/common_libs/lib_tftp.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_usbd.a b/Arduino_package/hardware/variants/common_libs/lib_usbd.a index d43c8a79..a3945d08 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_usbd.a and b/Arduino_package/hardware/variants/common_libs/lib_usbd.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_usbd_new.a b/Arduino_package/hardware/variants/common_libs/lib_usbd_new.a index c7ccbee5..27a36c7c 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_usbd_new.a and b/Arduino_package/hardware/variants/common_libs/lib_usbd_new.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_usbh.a b/Arduino_package/hardware/variants/common_libs/lib_usbh.a index 1d1f6a77..750518f2 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_usbh.a and b/Arduino_package/hardware/variants/common_libs/lib_usbh.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_user.a b/Arduino_package/hardware/variants/common_libs/lib_user.a index fc30aab6..305e52b7 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_user.a and b/Arduino_package/hardware/variants/common_libs/lib_user.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_websocket.a b/Arduino_package/hardware/variants/common_libs/lib_websocket.a index 96bdeee3..6f1f3e20 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_websocket.a and b/Arduino_package/hardware/variants/common_libs/lib_websocket.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_wlan.a b/Arduino_package/hardware/variants/common_libs/lib_wlan.a index d565c7aa..a0e34820 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_wlan.a and b/Arduino_package/hardware/variants/common_libs/lib_wlan.a differ diff --git a/Arduino_package/hardware/variants/common_libs/lib_wps.a b/Arduino_package/hardware/variants/common_libs/lib_wps.a index 73e45371..757cb026 100644 Binary files a/Arduino_package/hardware/variants/common_libs/lib_wps.a and b/Arduino_package/hardware/variants/common_libs/lib_wps.a differ diff --git a/Arduino_package/package_realtek_amebad_early_index.json b/Arduino_package/package_realtek_amebad_early_index.json index 1caee7ac..d1cf6d01 100644 --- a/Arduino_package/package_realtek_amebad_early_index.json +++ b/Arduino_package/package_realtek_amebad_early_index.json @@ -13,6 +13,44 @@ { "name": "Realtek Ameba Boards (32-bits ARM Cortex-M33 @200MHz)", "architecture": "AmebaD", + "version": "3.1.8-build20241002", + "category": "Contributed", + "help": { + "online": "https://www.amebaiot.com/" + }, + "url": "https://github.com/ambiot/ambd_arduino/raw/dev/Arduino_package/release/ameba_d-3.1.8-build20241002.tar.gz", + "archiveFileName": "ameba_d-3.1.8-build20241002.tar.gz", + "checksum": "SHA-256:95b4ccfa30b9ce2485c25561919153f5af88955f8bf4b674e834d3551084e409", + "size": "15743073", + "boards": [ + {"name": "AMB21 (RTL8722DM)"}, + {"name": "AMB22 (RTL8722CSM)"}, + {"name": "AMB23 (RTL8722DM)"}, + {"name": "BW16 (RTL8720DN)"}, + {"name": "BW16-TypeC (RTL8720DN)"}, + {"name": "AW-CU488 SparkFun Thing Plus (RTL8721DM)"}, + {"name": "AMB25 (RTL8720DF)"}, + {"name": "AMB26 (RTL8720DF)"}, + {"name": "NORA-W30 (RTL8720DF)"}, + {"name": "NORA-W306 SparkFun Thing Plus (RTL8720DF)"}, + {"name": "Datalogger Kevin’s Lab (RTL8720DF)"} + ], + "toolsDependencies": [ + { + "packager": "realtek", + "name": "ameba_d_asdk_toolchain", + "version": "1.0.1" + }, + { + "packager": "realtek", + "name": "ameba_d_tools", + "version": "1.1.3" + } + ] + }, + { + "name": "Realtek Ameba Boards (32-bits ARM Cortex-M33 @200MHz)", + "architecture": "AmebaD", "version": "3.1.8-build20240719", "category": "Contributed", "help": { diff --git a/Arduino_package/release/ameba_d-3.1.8-build20241002.tar.gz b/Arduino_package/release/ameba_d-3.1.8-build20241002.tar.gz new file mode 100644 index 00000000..a5e8fd49 Binary files /dev/null and b/Arduino_package/release/ameba_d-3.1.8-build20241002.tar.gz differ