Compare commits

..

11 Commits

Author SHA1 Message Date
908a69213c Fix null data_ 2025-06-26 15:23:57 +02:00
242d3f2f35 Fix resource manager tests 2025-06-26 15:18:46 +02:00
be1e33f5ff JsonVariantTests pass 2025-06-26 15:09:01 +02:00
cd2a4296af JsonVariantConstTests pass 2025-06-26 14:59:10 +02:00
462fab6f01 Extract ArrayImpl, CollectionImpl, and ObjectImpl 2025-06-25 16:29:07 +02:00
2feb07dc15 JsonObject: replace ObjectData* member with VariantData* 2025-06-24 09:39:23 +02:00
7f0cca379a JsonArray: replace ArrayData* member with VariantData* 2025-06-24 09:39:23 +02:00
434e90025d JsonObjectConst: replace ObjectData* member with VariantData* 2025-06-24 09:39:23 +02:00
19d2a8c555 JsonArrayConst: replace ArrayData* member with VariantData* 2025-06-24 09:39:22 +02:00
29a1c5732f Add a pool dedicated to 8-byte values (double/int64_t/uint64_t)
This new pool replaced the "extension" slot where a secondary variant slot was used to store 8-byte values.
2025-06-24 09:39:22 +02:00
943f22d8f9 Store static strings in a dedicated pool
Because a slot id is smaller than a pointer, this change will ultimately allow reducing the slot size.
2025-06-24 09:39:22 +02:00
27 changed files with 619 additions and 635 deletions

View File

@ -179,19 +179,19 @@ jobs:
conf_test_windows:
name: Test configuration on Windows
runs-on: windows-2022
runs-on: windows-2019
needs: [gcc, clang]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: 32-bit
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars32.bat"
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars32.bat"
cl /Isrc extras/conf_test/x86.cpp
shell: cmd
- name: 64-bit
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
cl /Isrc extras/conf_test/x64.cpp
shell: cmd

View File

@ -51,6 +51,7 @@
#include "ArduinoJson/Variant/ConverterImpl.hpp"
#include "ArduinoJson/Variant/JsonVariantCopier.hpp"
#include "ArduinoJson/Variant/VariantCompare.hpp"
#include "ArduinoJson/Variant/VariantImpl.hpp"
#include "ArduinoJson/Variant/VariantRefBaseImpl.hpp"
#include "ArduinoJson/Json/JsonDeserializer.hpp"

View File

@ -12,13 +12,9 @@ class ArrayImpl : public CollectionImpl {
public:
ArrayImpl() {}
ArrayImpl(VariantData* data, ResourceManager* resources)
ArrayImpl(CollectionData* data, ResourceManager* resources)
: CollectionImpl(data, resources) {}
bool isNull() const {
return !data_ || data_->type != VariantType::Array;
}
VariantData* addElement();
template <typename T>

View File

@ -11,9 +11,6 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline ArrayImpl::iterator ArrayImpl::at(size_t index) const {
if (isNull())
return iterator();
auto it = createIterator();
while (!it.done() && index) {
it.next(resources_);
@ -23,9 +20,10 @@ inline ArrayImpl::iterator ArrayImpl::at(size_t index) const {
}
inline VariantData* ArrayImpl::addElement() {
if (isNull())
if (!data_)
return nullptr;
auto slot = allocVariant();
ARDUINOJSON_ASSERT(resources_ != nullptr);
auto slot = resources_->allocVariant();
if (!slot)
return nullptr;
CollectionImpl::appendOne(slot);
@ -60,14 +58,15 @@ inline void ArrayImpl::removeElement(size_t index) {
template <typename T>
inline bool ArrayImpl::addValue(const T& value) {
if (isNull())
if (!data_)
return false;
auto slot = allocVariant();
ARDUINOJSON_ASSERT(resources_ != nullptr);
auto slot = resources_->allocVariant();
if (!slot)
return false;
JsonVariant variant(slot.ptr(), resources_);
if (!variant.set(value)) {
freeVariant(slot);
resources_->freeVariant(slot);
return false;
}
CollectionImpl::appendOne(slot);

View File

@ -24,7 +24,10 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// INTERNAL USE ONLY
JsonArray(detail::VariantData* data, detail::ResourceManager* resources)
: impl_(data, resources) {}
: impl_(detail::VariantImpl(data, resources).asArray()) {}
// INTERNAL USE ONLY
JsonArray(const detail::ArrayImpl& impl) : impl_(impl) {}
// Returns a JsonVariant pointing to the array.
// https://arduinojson.org/v7/api/jsonvariant/

View File

@ -38,7 +38,7 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
// INTERNAL USE ONLY
JsonArrayConst(detail::VariantData* data, detail::ResourceManager* resources)
: impl_(data, resources) {}
: impl_(detail::VariantImpl(data, resources).asArray()) {}
// INTERNAL USE ONLY
JsonArrayConst(const detail::ArrayImpl& impl) : impl_(impl) {}

View File

@ -4,6 +4,7 @@
#pragma once
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
@ -64,9 +65,21 @@ class CollectionIterator {
SlotId currentId_, nextId_;
};
struct CollectionData {
SlotId head = NULL_SLOT;
SlotId tail = NULL_SLOT;
// Placement new
static void* operator new(size_t, void* p) noexcept {
return p;
}
static void operator delete(void*, void*) noexcept {}
};
class CollectionImpl {
protected:
VariantData* data_;
CollectionData* data_;
ResourceManager* resources_;
public:
@ -74,19 +87,20 @@ class CollectionImpl {
CollectionImpl() : data_(nullptr), resources_(nullptr) {}
CollectionImpl(VariantData* data, ResourceManager* resources)
CollectionImpl(CollectionData* data, ResourceManager* resources)
: data_(data), resources_(resources) {}
explicit operator bool() const {
return data_ && data_->isCollection();
return data_ != nullptr;
}
bool isNull() const {
return !operator bool();
return data_ == nullptr;
}
VariantData* getData() const {
return data_;
void* data = data_; // prevent warning cast-align
return reinterpret_cast<VariantData*>(data);
}
ResourceManager* getResourceManager() const {
@ -101,7 +115,7 @@ class CollectionImpl {
void clear();
SlotId head() const {
return getCollectionData()->head;
return data_->head;
}
protected:
@ -111,29 +125,8 @@ class CollectionImpl {
void removeOne(iterator it);
void removePair(iterator it);
VariantData* getVariant(SlotId id) const {
ARDUINOJSON_ASSERT(resources_ != nullptr);
return resources_->getVariant(id);
}
void freeVariant(Slot<VariantData> slot) {
ARDUINOJSON_ASSERT(resources_ != nullptr);
resources_->freeVariant(slot);
}
Slot<VariantData> allocVariant() {
ARDUINOJSON_ASSERT(resources_ != nullptr);
return resources_->allocVariant();
}
private:
Slot<VariantData> getPreviousSlot(VariantData*) const;
CollectionData* getCollectionData() const {
ARDUINOJSON_ASSERT(data_ != nullptr);
ARDUINOJSON_ASSERT(data_->isCollection());
return &data_->content.asCollection;
}
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -26,66 +26,63 @@ inline void CollectionIterator::next(const ResourceManager* resources) {
}
inline CollectionImpl::iterator CollectionImpl::createIterator() const {
if (!data_ || !data_->isCollection())
if (!data_)
return iterator();
auto coll = getCollectionData();
return iterator(getVariant(coll->head), coll->head);
return iterator(resources_->getVariant(data_->head), data_->head);
}
inline void CollectionImpl::appendOne(Slot<VariantData> slot) {
auto coll = getCollectionData();
ARDUINOJSON_ASSERT(data_ != nullptr);
ARDUINOJSON_ASSERT(resources_ != nullptr);
if (coll->tail != NULL_SLOT) {
auto tail = getVariant(coll->tail);
if (data_->tail != NULL_SLOT) {
auto tail = resources_->getVariant(data_->tail);
tail->next = slot.id();
coll->tail = slot.id();
data_->tail = slot.id();
} else {
coll->head = slot.id();
coll->tail = slot.id();
data_->head = slot.id();
data_->tail = slot.id();
}
}
inline void CollectionImpl::appendPair(Slot<VariantData> key,
Slot<VariantData> value) {
auto coll = getCollectionData();
ARDUINOJSON_ASSERT(data_ != nullptr);
ARDUINOJSON_ASSERT(resources_ != nullptr);
key->next = value.id();
if (coll->tail != NULL_SLOT) {
auto tail = getVariant(coll->tail);
if (data_->tail != NULL_SLOT) {
auto tail = resources_->getVariant(data_->tail);
tail->next = key.id();
coll->tail = value.id();
data_->tail = value.id();
} else {
coll->head = key.id();
coll->tail = value.id();
data_->head = key.id();
data_->tail = value.id();
}
}
inline void CollectionImpl::clear() {
if (!data_ || !data_->isCollection())
if (!data_)
return;
auto coll = getCollectionData();
auto next = coll->head;
auto next = data_->head;
while (next != NULL_SLOT) {
auto currId = next;
auto slot = getVariant(next);
auto slot = resources_->getVariant(next);
next = slot->next;
freeVariant({slot, currId});
resources_->freeVariant({slot, currId});
}
coll->head = NULL_SLOT;
coll->tail = NULL_SLOT;
data_->head = NULL_SLOT;
data_->tail = NULL_SLOT;
}
inline Slot<VariantData> CollectionImpl::getPreviousSlot(
VariantData* target) const {
auto coll = getCollectionData();
auto prev = Slot<VariantData>();
auto currentId = coll->head;
auto currentId = data_->head;
while (currentId != NULL_SLOT) {
auto currentSlot = getVariant(currentId);
auto currentSlot = resources_->getVariant(currentId);
if (currentSlot == target)
break;
prev = Slot<VariantData>(currentSlot, currentId);
@ -97,17 +94,16 @@ inline Slot<VariantData> CollectionImpl::getPreviousSlot(
inline void CollectionImpl::removeOne(iterator it) {
if (it.done())
return;
auto coll = getCollectionData();
auto curr = it.slot_;
auto prev = getPreviousSlot(curr);
auto next = curr->next;
if (prev)
prev->next = next;
else
coll->head = next;
data_->head = next;
if (next == NULL_SLOT)
coll->tail = prev.id();
freeVariant({it.slot_, it.currentId_});
data_->tail = prev.id();
resources_->freeVariant({it.slot_, it.currentId_});
}
inline void CollectionImpl::removePair(ObjectImpl::iterator it) {
@ -117,18 +113,18 @@ inline void CollectionImpl::removePair(ObjectImpl::iterator it) {
auto keySlot = it.slot_;
auto valueId = it.nextId_;
auto valueSlot = getVariant(valueId);
auto valueSlot = resources_->getVariant(valueId);
// remove value slot
keySlot->next = valueSlot->next;
freeVariant({valueSlot, valueId});
resources_->freeVariant({valueSlot, valueId});
// remove key slot
removeOne(it);
}
inline size_t CollectionImpl::nesting() const {
if (!data_ || !data_->isCollection())
if (!data_)
return 0;
size_t maxChildNesting = 0;
for (auto it = createIterator(); !it.done(); it.next(resources_)) {

View File

@ -308,7 +308,8 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
void remove(const TString& key) {
getVariantImpl().removeMember(detail::adaptString(key));
detail::VariantImpl(getData(), getResourceManager())
.removeMember(detail::adaptString(key));
}
// Removes a member of the root object or an element of the root array.

View File

@ -64,8 +64,6 @@ class JsonDeserializer {
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
ARDUINOJSON_ASSERT(variant != nullptr);
err = skipSpacesAndComments();
if (err)
return err;
@ -73,13 +71,15 @@ class JsonDeserializer {
switch (current()) {
case '[':
if (filter.allowArray())
return parseArray(variant->toArray(), filter, nestingLimit);
return parseArray(VariantImpl(variant, resources_).toArray(), filter,
nestingLimit);
else
return skipArray(nestingLimit);
case '{':
if (filter.allowObject())
return parseObject(variant->toObject(), filter, nestingLimit);
return parseObject(VariantImpl(variant, resources_).toObject(),
filter, nestingLimit);
else
return skipObject(nestingLimit);
@ -148,7 +148,7 @@ class JsonDeserializer {
template <typename TFilter>
DeserializationError::Code parseArray(
VariantData* arrayData, TFilter filter,
ArrayImpl array, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
@ -173,8 +173,6 @@ class JsonDeserializer {
// Read each value
for (;;) {
if (elementFilter.allow()) {
ArrayImpl array(arrayData, resources_);
// Allocate slot in array
VariantData* value = array.addElement();
if (!value)
@ -236,7 +234,7 @@ class JsonDeserializer {
template <typename TFilter>
DeserializationError::Code parseObject(
VariantData* objectData, TFilter filter,
ObjectImpl object, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
@ -277,7 +275,6 @@ class JsonDeserializer {
TFilter memberFilter = filter[key];
if (memberFilter.allow()) {
ObjectImpl object(objectData, resources_);
auto member = object.getMember(adaptString(key));
if (!member) {
auto keyVariant = object.addPair(&member);

View File

@ -4,9 +4,10 @@
#pragma once
#include <ArduinoJson/Collection/CollectionData.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Polyfills/alias_cast.hpp>
#include <ArduinoJson/Variant/VariantImpl.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE

View File

@ -5,7 +5,6 @@
#pragma once
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Strings/JsonString.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE

View File

@ -352,7 +352,7 @@ class MsgPackDeserializer {
ArrayImpl array;
if (allowArray) {
ARDUINOJSON_ASSERT(variant != 0);
array = ArrayImpl(variant->toArray(), resources_);
array = VariantImpl(variant, resources_).toArray();
}
TFilter elementFilter = filter[0U];
@ -388,7 +388,7 @@ class MsgPackDeserializer {
ObjectImpl object;
if (filter.allowObject()) {
ARDUINOJSON_ASSERT(variant != 0);
object = ObjectImpl(variant->toObject(), resources_);
object = VariantImpl(variant, resources_).toObject();
}
for (; n; --n) {

View File

@ -22,9 +22,12 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
// Creates an unbound reference.
JsonObject() {}
// INTERNAL USE ONLY
JsonObject(const detail::ObjectImpl& impl) : impl_(impl) {}
// INTERNAL USE ONLY
JsonObject(detail::VariantData* data, detail::ResourceManager* resource)
: impl_(data, resource) {}
: impl_(detail::VariantImpl(data, resource).asObject()) {}
operator JsonVariant() const {
return JsonVariant(getData(), getResourceManager());

View File

@ -23,7 +23,7 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
// INTERNAL USE ONLY
JsonObjectConst(detail::VariantData* data, detail::ResourceManager* resources)
: impl_(data, resources) {}
: impl_(detail::VariantImpl(data, resources).asObject()) {}
// INTERNAL USE ONLY
JsonObjectConst(const detail::ObjectImpl& impl) : impl_(impl) {}

View File

@ -56,7 +56,9 @@ class MemberProxy
}
VariantData* getData() const {
return VariantAttorney::getVariantImpl(upstream_).getMember(key_);
VariantImpl variant(VariantAttorney::getData(upstream_),
VariantAttorney::getResourceManager(upstream_));
return variant.getMember(key_);
}
VariantData* getOrCreateData() const {

View File

@ -14,13 +14,9 @@ class ObjectImpl : public CollectionImpl {
public:
ObjectImpl() {}
ObjectImpl(VariantData* data, ResourceManager* resources)
ObjectImpl(CollectionData* data, ResourceManager* resources)
: CollectionImpl(data, resources) {}
bool isNull() const {
return !data_ || data_->type != VariantType::Object;
}
template <typename TAdaptedString>
VariantData* addMember(TAdaptedString key);

View File

@ -29,8 +29,6 @@ VariantData* ObjectImpl::getOrAddMember(TAdaptedString key) {
template <typename TAdaptedString>
inline ObjectImpl::iterator ObjectImpl::findKey(TAdaptedString key) const {
if (isNull())
return iterator();
if (key.isNull())
return iterator();
bool isKey = true;
@ -50,14 +48,15 @@ inline void ObjectImpl::removeMember(TAdaptedString key) {
template <typename TAdaptedString>
inline VariantData* ObjectImpl::addMember(TAdaptedString key) {
if (isNull())
if (!data_)
return nullptr;
ARDUINOJSON_ASSERT(resources_ != nullptr);
auto keySlot = allocVariant();
auto keySlot = resources_->allocVariant();
if (!keySlot)
return nullptr;
auto valueSlot = allocVariant();
auto valueSlot = resources_->allocVariant();
if (!valueSlot)
return nullptr;
@ -71,13 +70,15 @@ inline VariantData* ObjectImpl::addMember(TAdaptedString key) {
}
inline VariantData* ObjectImpl::addPair(VariantData** value) {
ARDUINOJSON_ASSERT(!isNull());
if (!data_)
return nullptr;
ARDUINOJSON_ASSERT(resources_ != nullptr);
auto keySlot = allocVariant();
auto keySlot = resources_->allocVariant();
if (!keySlot)
return nullptr;
auto valueSlot = allocVariant();
auto valueSlot = resources_->allocVariant();
if (!valueSlot)
return nullptr;
*value = valueSlot.ptr();

View File

@ -14,7 +14,8 @@ size_t measure(ArduinoJson::JsonVariantConst source) {
auto data = VariantAttorney::getData(source);
auto resources = VariantAttorney::getResourceManager(source);
TSerializer<DummyWriter> serializer(dp, resources);
return VariantImpl(data, resources).accept(serializer);
VariantImpl variant(data, resources);
return variant.accept(serializer);
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -13,7 +13,8 @@ size_t doSerialize(ArduinoJson::JsonVariantConst source, TWriter writer) {
auto data = VariantAttorney::getData(source);
auto resources = VariantAttorney::getResourceManager(source);
TSerializer<TWriter> serializer(writer, resources);
return VariantImpl(data, resources).accept(serializer);
VariantImpl variant(data, resources);
return variant.accept(serializer);
}
template <template <typename> class TSerializer, typename TDestination>

View File

@ -240,7 +240,8 @@ inline void convertToJson(const ::Printable& src, JsonVariant dst) {
auto data = detail::VariantAttorney::getData(dst);
if (!resources || !data)
return;
detail::VariantImpl(data, resources).clear();
detail::VariantImpl impl(data, resources);
impl.clear();
detail::StringBuilderPrint print(resources);
src.printTo(print);
if (print.overflowed())

View File

@ -50,8 +50,9 @@ typename TVisitor::result_type accept(JsonVariantConst variant,
TVisitor& visit) {
auto data = VariantAttorney::getData(variant);
auto resources = VariantAttorney::getResourceManager(variant);
VariantImpl impl(data, resources);
VisitorAdapter<TVisitor> adapter(visit);
return VariantImpl(data, resources).accept(adapter);
return impl.accept(adapter);
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -6,7 +6,7 @@
#include <ArduinoJson/Polyfills/attributes.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Variant/VariantImpl.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
#include <ArduinoJson/Variant/VariantTo.hpp>
#include "JsonVariantConst.hpp"

View File

@ -6,8 +6,10 @@
#include <stddef.h> // size_t
#include <ArduinoJson/Array/ArrayData.hpp>
#include <ArduinoJson/Numbers/JsonFloat.hpp>
#include <ArduinoJson/Numbers/JsonInteger.hpp>
#include <ArduinoJson/Object/ObjectData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
@ -45,18 +47,6 @@ inline bool operator&(VariantType type, VariantTypeBits bit) {
return (uint8_t(type) & uint8_t(bit)) != 0;
}
struct CollectionData {
SlotId head = NULL_SLOT;
SlotId tail = NULL_SLOT;
// Placement new
static void* operator new(size_t, void* p) noexcept {
return p;
}
static void operator delete(void*, void*) noexcept {}
};
const size_t tinyStringMaxLength = 3;
union VariantContent {

View File

@ -4,7 +4,12 @@
#pragma once
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Memory/StringNode.hpp>
#include <ArduinoJson/Misc/SerializedValue.hpp>
#include <ArduinoJson/Numbers/convertNumber.hpp>
#include <ArduinoJson/Strings/JsonString.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <ArduinoJson/Variant/VariantContent.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
@ -71,10 +76,6 @@ struct VariantData {
content.asOwnedString = s;
}
bool isCollection() const {
return type & VariantTypeBits::CollectionMask;
}
bool isFloat() const {
return type & VariantTypeBits::NumberBit;
}
@ -83,42 +84,424 @@ struct VariantData {
return type == VariantType::LinkedString ||
type == VariantType::OwnedString || type == VariantType::TinyString;
}
};
VariantData* toArray() {
ARDUINOJSON_ASSERT(type == VariantType::Null);
type = VariantType::Array;
new (&content.asCollection) CollectionData();
return this;
class VariantImpl {
public:
VariantImpl() : data_(nullptr), resources_(nullptr) {}
VariantImpl(VariantData* data, ResourceManager* resources)
: data_(data), resources_(resources) {}
VariantData* getData() const {
return data_;
}
VariantData* toObject() {
ARDUINOJSON_ASSERT(type == VariantType::Null);
type = VariantType::Object;
new (&content.asCollection) CollectionData();
return this;
ResourceManager* getResourceManager() const {
return resources_;
}
VariantData* getOrCreateArray() {
switch (type) {
case VariantType::Null:
return toArray();
template <typename TVisitor>
typename TVisitor::result_type accept(TVisitor& visit) {
if (!data_)
return visit.visit(nullptr);
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte();
#endif
switch (data_->type) {
case VariantType::Float:
return visit.visit(data_->content.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return visit.visit(eightByteValue->asDouble);
#endif
case VariantType::Array:
return this;
return visit.visit(asArray());
case VariantType::Object:
return visit.visit(asObject());
case VariantType::TinyString:
return visit.visit(JsonString(data_->content.asTinyString));
case VariantType::LinkedString:
return visit.visit(JsonString(asLinkedString(), true));
case VariantType::OwnedString:
return visit.visit(JsonString(data_->content.asOwnedString->data,
data_->content.asOwnedString->length));
case VariantType::RawString:
return visit.visit(RawString(data_->content.asOwnedString->data,
data_->content.asOwnedString->length));
case VariantType::Int32:
return visit.visit(static_cast<JsonInteger>(data_->content.asInt32));
case VariantType::Uint32:
return visit.visit(static_cast<JsonUInt>(data_->content.asUint32));
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Int64:
return visit.visit(eightByteValue->asInt64);
case VariantType::Uint64:
return visit.visit(eightByteValue->asUint64);
#endif
case VariantType::Boolean:
return visit.visit(data_->content.asBoolean != 0);
default:
return nullptr;
return visit.visit(nullptr);
}
}
VariantData* getOrCreateObject() {
switch (type) {
VariantData* addElement() {
auto array = isNull() ? toArray() : asArray();
return array.addElement();
}
template <typename T>
bool addValue(const T& value) {
auto array = isNull() ? toArray() : asArray();
return array.addValue(value);
}
bool asBoolean() const {
if (!data_)
return false;
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte();
#endif
switch (data_->type) {
case VariantType::Boolean:
return data_->content.asBoolean;
case VariantType::Uint32:
case VariantType::Int32:
return data_->content.asUint32 != 0;
case VariantType::Float:
return data_->content.asFloat != 0;
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return eightByteValue->asDouble != 0;
#endif
case VariantType::Null:
return toObject();
case VariantType::Object:
return this;
return false;
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
case VariantType::Int64:
return eightByteValue->asUint64 != 0;
#endif
default:
return nullptr;
return true;
}
}
ArrayImpl asArray() {
return ArrayImpl(isArray() ? &data_->content.asCollection : nullptr,
resources_);
}
CollectionImpl asCollection() {
return CollectionImpl(
isCollection() ? &data_->content.asCollection : nullptr, resources_);
}
template <typename T>
T asFloat() const {
if (!data_)
return 0.0;
static_assert(is_floating_point<T>::value, "T must be a floating point");
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte();
#endif
const char* str = nullptr;
switch (data_->type) {
case VariantType::Boolean:
return static_cast<T>(data_->content.asBoolean);
case VariantType::Uint32:
return static_cast<T>(data_->content.asUint32);
case VariantType::Int32:
return static_cast<T>(data_->content.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
return static_cast<T>(eightByteValue->asUint64);
case VariantType::Int64:
return static_cast<T>(eightByteValue->asInt64);
#endif
case VariantType::TinyString:
str = data_->content.asTinyString;
break;
case VariantType::LinkedString:
str = asLinkedString();
break;
case VariantType::OwnedString:
str = data_->content.asOwnedString->data;
break;
case VariantType::Float:
return static_cast<T>(data_->content.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return static_cast<T>(eightByteValue->asDouble);
#endif
default:
return 0.0;
}
ARDUINOJSON_ASSERT(str != nullptr);
return parseNumber<T>(str);
}
template <typename T>
T asIntegral() const {
if (!data_)
return 0;
static_assert(is_integral<T>::value, "T must be an integral type");
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte();
#endif
const char* str = nullptr;
switch (data_->type) {
case VariantType::Boolean:
return data_->content.asBoolean;
case VariantType::Uint32:
return convertNumber<T>(data_->content.asUint32);
case VariantType::Int32:
return convertNumber<T>(data_->content.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
return convertNumber<T>(eightByteValue->asUint64);
case VariantType::Int64:
return convertNumber<T>(eightByteValue->asInt64);
#endif
case VariantType::TinyString:
str = data_->content.asTinyString;
break;
case VariantType::LinkedString:
str = asLinkedString();
break;
case VariantType::OwnedString:
str = data_->content.asOwnedString->data;
break;
case VariantType::Float:
return convertNumber<T>(data_->content.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return convertNumber<T>(eightByteValue->asDouble);
#endif
default:
return 0;
}
ARDUINOJSON_ASSERT(str != nullptr);
return parseNumber<T>(str);
}
ObjectImpl asObject() {
return ObjectImpl(isObject() ? &data_->content.asCollection : nullptr,
resources_);
}
JsonString asRawString() const {
switch (type()) {
case VariantType::RawString:
return JsonString(data_->content.asOwnedString->data,
data_->content.asOwnedString->length);
default:
return JsonString();
}
}
const char* asLinkedString() const;
JsonString asString() const {
switch (type()) {
case VariantType::TinyString:
return JsonString(data_->content.asTinyString);
case VariantType::LinkedString:
return JsonString(asLinkedString(), true);
case VariantType::OwnedString:
return JsonString(data_->content.asOwnedString->data,
data_->content.asOwnedString->length);
default:
return JsonString();
}
}
#if ARDUINOJSON_USE_8_BYTE_POOL
const EightByteValue* getEightByte() const;
#endif
VariantData* getElement(size_t index) {
return asArray().getElement(index);
}
template <typename TAdaptedString>
VariantData* getMember(TAdaptedString key) {
return asObject().getMember(key);
}
VariantData* getOrAddElement(size_t index) {
auto array = isNull() ? toArray() : asArray();
return array.getOrAddElement(index);
}
template <typename TAdaptedString>
VariantData* getOrAddMember(TAdaptedString key) {
if (key.isNull())
return nullptr;
auto obj = isNull() ? toObject() : asObject();
return obj.getOrAddMember(key);
}
bool isArray() const {
return type() == VariantType::Array;
}
bool isBoolean() const {
return type() == VariantType::Boolean;
}
bool isCollection() const {
return type() & VariantTypeBits::CollectionMask;
}
bool isFloat() const {
return data_ && data_->isFloat();
}
template <typename T>
bool isInteger() const {
if (!data_)
return false;
#if ARDUINOJSON_USE_LONG_LONG
auto eightByteValue = getEightByte();
#endif
switch (data_->type) {
case VariantType::Uint32:
return canConvertNumber<T>(data_->content.asUint32);
case VariantType::Int32:
return canConvertNumber<T>(data_->content.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
return canConvertNumber<T>(eightByteValue->asUint64);
case VariantType::Int64:
return canConvertNumber<T>(eightByteValue->asInt64);
#endif
default:
return false;
}
}
bool isNull() const {
return type() == VariantType::Null;
}
bool isObject() const {
return type() == VariantType::Object;
}
bool isString() const {
return data_ && data_->isString();
}
size_t nesting() {
return asCollection().nesting();
}
void removeElement(size_t index) {
asArray().removeElement(index);
}
template <typename TAdaptedString>
void removeMember(TAdaptedString key) {
asObject().removeMember(key);
}
bool setBoolean(bool value) {
if (!data_)
return false;
data_->setBoolean(value);
return true;
}
template <typename T>
enable_if_t<sizeof(T) == 4, bool> setFloat(T value) {
ARDUINOJSON_ASSERT(type() == VariantType::Null); // must call clear() first
if (!data_)
return false;
data_->type = VariantType::Float;
data_->content.asFloat = value;
return true;
}
template <typename T>
enable_if_t<sizeof(T) == 8, bool> setFloat(T value);
template <typename T>
enable_if_t<is_signed<T>::value, bool> setInteger(T value);
template <typename T>
enable_if_t<is_unsigned<T>::value, bool> setInteger(T value);
template <typename T>
void setRawString(SerializedValue<T> value);
template <typename TAdaptedString>
bool setString(TAdaptedString value);
bool setLinkedString(const char* s);
size_t size() {
if (isObject())
return asObject().size();
if (isArray())
return asArray().size();
return 0;
}
ArrayImpl toArray() {
ARDUINOJSON_ASSERT(type() == VariantType::Null); // must call clear() first
if (!data_)
return ArrayImpl();
data_->type = VariantType::Array;
return ArrayImpl(new (&data_->content.asCollection) CollectionData(),
resources_);
}
ObjectImpl toObject() {
ARDUINOJSON_ASSERT(type() == VariantType::Null); // must call clear() first
if (!data_)
return ObjectImpl();
data_->type = VariantType::Object;
return ObjectImpl(new (&data_->content.asCollection) CollectionData(),
resources_);
}
VariantType type() const {
return data_ ? data_->type : VariantType::Null;
}
// Release the resources used by this variant and set it to null.
void clear();
private:
VariantData* data_;
ResourceManager* resources_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -4,477 +4,11 @@
#pragma once
#include <ArduinoJson/Array/ArrayData.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Misc/SerializedValue.hpp>
#include <ArduinoJson/Numbers/convertNumber.hpp>
#include <ArduinoJson/Object/ObjectData.hpp>
#include <ArduinoJson/Strings/JsonString.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantImpl {
public:
VariantImpl() : data_(nullptr), resources_(nullptr) {}
VariantImpl(VariantData* data, ResourceManager* resources)
: data_(data), resources_(resources) {}
VariantData* getData() const {
return data_;
}
ResourceManager* getResourceManager() const {
return resources_;
}
template <typename TVisitor>
typename TVisitor::result_type accept(TVisitor& visit) {
if (!data_)
return visit.visit(nullptr);
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte();
#endif
switch (data_->type) {
case VariantType::Float:
return visit.visit(data_->content.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return visit.visit(eightByteValue->asDouble);
#endif
case VariantType::Array:
return visit.visit(ArrayImpl(data_, resources_));
case VariantType::Object:
return visit.visit(ObjectImpl(data_, resources_));
case VariantType::TinyString:
return visit.visit(JsonString(data_->content.asTinyString));
case VariantType::LinkedString:
return visit.visit(JsonString(asLinkedString(), true));
case VariantType::OwnedString:
return visit.visit(JsonString(data_->content.asOwnedString->data,
data_->content.asOwnedString->length));
case VariantType::RawString:
return visit.visit(RawString(data_->content.asOwnedString->data,
data_->content.asOwnedString->length));
case VariantType::Int32:
return visit.visit(static_cast<JsonInteger>(data_->content.asInt32));
case VariantType::Uint32:
return visit.visit(static_cast<JsonUInt>(data_->content.asUint32));
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Int64:
return visit.visit(eightByteValue->asInt64);
case VariantType::Uint64:
return visit.visit(eightByteValue->asUint64);
#endif
case VariantType::Boolean:
return visit.visit(data_->content.asBoolean != 0);
default:
return visit.visit(nullptr);
}
}
VariantData* addElement() {
if (!data_)
return nullptr;
return ArrayImpl(data_->getOrCreateArray(), resources_).addElement();
}
template <typename T>
bool addValue(const T& value) {
if (!data_)
return false;
return ArrayImpl(data_->getOrCreateArray(), resources_).addValue(value);
}
bool asBoolean() const {
if (!data_)
return false;
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte();
#endif
switch (data_->type) {
case VariantType::Boolean:
return data_->content.asBoolean;
case VariantType::Uint32:
case VariantType::Int32:
return data_->content.asUint32 != 0;
case VariantType::Float:
return data_->content.asFloat != 0;
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return eightByteValue->asDouble != 0;
#endif
case VariantType::Null:
return false;
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
case VariantType::Int64:
return eightByteValue->asUint64 != 0;
#endif
default:
return true;
}
}
template <typename T>
T asFloat() const {
if (!data_)
return 0.0;
static_assert(is_floating_point<T>::value, "T must be a floating point");
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte();
#endif
const char* str = nullptr;
switch (data_->type) {
case VariantType::Boolean:
return static_cast<T>(data_->content.asBoolean);
case VariantType::Uint32:
return static_cast<T>(data_->content.asUint32);
case VariantType::Int32:
return static_cast<T>(data_->content.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
return static_cast<T>(eightByteValue->asUint64);
case VariantType::Int64:
return static_cast<T>(eightByteValue->asInt64);
#endif
case VariantType::TinyString:
str = data_->content.asTinyString;
break;
case VariantType::LinkedString:
str = asLinkedString();
break;
case VariantType::OwnedString:
str = data_->content.asOwnedString->data;
break;
case VariantType::Float:
return static_cast<T>(data_->content.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return static_cast<T>(eightByteValue->asDouble);
#endif
default:
return 0.0;
}
ARDUINOJSON_ASSERT(str != nullptr);
return parseNumber<T>(str);
}
template <typename T>
T asIntegral() const {
if (!data_)
return 0;
static_assert(is_integral<T>::value, "T must be an integral type");
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte();
#endif
const char* str = nullptr;
switch (data_->type) {
case VariantType::Boolean:
return data_->content.asBoolean;
case VariantType::Uint32:
return convertNumber<T>(data_->content.asUint32);
case VariantType::Int32:
return convertNumber<T>(data_->content.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
return convertNumber<T>(eightByteValue->asUint64);
case VariantType::Int64:
return convertNumber<T>(eightByteValue->asInt64);
#endif
case VariantType::TinyString:
str = data_->content.asTinyString;
break;
case VariantType::LinkedString:
str = asLinkedString();
break;
case VariantType::OwnedString:
str = data_->content.asOwnedString->data;
break;
case VariantType::Float:
return convertNumber<T>(data_->content.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return convertNumber<T>(eightByteValue->asDouble);
#endif
default:
return 0;
}
ARDUINOJSON_ASSERT(str != nullptr);
return parseNumber<T>(str);
}
JsonString asRawString() const {
switch (type()) {
case VariantType::RawString:
return JsonString(data_->content.asOwnedString->data,
data_->content.asOwnedString->length);
default:
return JsonString();
}
}
const char* asLinkedString() const {
ARDUINOJSON_ASSERT(type() == VariantType::LinkedString);
return resources_->getStaticString(data_->content.asSlotId);
}
JsonString asString() const {
switch (type()) {
case VariantType::TinyString:
return JsonString(data_->content.asTinyString);
case VariantType::LinkedString:
return JsonString(asLinkedString(), true);
case VariantType::OwnedString:
return JsonString(data_->content.asOwnedString->data,
data_->content.asOwnedString->length);
default:
return JsonString();
}
}
#if ARDUINOJSON_USE_8_BYTE_POOL
const EightByteValue* getEightByte() const {
return type() & VariantTypeBits::EightByteBit
? resources_->getEightByte(data_->content.asSlotId)
: 0;
}
#endif
VariantData* getElement(size_t index) {
return ArrayImpl(data_, resources_).getElement(index);
}
template <typename TAdaptedString>
VariantData* getMember(TAdaptedString key) {
return ObjectImpl(data_, resources_).getMember(key);
}
VariantData* getOrAddElement(size_t index) {
if (!data_)
return nullptr;
return ArrayImpl(data_->getOrCreateArray(), resources_)
.getOrAddElement(index);
}
template <typename TAdaptedString>
VariantData* getOrAddMember(TAdaptedString key) {
if (key.isNull())
return nullptr;
if (!data_)
return nullptr;
return ObjectImpl(data_->getOrCreateObject(), resources_)
.getOrAddMember(key);
}
bool isArray() const {
return type() == VariantType::Array;
}
bool isBoolean() const {
return type() == VariantType::Boolean;
}
bool isFloat() const {
return data_ && data_->isFloat();
}
template <typename T>
bool isInteger() const {
if (!data_)
return false;
#if ARDUINOJSON_USE_LONG_LONG
auto eightByteValue = getEightByte();
#endif
switch (data_->type) {
case VariantType::Uint32:
return canConvertNumber<T>(data_->content.asUint32);
case VariantType::Int32:
return canConvertNumber<T>(data_->content.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
return canConvertNumber<T>(eightByteValue->asUint64);
case VariantType::Int64:
return canConvertNumber<T>(eightByteValue->asInt64);
#endif
default:
return false;
}
}
bool isNull() const {
return type() == VariantType::Null;
}
bool isObject() const {
return type() == VariantType::Object;
}
bool isString() const {
return data_ && data_->isString();
}
size_t nesting() {
return CollectionImpl(data_, resources_).nesting();
}
void removeElement(size_t index) {
ArrayImpl(data_, resources_).removeElement(index);
}
template <typename TAdaptedString>
void removeMember(TAdaptedString key) {
ObjectImpl(data_, resources_).removeMember(key);
}
bool setBoolean(bool value) {
if (!data_)
return false;
data_->setBoolean(value);
return true;
}
template <typename T>
enable_if_t<sizeof(T) == 4, bool> setFloat(T value) {
ARDUINOJSON_ASSERT(type() == VariantType::Null); // must call clear() first
if (!data_)
return false;
data_->type = VariantType::Float;
data_->content.asFloat = value;
return true;
}
template <typename T>
enable_if_t<sizeof(T) == 8, bool> setFloat(T value) {
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
if (!data_)
return false;
float valueAsFloat = static_cast<float>(value);
#if ARDUINOJSON_USE_DOUBLE
if (value == valueAsFloat) {
data_->type = VariantType::Float;
data_->content.asFloat = valueAsFloat;
} else {
auto slot = resources_->allocEightByte();
if (!slot)
return false;
data_->type = VariantType::Double;
data_->content.asSlotId = slot.id();
slot->asDouble = value;
}
#else
data_->type = VariantType::Float;
data_->content.asFloat = valueAsFloat;
#endif
return true;
}
template <typename T>
enable_if_t<is_signed<T>::value, bool> setInteger(T value) {
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
if (!data_)
return false;
if (canConvertNumber<int32_t>(value)) {
data_->type = VariantType::Int32;
data_->content.asInt32 = static_cast<int32_t>(value);
}
#if ARDUINOJSON_USE_LONG_LONG
else {
auto slot = resources_->allocEightByte();
if (!slot)
return false;
data_->type = VariantType::Int64;
data_->content.asSlotId = slot.id();
slot->asInt64 = value;
}
#endif
return true;
}
template <typename T>
enable_if_t<is_unsigned<T>::value, bool> setInteger(T value) {
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
if (!data_)
return false;
if (canConvertNumber<uint32_t>(value)) {
data_->type = VariantType::Uint32;
data_->content.asUint32 = static_cast<uint32_t>(value);
}
#if ARDUINOJSON_USE_LONG_LONG
else {
auto slot = resources_->allocEightByte();
if (!slot)
return false;
data_->type = VariantType::Uint64;
data_->content.asSlotId = slot.id();
slot->asUint64 = value;
}
#endif
return true;
}
template <typename T>
void setRawString(SerializedValue<T> value);
template <typename TAdaptedString>
bool setString(TAdaptedString value);
bool setLinkedString(const char* s);
size_t size() {
auto size = CollectionImpl(data_, resources_).size();
if (data_ && data_->type == VariantType::Object)
size /= 2;
return size;
}
VariantType type() const {
return data_ ? data_->type : VariantType::Null;
}
// Release the resources used by this variant and set it to null.
void clear();
private:
VariantData* data_;
ResourceManager* resources_;
};
template <typename T>
inline void VariantImpl::setRawString(SerializedValue<T> value) {
if (!data_)
@ -536,9 +70,98 @@ inline void VariantImpl::clear() {
resources_->freeEightByte(data_->content.asSlotId);
#endif
CollectionImpl(data_, resources_).clear();
asCollection().clear();
data_->type = VariantType::Null;
}
#if ARDUINOJSON_USE_8_BYTE_POOL
inline const EightByteValue* VariantImpl::getEightByte() const {
return type() & VariantTypeBits::EightByteBit
? resources_->getEightByte(data_->content.asSlotId)
: 0;
}
#endif
inline const char* VariantImpl::asLinkedString() const {
ARDUINOJSON_ASSERT(type() == VariantType::LinkedString);
return resources_->getStaticString(data_->content.asSlotId);
}
template <typename T>
enable_if_t<sizeof(T) == 8, bool> VariantImpl::setFloat(T value) {
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
if (!data_)
return false;
float valueAsFloat = static_cast<float>(value);
#if ARDUINOJSON_USE_DOUBLE
if (value == valueAsFloat) {
data_->type = VariantType::Float;
data_->content.asFloat = valueAsFloat;
} else {
auto slot = resources_->allocEightByte();
if (!slot)
return false;
data_->type = VariantType::Double;
data_->content.asSlotId = slot.id();
slot->asDouble = value;
}
#else
data_->type = VariantType::Float;
data_->content.asFloat = valueAsFloat;
#endif
return true;
}
template <typename T>
enable_if_t<is_signed<T>::value, bool> VariantImpl::setInteger(T value) {
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
if (!data_)
return false;
if (canConvertNumber<int32_t>(value)) {
data_->type = VariantType::Int32;
data_->content.asInt32 = static_cast<int32_t>(value);
}
#if ARDUINOJSON_USE_LONG_LONG
else {
auto slot = resources_->allocEightByte();
if (!slot)
return false;
data_->type = VariantType::Int64;
data_->content.asSlotId = slot.id();
slot->asInt64 = value;
}
#endif
return true;
}
template <typename T>
enable_if_t<is_unsigned<T>::value, bool> VariantImpl::setInteger(T value) {
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
if (!data_)
return false;
if (canConvertNumber<uint32_t>(value)) {
data_->type = VariantType::Uint32;
data_->content.asUint32 = static_cast<uint32_t>(value);
}
#if ARDUINOJSON_USE_LONG_LONG
else {
auto slot = resources_->allocEightByte();
if (!slot)
return false;
data_->type = VariantType::Uint64;
data_->content.asSlotId = slot.id();
slot->asUint64 = value;
}
#endif
return true;
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -69,20 +69,22 @@ inline void convertToJson(const VariantRefBase<TDerived>& src,
template <typename TDerived>
template <typename T, enable_if_t<is_same<T, JsonVariant>::value, int>>
inline T VariantRefBase<TDerived>::add() const {
return JsonVariant(getOrCreateVariantImpl().addElement(),
getResourceManager());
detail::VariantImpl variant(getOrCreateData(), getResourceManager());
return JsonVariant(variant.addElement(), getResourceManager());
}
template <typename TDerived>
template <typename TString, enable_if_t<IsString<TString>::value, int>>
inline bool VariantRefBase<TDerived>::containsKey(const TString& key) const {
return getVariantImpl().getMember(adaptString(key)) != 0;
detail::VariantImpl variant(getData(), getResourceManager());
return variant.getMember(adaptString(key)) != 0;
}
template <typename TDerived>
template <typename TChar, enable_if_t<IsString<TChar*>::value, int>>
inline bool VariantRefBase<TDerived>::containsKey(TChar* key) const {
return getVariantImpl().getMember(adaptString(key)) != 0;
detail::VariantImpl variant(getData(), getResourceManager());
return variant.getMember(adaptString(key)) != 0;
}
template <typename TDerived>
@ -147,23 +149,17 @@ inline bool VariantRefBase<TDerived>::doSet(const T& value, true_type) const {
template <typename TDerived>
template <typename T, enable_if_t<is_same<T, JsonArray>::value, int>>
inline JsonArray VariantRefBase<TDerived>::to() const {
auto data = getOrCreateData();
if (!data)
return JsonArray();
auto resources = getResourceManager();
VariantImpl(data, resources).clear();
return JsonArray(data->toArray(), resources);
auto variant = getOrCreateVariantImpl();
variant.clear();
return JsonArray(variant.toArray());
}
template <typename TDerived>
template <typename T, enable_if_t<is_same<T, JsonObject>::value, int>>
JsonObject VariantRefBase<TDerived>::to() const {
auto data = getOrCreateData();
if (!data)
return JsonObject();
auto resources = getResourceManager();
VariantImpl(data, resources).clear();
return JsonObject(data->toObject(), resources);
auto variant = getOrCreateVariantImpl();
variant.clear();
return JsonObject(variant.toObject());
}
template <typename TDerived>