|
|
|
@ -11,67 +11,50 @@ |
|
|
|
|
#include <ArduinoJson/Strings/StringAdapters.hpp> |
|
|
|
|
#include <ArduinoJson/Variant/VariantContent.hpp> |
|
|
|
|
|
|
|
|
|
// VariantData can't have a constructor (to be a POD), so we have no way to fix
|
|
|
|
|
// this warning
|
|
|
|
|
#if defined(__GNUC__) |
|
|
|
|
# if __GNUC__ >= 7 |
|
|
|
|
# pragma GCC diagnostic push |
|
|
|
|
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" |
|
|
|
|
# pragma GCC diagnostic ignored "-Wuninitialized" |
|
|
|
|
# endif |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE |
|
|
|
|
|
|
|
|
|
class VariantData { |
|
|
|
|
VariantContent _content; // must be first to allow cast from array to variant
|
|
|
|
|
uint8_t _flags; |
|
|
|
|
VariantContent content_; // must be first to allow cast from array to variant
|
|
|
|
|
uint8_t flags_; |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
// Must be a POD!
|
|
|
|
|
// - no constructor
|
|
|
|
|
// - no destructor
|
|
|
|
|
// - no virtual
|
|
|
|
|
// - no inheritance
|
|
|
|
|
void init() { |
|
|
|
|
_flags = VALUE_IS_NULL; |
|
|
|
|
} |
|
|
|
|
VariantData() : flags_(VALUE_IS_NULL) {} |
|
|
|
|
|
|
|
|
|
void operator=(const VariantData& src) { |
|
|
|
|
_content = src._content; |
|
|
|
|
_flags = uint8_t((_flags & OWNED_KEY_BIT) | (src._flags & ~OWNED_KEY_BIT)); |
|
|
|
|
content_ = src.content_; |
|
|
|
|
flags_ = uint8_t((flags_ & OWNED_KEY_BIT) | (src.flags_ & ~OWNED_KEY_BIT)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <typename TVisitor> |
|
|
|
|
typename TVisitor::result_type accept(TVisitor& visitor) const { |
|
|
|
|
switch (type()) { |
|
|
|
|
case VALUE_IS_FLOAT: |
|
|
|
|
return visitor.visitFloat(_content.asFloat); |
|
|
|
|
return visitor.visitFloat(content_.asFloat); |
|
|
|
|
|
|
|
|
|
case VALUE_IS_ARRAY: |
|
|
|
|
return visitor.visitArray(_content.asCollection); |
|
|
|
|
return visitor.visitArray(content_.asCollection); |
|
|
|
|
|
|
|
|
|
case VALUE_IS_OBJECT: |
|
|
|
|
return visitor.visitObject(_content.asCollection); |
|
|
|
|
return visitor.visitObject(content_.asCollection); |
|
|
|
|
|
|
|
|
|
case VALUE_IS_LINKED_STRING: |
|
|
|
|
case VALUE_IS_OWNED_STRING: |
|
|
|
|
return visitor.visitString(_content.asString.data, |
|
|
|
|
_content.asString.size); |
|
|
|
|
return visitor.visitString(content_.asString.data, |
|
|
|
|
content_.asString.size); |
|
|
|
|
|
|
|
|
|
case VALUE_IS_OWNED_RAW: |
|
|
|
|
case VALUE_IS_LINKED_RAW: |
|
|
|
|
return visitor.visitRawJson(_content.asString.data, |
|
|
|
|
_content.asString.size); |
|
|
|
|
return visitor.visitRawJson(content_.asString.data, |
|
|
|
|
content_.asString.size); |
|
|
|
|
|
|
|
|
|
case VALUE_IS_SIGNED_INTEGER: |
|
|
|
|
return visitor.visitSignedInteger(_content.asSignedInteger); |
|
|
|
|
return visitor.visitSignedInteger(content_.asSignedInteger); |
|
|
|
|
|
|
|
|
|
case VALUE_IS_UNSIGNED_INTEGER: |
|
|
|
|
return visitor.visitUnsignedInteger(_content.asUnsignedInteger); |
|
|
|
|
return visitor.visitUnsignedInteger(content_.asUnsignedInteger); |
|
|
|
|
|
|
|
|
|
case VALUE_IS_BOOLEAN: |
|
|
|
|
return visitor.visitBoolean(_content.asBoolean != 0); |
|
|
|
|
return visitor.visitBoolean(content_.asBoolean != 0); |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
return visitor.visitNull(); |
|
|
|
@ -89,7 +72,7 @@ class VariantData { |
|
|
|
|
bool asBoolean() const; |
|
|
|
|
|
|
|
|
|
CollectionData* asArray() { |
|
|
|
|
return isArray() ? &_content.asCollection : 0; |
|
|
|
|
return isArray() ? &content_.asCollection : 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const CollectionData* asArray() const { |
|
|
|
@ -97,11 +80,11 @@ class VariantData { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const CollectionData* asCollection() const { |
|
|
|
|
return isCollection() ? &_content.asCollection : 0; |
|
|
|
|
return isCollection() ? &content_.asCollection : 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
CollectionData* asObject() { |
|
|
|
|
return isObject() ? &_content.asCollection : 0; |
|
|
|
|
return isObject() ? &content_.asCollection : 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const CollectionData* asObject() const { |
|
|
|
@ -111,7 +94,7 @@ class VariantData { |
|
|
|
|
bool copyFrom(const VariantData& src, MemoryPool* pool); |
|
|
|
|
|
|
|
|
|
bool isArray() const { |
|
|
|
|
return (_flags & VALUE_IS_ARRAY) != 0; |
|
|
|
|
return (flags_ & VALUE_IS_ARRAY) != 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool isBoolean() const { |
|
|
|
@ -119,17 +102,17 @@ class VariantData { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool isCollection() const { |
|
|
|
|
return (_flags & COLLECTION_MASK) != 0; |
|
|
|
|
return (flags_ & COLLECTION_MASK) != 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
bool isInteger() const { |
|
|
|
|
switch (type()) { |
|
|
|
|
case VALUE_IS_UNSIGNED_INTEGER: |
|
|
|
|
return canConvertNumber<T>(_content.asUnsignedInteger); |
|
|
|
|
return canConvertNumber<T>(content_.asUnsignedInteger); |
|
|
|
|
|
|
|
|
|
case VALUE_IS_SIGNED_INTEGER: |
|
|
|
|
return canConvertNumber<T>(_content.asSignedInteger); |
|
|
|
|
return canConvertNumber<T>(content_.asSignedInteger); |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
return false; |
|
|
|
@ -137,7 +120,7 @@ class VariantData { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool isFloat() const { |
|
|
|
|
return (_flags & NUMBER_BIT) != 0; |
|
|
|
|
return (flags_ & NUMBER_BIT) != 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool isString() const { |
|
|
|
@ -145,7 +128,7 @@ class VariantData { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool isObject() const { |
|
|
|
|
return (_flags & VALUE_IS_OBJECT) != 0; |
|
|
|
|
return (flags_ & VALUE_IS_OBJECT) != 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool isNull() const { |
|
|
|
@ -158,30 +141,30 @@ class VariantData { |
|
|
|
|
|
|
|
|
|
void remove(size_t index) { |
|
|
|
|
if (isArray()) |
|
|
|
|
_content.asCollection.removeElement(index); |
|
|
|
|
content_.asCollection.removeElement(index); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <typename TAdaptedString> |
|
|
|
|
void remove(TAdaptedString key) { |
|
|
|
|
if (isObject()) |
|
|
|
|
_content.asCollection.removeMember(key); |
|
|
|
|
content_.asCollection.removeMember(key); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void setBoolean(bool value) { |
|
|
|
|
setType(VALUE_IS_BOOLEAN); |
|
|
|
|
_content.asBoolean = value; |
|
|
|
|
content_.asBoolean = value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void setFloat(JsonFloat value) { |
|
|
|
|
setType(VALUE_IS_FLOAT); |
|
|
|
|
_content.asFloat = value; |
|
|
|
|
content_.asFloat = value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void setLinkedRaw(SerializedValue<const char*> value) { |
|
|
|
|
if (value.data()) { |
|
|
|
|
setType(VALUE_IS_LINKED_RAW); |
|
|
|
|
_content.asString.data = value.data(); |
|
|
|
|
_content.asString.size = value.size(); |
|
|
|
|
content_.asString.data = value.data(); |
|
|
|
|
content_.asString.size = value.size(); |
|
|
|
|
} else { |
|
|
|
|
setType(VALUE_IS_NULL); |
|
|
|
|
} |
|
|
|
@ -192,8 +175,8 @@ class VariantData { |
|
|
|
|
const char* dup = pool->saveString(adaptString(value.data(), value.size())); |
|
|
|
|
if (dup) { |
|
|
|
|
setType(VALUE_IS_OWNED_RAW); |
|
|
|
|
_content.asString.data = dup; |
|
|
|
|
_content.asString.size = value.size(); |
|
|
|
|
content_.asString.data = dup; |
|
|
|
|
content_.asString.size = value.size(); |
|
|
|
|
return true; |
|
|
|
|
} else { |
|
|
|
|
setType(VALUE_IS_NULL); |
|
|
|
@ -204,13 +187,13 @@ class VariantData { |
|
|
|
|
template <typename T> |
|
|
|
|
typename enable_if<is_unsigned<T>::value>::type setInteger(T value) { |
|
|
|
|
setType(VALUE_IS_UNSIGNED_INTEGER); |
|
|
|
|
_content.asUnsignedInteger = static_cast<JsonUInt>(value); |
|
|
|
|
content_.asUnsignedInteger = static_cast<JsonUInt>(value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
typename enable_if<is_signed<T>::value>::type setInteger(T value) { |
|
|
|
|
setType(VALUE_IS_SIGNED_INTEGER); |
|
|
|
|
_content.asSignedInteger = value; |
|
|
|
|
content_.asSignedInteger = value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void setNull() { |
|
|
|
@ -223,20 +206,20 @@ class VariantData { |
|
|
|
|
setType(VALUE_IS_LINKED_STRING); |
|
|
|
|
else |
|
|
|
|
setType(VALUE_IS_OWNED_STRING); |
|
|
|
|
_content.asString.data = s.c_str(); |
|
|
|
|
_content.asString.size = s.size(); |
|
|
|
|
content_.asString.data = s.c_str(); |
|
|
|
|
content_.asString.size = s.size(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
CollectionData& toArray() { |
|
|
|
|
setType(VALUE_IS_ARRAY); |
|
|
|
|
_content.asCollection.clear(); |
|
|
|
|
return _content.asCollection; |
|
|
|
|
content_.asCollection.clear(); |
|
|
|
|
return content_.asCollection; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
CollectionData& toObject() { |
|
|
|
|
setType(VALUE_IS_OBJECT); |
|
|
|
|
_content.asCollection.clear(); |
|
|
|
|
return _content.asCollection; |
|
|
|
|
content_.asCollection.clear(); |
|
|
|
|
return content_.asCollection; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
size_t memoryUsage() const { |
|
|
|
@ -245,17 +228,17 @@ class VariantData { |
|
|
|
|
case VALUE_IS_OWNED_RAW: |
|
|
|
|
// We always add a zero at the end: the deduplication function uses it
|
|
|
|
|
// to detect the beginning of the next string.
|
|
|
|
|
return _content.asString.size + 1; |
|
|
|
|
return content_.asString.size + 1; |
|
|
|
|
case VALUE_IS_OBJECT: |
|
|
|
|
case VALUE_IS_ARRAY: |
|
|
|
|
return _content.asCollection.memoryUsage(); |
|
|
|
|
return content_.asCollection.memoryUsage(); |
|
|
|
|
default: |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
size_t size() const { |
|
|
|
|
return isCollection() ? _content.asCollection.size() : 0; |
|
|
|
|
return isCollection() ? content_.asCollection.size() : 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
VariantData* addElement(MemoryPool* pool) { |
|
|
|
@ -263,7 +246,7 @@ class VariantData { |
|
|
|
|
toArray(); |
|
|
|
|
if (!isArray()) |
|
|
|
|
return 0; |
|
|
|
|
return _content.asCollection.addElement(pool); |
|
|
|
|
return content_.asCollection.addElement(pool); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
VariantData* getElement(size_t index) const { |
|
|
|
@ -276,7 +259,7 @@ class VariantData { |
|
|
|
|
toArray(); |
|
|
|
|
if (!isArray()) |
|
|
|
|
return 0; |
|
|
|
|
return _content.asCollection.getOrAddElement(index, pool); |
|
|
|
|
return content_.asCollection.getOrAddElement(index, pool); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <typename TAdaptedString> |
|
|
|
@ -291,18 +274,18 @@ class VariantData { |
|
|
|
|
toObject(); |
|
|
|
|
if (!isObject()) |
|
|
|
|
return 0; |
|
|
|
|
return _content.asCollection.getOrAddMember(key, pool); |
|
|
|
|
return content_.asCollection.getOrAddMember(key, pool); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) { |
|
|
|
|
if (_flags & OWNED_VALUE_BIT) |
|
|
|
|
_content.asString.data += stringDistance; |
|
|
|
|
if (_flags & COLLECTION_MASK) |
|
|
|
|
_content.asCollection.movePointers(stringDistance, variantDistance); |
|
|
|
|
if (flags_ & OWNED_VALUE_BIT) |
|
|
|
|
content_.asString.data += stringDistance; |
|
|
|
|
if (flags_ & COLLECTION_MASK) |
|
|
|
|
content_.asCollection.movePointers(stringDistance, variantDistance); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
uint8_t type() const { |
|
|
|
|
return _flags & VALUE_MASK; |
|
|
|
|
return flags_ & VALUE_MASK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <typename TAdaptedString> |
|
|
|
@ -317,29 +300,23 @@ class VariantData { |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
void setType(uint8_t t) { |
|
|
|
|
_flags &= OWNED_KEY_BIT; |
|
|
|
|
_flags |= t; |
|
|
|
|
flags_ &= OWNED_KEY_BIT; |
|
|
|
|
flags_ |= t; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct VariantStringSetter { |
|
|
|
|
VariantStringSetter(VariantData* instance) : _instance(instance) {} |
|
|
|
|
VariantStringSetter(VariantData* instance) : instance_(instance) {} |
|
|
|
|
|
|
|
|
|
template <typename TStoredString> |
|
|
|
|
void operator()(TStoredString s) { |
|
|
|
|
if (s) |
|
|
|
|
_instance->setString(s); |
|
|
|
|
instance_->setString(s); |
|
|
|
|
else |
|
|
|
|
_instance->setNull(); |
|
|
|
|
instance_->setNull(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
VariantData* _instance; |
|
|
|
|
VariantData* instance_; |
|
|
|
|
}; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
ARDUINOJSON_END_PRIVATE_NAMESPACE |
|
|
|
|
|
|
|
|
|
#if defined(__GNUC__) |
|
|
|
|
# if __GNUC__ >= 8 |
|
|
|
|
# pragma GCC diagnostic pop |
|
|
|
|
# endif |
|
|
|
|
#endif |
|
|
|
|