Extract VariantImpl

This commit is contained in:
Benoit Blanchon
2025-06-26 14:59:10 +02:00
parent 48dec78781
commit 31a729d35c
35 changed files with 506 additions and 608 deletions

View File

@ -21,8 +21,8 @@ TEST_CASE("StringBuffer") {
strcpy(ptr, "hi!"); strcpy(ptr, "hi!");
sb.save(&variant); sb.save(&variant);
REQUIRE(variant.type() == VariantType::TinyString); REQUIRE(variant.type == VariantType::TinyString);
REQUIRE(variant.asString(&resources) == "hi!"); REQUIRE(VariantImpl(&variant, &resources).asString() == "hi!");
} }
SECTION("Tiny string can't contain NUL") { SECTION("Tiny string can't contain NUL") {
@ -30,9 +30,9 @@ TEST_CASE("StringBuffer") {
memcpy(ptr, "a\0b", 3); memcpy(ptr, "a\0b", 3);
sb.save(&variant); sb.save(&variant);
REQUIRE(variant.type() == VariantType::OwnedString); REQUIRE(variant.type == VariantType::OwnedString);
auto str = variant.asString(&resources); auto str = VariantImpl(&variant, &resources).asString();
REQUIRE(str.size() == 3); REQUIRE(str.size() == 3);
REQUIRE(str.c_str()[0] == 'a'); REQUIRE(str.c_str()[0] == 'a');
REQUIRE(str.c_str()[1] == 0); REQUIRE(str.c_str()[1] == 0);
@ -44,7 +44,7 @@ TEST_CASE("StringBuffer") {
strcpy(ptr, "alfa"); strcpy(ptr, "alfa");
sb.save(&variant); sb.save(&variant);
REQUIRE(variant.type() == VariantType::OwnedString); REQUIRE(variant.type == VariantType::OwnedString);
REQUIRE(variant.asString(&resources) == "alfa"); REQUIRE(VariantImpl(&variant, &resources).asString() == "alfa");
} }
} }

View File

@ -26,7 +26,7 @@ TEST_CASE("StringBuilder") {
REQUIRE(spyingAllocator.log() == AllocatorLog{ REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofStringBuffer()), Allocate(sizeofStringBuffer()),
}); });
REQUIRE(data.type() == VariantType::TinyString); REQUIRE(data.type == VariantType::TinyString);
} }
SECTION("Tiny string") { SECTION("Tiny string") {
@ -45,8 +45,8 @@ TEST_CASE("StringBuilder") {
str.save(&data); str.save(&data);
REQUIRE(resources.overflowed() == false); REQUIRE(resources.overflowed() == false);
REQUIRE(data.type() == VariantType::TinyString); REQUIRE(data.type == VariantType::TinyString);
REQUIRE(data.asString(&resources) == "url"); REQUIRE(VariantImpl(&data, &resources).asString() == "url");
} }
SECTION("Short string fits in first allocation") { SECTION("Short string fits in first allocation") {
@ -134,10 +134,10 @@ TEST_CASE("StringBuilder::save() deduplicates strings") {
auto s2 = saveString(builder, "world"); auto s2 = saveString(builder, "world");
auto s3 = saveString(builder, "hello"); auto s3 = saveString(builder, "hello");
REQUIRE(s1.asString(&resources) == "hello"); REQUIRE(VariantImpl(&s1, &resources).asString() == "hello");
REQUIRE(s2.asString(&resources) == "world"); REQUIRE(VariantImpl(&s2, &resources).asString() == "world");
REQUIRE(+s1.asString(&resources).c_str() == REQUIRE(+VariantImpl(&s1, &resources).asString().c_str() ==
+s3.asString(&resources).c_str()); // same address +VariantImpl(&s3, &resources).asString().c_str()); // same address
REQUIRE(spy.log() == REQUIRE(spy.log() ==
AllocatorLog{ AllocatorLog{
@ -153,10 +153,11 @@ TEST_CASE("StringBuilder::save() deduplicates strings") {
auto s1 = saveString(builder, "hello world"); auto s1 = saveString(builder, "hello world");
auto s2 = saveString(builder, "hello"); auto s2 = saveString(builder, "hello");
REQUIRE(s1.asString(&resources) == "hello world"); REQUIRE(VariantImpl(&s1, &resources).asString() == "hello world");
REQUIRE(s2.asString(&resources) == "hello"); REQUIRE(VariantImpl(&s2, &resources).asString() == "hello");
REQUIRE(+s2.asString(&resources).c_str() != REQUIRE(
+s1.asString(&resources).c_str()); // different address +VariantImpl(&s1, &resources).asString().c_str() !=
+VariantImpl(&s2, &resources).asString().c_str()); // different address
REQUIRE(spy.log() == REQUIRE(spy.log() ==
AllocatorLog{ AllocatorLog{
@ -171,10 +172,11 @@ TEST_CASE("StringBuilder::save() deduplicates strings") {
auto s1 = saveString(builder, "hello world"); auto s1 = saveString(builder, "hello world");
auto s2 = saveString(builder, "worl"); auto s2 = saveString(builder, "worl");
REQUIRE(s1.asString(&resources) == "hello world"); REQUIRE(VariantImpl(&s1, &resources).asString() == "hello world");
REQUIRE(s2.asString(&resources) == "worl"); REQUIRE(VariantImpl(&s2, &resources).asString() == "worl");
REQUIRE(s2.asString(&resources).c_str() != REQUIRE(
s1.asString(&resources).c_str()); // different address VariantImpl(&s1, &resources).asString().c_str() !=
VariantImpl(&s2, &resources).asString().c_str()); // different address
REQUIRE(spy.log() == REQUIRE(spy.log() ==
AllocatorLog{ AllocatorLog{

View File

@ -55,17 +55,12 @@ class ElementProxy : public VariantRefBase<ElementProxy<TUpstream>>,
} }
FORCE_INLINE VariantData* getData() const { FORCE_INLINE VariantData* getData() const {
auto data = VariantAttorney::getData(upstream_); return VariantAttorney::getVariantImpl(upstream_).getElement(index_);
auto resources = VariantAttorney::getResourceManager(upstream_);
return VariantData::asArray(data, resources).getElement(index_);
} }
VariantData* getOrCreateData() const { VariantData* getOrCreateData() const {
auto data = VariantAttorney::getOrCreateData(upstream_); return VariantAttorney::getOrCreateVariantImpl(upstream_).getOrAddElement(
if (!data) index_);
return nullptr;
return data->getOrAddElement(
index_, VariantAttorney::getResourceManager(upstream_));
} }
TUpstream upstream_; TUpstream upstream_;

View File

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

View File

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

View File

@ -12,7 +12,7 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantData; struct VariantData;
class ResourceManager; class ResourceManager;
class CollectionIterator { class CollectionIterator {

View File

@ -14,7 +14,7 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline CollectionIterator::CollectionIterator(VariantData* slot, SlotId slotId) inline CollectionIterator::CollectionIterator(VariantData* slot, SlotId slotId)
: slot_(slot), currentId_(slotId) { : slot_(slot), currentId_(slotId) {
nextId_ = slot_ ? slot_->next() : NULL_SLOT; nextId_ = slot_ ? slot_->next : NULL_SLOT;
} }
inline void CollectionIterator::next(const ResourceManager* resources) { inline void CollectionIterator::next(const ResourceManager* resources) {
@ -22,7 +22,7 @@ inline void CollectionIterator::next(const ResourceManager* resources) {
slot_ = resources->getVariant(nextId_); slot_ = resources->getVariant(nextId_);
currentId_ = nextId_; currentId_ = nextId_;
if (slot_) if (slot_)
nextId_ = slot_->next(); nextId_ = slot_->next;
} }
inline CollectionImpl::iterator CollectionImpl::createIterator() const { inline CollectionImpl::iterator CollectionImpl::createIterator() const {
@ -37,7 +37,7 @@ inline void CollectionImpl::appendOne(Slot<VariantData> slot) {
if (data_->tail != NULL_SLOT) { if (data_->tail != NULL_SLOT) {
auto tail = resources_->getVariant(data_->tail); auto tail = resources_->getVariant(data_->tail);
tail->setNext(slot.id()); tail->next = slot.id();
data_->tail = slot.id(); data_->tail = slot.id();
} else { } else {
data_->head = slot.id(); data_->head = slot.id();
@ -50,11 +50,11 @@ inline void CollectionImpl::appendPair(Slot<VariantData> key,
ARDUINOJSON_ASSERT(data_ != nullptr); ARDUINOJSON_ASSERT(data_ != nullptr);
ARDUINOJSON_ASSERT(resources_ != nullptr); ARDUINOJSON_ASSERT(resources_ != nullptr);
key->setNext(value.id()); key->next = value.id();
if (data_->tail != NULL_SLOT) { if (data_->tail != NULL_SLOT) {
auto tail = resources_->getVariant(data_->tail); auto tail = resources_->getVariant(data_->tail);
tail->setNext(key.id()); tail->next = key.id();
data_->tail = value.id(); data_->tail = value.id();
} else { } else {
data_->head = key.id(); data_->head = key.id();
@ -69,7 +69,7 @@ inline void CollectionImpl::clear() {
while (next != NULL_SLOT) { while (next != NULL_SLOT) {
auto currId = next; auto currId = next;
auto slot = resources_->getVariant(next); auto slot = resources_->getVariant(next);
next = slot->next(); next = slot->next;
resources_->freeVariant({slot, currId}); resources_->freeVariant({slot, currId});
} }
@ -86,7 +86,7 @@ inline Slot<VariantData> CollectionImpl::getPreviousSlot(
if (currentSlot == target) if (currentSlot == target)
break; break;
prev = Slot<VariantData>(currentSlot, currentId); prev = Slot<VariantData>(currentSlot, currentId);
currentId = currentSlot->next(); currentId = currentSlot->next;
} }
return prev; return prev;
} }
@ -96,9 +96,9 @@ inline void CollectionImpl::removeOne(iterator it) {
return; return;
auto curr = it.slot_; auto curr = it.slot_;
auto prev = getPreviousSlot(curr); auto prev = getPreviousSlot(curr);
auto next = curr->next(); auto next = curr->next;
if (prev) if (prev)
prev->setNext(next); prev->next = next;
else else
data_->head = next; data_->head = next;
if (next == NULL_SLOT) if (next == NULL_SLOT)
@ -116,7 +116,7 @@ inline void CollectionImpl::removePair(ObjectImpl::iterator it) {
auto valueSlot = resources_->getVariant(valueId); auto valueSlot = resources_->getVariant(valueId);
// remove value slot // remove value slot
keySlot->setNext(valueSlot->next()); keySlot->next = valueSlot->next;
resources_->freeVariant({valueSlot, valueId}); resources_->freeVariant({valueSlot, valueId});
// remove key slot // remove key slot
@ -128,7 +128,8 @@ inline size_t CollectionImpl::nesting() const {
return 0; return 0;
size_t maxChildNesting = 0; size_t maxChildNesting = 0;
for (auto it = createIterator(); !it.done(); it.next(resources_)) { for (auto it = createIterator(); !it.done(); it.next(resources_)) {
size_t childNesting = it->nesting(resources_); VariantImpl variant(it.data(), resources_);
size_t childNesting = variant.nesting();
if (childNesting > maxChildNesting) if (childNesting > maxChildNesting)
maxChildNesting = childNesting; maxChildNesting = childNesting;
} }

View File

@ -50,7 +50,7 @@ DeserializationError doDeserialize(TDestination&& dst, TReader reader,
auto resources = VariantAttorney::getResourceManager(dst); auto resources = VariantAttorney::getResourceManager(dst);
dst.clear(); dst.clear();
auto err = TDeserializer<TReader>(resources, reader) auto err = TDeserializer<TReader>(resources, reader)
.parse(*data, options.filter, options.nestingLimit); .parse(data, options.filter, options.nestingLimit);
shrinkJsonDocument(dst); shrinkJsonDocument(dst);
return err; return err;
} }

View File

@ -88,7 +88,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// https://arduinojson.org/v7/api/jsondocument/clear/ // https://arduinojson.org/v7/api/jsondocument/clear/
void clear() { void clear() {
resources_.clear(); resources_.clear();
data_.reset(); data_.type = detail::VariantType::Null;
} }
// Returns true if the root is of the specified type. // Returns true if the root is of the specified type.
@ -120,13 +120,13 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Returns the depth (nesting level) of the array. // Returns the depth (nesting level) of the array.
// https://arduinojson.org/v7/api/jsondocument/nesting/ // https://arduinojson.org/v7/api/jsondocument/nesting/
size_t nesting() const { size_t nesting() const {
return data_.nesting(&resources_); return getVariantImpl().nesting();
} }
// Returns the number of elements in the root array or object. // Returns the number of elements in the root array or object.
// https://arduinojson.org/v7/api/jsondocument/size/ // https://arduinojson.org/v7/api/jsondocument/size/
size_t size() const { size_t size() const {
return data_.size(&resources_); return getVariantImpl().size();
} }
// Copies the specified document. // Copies the specified document.
@ -165,7 +165,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
template <typename TChar> template <typename TChar>
ARDUINOJSON_DEPRECATED("use doc[\"key\"].is<T>() instead") ARDUINOJSON_DEPRECATED("use doc[\"key\"].is<T>() instead")
bool containsKey(TChar* key) const { bool containsKey(TChar* key) const {
return data_.getMember(detail::adaptString(key), &resources_) != 0; return getVariantImpl().getMember(detail::adaptString(key)) != 0;
} }
// DEPRECATED: use obj[key].is<T>() instead // DEPRECATED: use obj[key].is<T>() instead
@ -174,7 +174,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
detail::enable_if_t<detail::IsString<TString>::value, int> = 0> detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use doc[key].is<T>() instead") ARDUINOJSON_DEPRECATED("use doc[key].is<T>() instead")
bool containsKey(const TString& key) const { bool containsKey(const TString& key) const {
return data_.getMember(detail::adaptString(key), &resources_) != 0; return getVariantImpl().getMember(detail::adaptString(key)) != 0;
} }
// DEPRECATED: use obj[key].is<T>() instead // DEPRECATED: use obj[key].is<T>() instead
@ -212,7 +212,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
detail::enable_if_t<detail::IsString<TString>::value, int> = 0> detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
JsonVariantConst operator[](const TString& key) const { JsonVariantConst operator[](const TString& key) const {
return JsonVariantConst( return JsonVariantConst(
data_.getMember(detail::adaptString(key), &resources_), &resources_); getVariantImpl().getMember(detail::adaptString(key)), &resources_);
} }
// Gets a root object's member. // Gets a root object's member.
@ -223,7 +223,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
int> = 0> int> = 0>
JsonVariantConst operator[](TChar* key) const { JsonVariantConst operator[](TChar* key) const {
return JsonVariantConst( return JsonVariantConst(
data_.getMember(detail::adaptString(key), &resources_), &resources_); getVariantImpl().getMember(detail::adaptString(key)), &resources_);
} }
// Gets or sets a root array's element. // Gets or sets a root array's element.
@ -237,7 +237,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Gets a root array's member. // Gets a root array's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/ // https://arduinojson.org/v7/api/jsondocument/subscript/
JsonVariantConst operator[](size_t index) const { JsonVariantConst operator[](size_t index) const {
return JsonVariantConst(data_.getElement(index, &resources_), &resources_); return JsonVariantConst(getVariantImpl().getElement(index), &resources_);
} }
// Gets or sets a root object's member. // Gets or sets a root object's member.
@ -267,14 +267,14 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
template <typename T, detail::enable_if_t< template <typename T, detail::enable_if_t<
detail::is_same<T, JsonVariant>::value, int> = 0> detail::is_same<T, JsonVariant>::value, int> = 0>
JsonVariant add() { JsonVariant add() {
return JsonVariant(data_.addElement(&resources_), &resources_); return JsonVariant(getVariantImpl().addElement(), &resources_);
} }
// Appends a value to the root array. // Appends a value to the root array.
// https://arduinojson.org/v7/api/jsondocument/add/ // https://arduinojson.org/v7/api/jsondocument/add/
template <typename TValue> template <typename TValue>
bool add(const TValue& value) { bool add(const TValue& value) {
return data_.addValue(value, &resources_); return getVariantImpl().addValue(value);
} }
// Appends a value to the root array. // Appends a value to the root array.
@ -282,7 +282,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
template <typename TChar, template <typename TChar,
detail::enable_if_t<!detail::is_const<TChar>::value, int> = 0> detail::enable_if_t<!detail::is_const<TChar>::value, int> = 0>
bool add(TChar* value) { bool add(TChar* value) {
return data_.addValue(value, &resources_); return getVariantImpl().addValue(value);
} }
// Removes an element of the root array. // Removes an element of the root array.
@ -290,8 +290,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
template <typename T, template <typename T,
detail::enable_if_t<detail::is_integral<T>::value, int> = 0> detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
void remove(T index) { void remove(T index) {
detail::VariantData::removeElement(getData(), size_t(index), getVariantImpl().removeElement(size_t(index));
getResourceManager());
} }
// Removes a member of the root object. // Removes a member of the root object.
@ -301,8 +300,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
!detail::is_const<TChar>::value, !detail::is_const<TChar>::value,
int> = 0> int> = 0>
void remove(TChar* key) { void remove(TChar* key) {
detail::VariantData::removeMember(getData(), detail::adaptString(key), getVariantImpl().removeMember(detail::adaptString(key));
getResourceManager());
} }
// Removes a member of the root object. // Removes a member of the root object.
@ -310,8 +308,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
template <typename TString, template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0> detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
void remove(const TString& key) { void remove(const TString& key) {
detail::VariantData::removeMember(getData(), detail::adaptString(key), getVariantImpl().removeMember(detail::adaptString(key));
getResourceManager());
} }
// Removes a member of the root object or an element of the root array. // Removes a member of the root object or an element of the root array.
@ -391,6 +388,10 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
} }
private: private:
detail::VariantImpl getVariantImpl() const {
return detail::VariantImpl(&data_, &resources_);
}
JsonVariant getVariant() { JsonVariant getVariant() {
return JsonVariant(&data_, &resources_); return JsonVariant(&data_, &resources_);
} }

View File

@ -28,13 +28,13 @@ class JsonDeserializer {
resources_(resources) {} resources_(resources) {}
template <typename TFilter> template <typename TFilter>
DeserializationError parse(VariantData& variant, TFilter filter, DeserializationError parse(VariantData* variant, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) { DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err; DeserializationError::Code err;
err = parseVariant(variant, filter, nestingLimit); err = parseVariant(variant, filter, nestingLimit);
if (!err && latch_.last() != 0 && variant.isFloat()) { if (!err && latch_.last() != 0 && variant->isFloat()) {
// We don't detect trailing characters earlier, so we need to check now // We don't detect trailing characters earlier, so we need to check now
return DeserializationError::InvalidInput; return DeserializationError::InvalidInput;
} }
@ -60,7 +60,7 @@ class JsonDeserializer {
template <typename TFilter> template <typename TFilter>
DeserializationError::Code parseVariant( DeserializationError::Code parseVariant(
VariantData& variant, TFilter filter, VariantData* variant, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) { DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err; DeserializationError::Code err;
@ -71,14 +71,15 @@ class JsonDeserializer {
switch (current()) { switch (current()) {
case '[': case '[':
if (filter.allowArray()) if (filter.allowArray())
return parseArray(variant.toArray(resources_), filter, nestingLimit); return parseArray(VariantImpl(variant, resources_).toArray(), filter,
nestingLimit);
else else
return skipArray(nestingLimit); return skipArray(nestingLimit);
case '{': case '{':
if (filter.allowObject()) if (filter.allowObject())
return parseObject(variant.toObject(resources_), filter, return parseObject(VariantImpl(variant, resources_).toObject(),
nestingLimit); filter, nestingLimit);
else else
return skipObject(nestingLimit); return skipObject(nestingLimit);
@ -91,12 +92,12 @@ class JsonDeserializer {
case 't': case 't':
if (filter.allowValue()) if (filter.allowValue())
variant.setBoolean(true); variant->setBoolean(true);
return skipKeyword("true"); return skipKeyword("true");
case 'f': case 'f':
if (filter.allowValue()) if (filter.allowValue())
variant.setBoolean(false); variant->setBoolean(false);
return skipKeyword("false"); return skipKeyword("false");
case 'n': case 'n':
@ -178,7 +179,7 @@ class JsonDeserializer {
return DeserializationError::NoMemory; return DeserializationError::NoMemory;
// 1 - Parse value // 1 - Parse value
err = parseVariant(*value, elementFilter, nestingLimit.decrement()); err = parseVariant(value, elementFilter, nestingLimit.decrement());
if (err) if (err)
return err; return err;
} else { } else {
@ -282,11 +283,11 @@ class JsonDeserializer {
stringBuilder_.save(keyVariant); stringBuilder_.save(keyVariant);
} else { } else {
member->clear(resources_); VariantImpl(member, resources_).clear();
} }
// Parse value // Parse value
err = parseVariant(*member, memberFilter, nestingLimit.decrement()); err = parseVariant(member, memberFilter, nestingLimit.decrement());
if (err) if (err)
return err; return err;
} else { } else {
@ -380,7 +381,7 @@ class JsonDeserializer {
} }
} }
DeserializationError::Code parseStringValue(VariantData& variant) { DeserializationError::Code parseStringValue(VariantData* variant) {
DeserializationError::Code err; DeserializationError::Code err;
stringBuilder_.startString(); stringBuilder_.startString();
@ -389,7 +390,7 @@ class JsonDeserializer {
if (err) if (err)
return err; return err;
stringBuilder_.save(&variant); stringBuilder_.save(variant);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
@ -505,7 +506,7 @@ class JsonDeserializer {
return DeserializationError::Ok; return DeserializationError::Ok;
} }
DeserializationError::Code parseNumericValue(VariantData& result) { DeserializationError::Code parseNumericValue(VariantData* result) {
uint8_t n = 0; uint8_t n = 0;
char c = current(); char c = current();
@ -519,26 +520,28 @@ class JsonDeserializer {
auto number = parseNumber(buffer_); auto number = parseNumber(buffer_);
switch (number.type()) { switch (number.type()) {
case NumberType::UnsignedInteger: case NumberType::UnsignedInteger:
if (result.setInteger(number.asUnsignedInteger(), resources_)) if (VariantImpl(result, resources_)
.setInteger(number.asUnsignedInteger()))
return DeserializationError::Ok; return DeserializationError::Ok;
else else
return DeserializationError::NoMemory; return DeserializationError::NoMemory;
case NumberType::SignedInteger: case NumberType::SignedInteger:
if (result.setInteger(number.asSignedInteger(), resources_)) if (VariantImpl(result, resources_)
.setInteger(number.asSignedInteger()))
return DeserializationError::Ok; return DeserializationError::Ok;
else else
return DeserializationError::NoMemory; return DeserializationError::NoMemory;
case NumberType::Float: case NumberType::Float:
if (result.setFloat(number.asFloat(), resources_)) if (VariantImpl(result, resources_).setFloat(number.asFloat()))
return DeserializationError::Ok; return DeserializationError::Ok;
else else
return DeserializationError::NoMemory; return DeserializationError::NoMemory;
#if ARDUINOJSON_USE_DOUBLE #if ARDUINOJSON_USE_DOUBLE
case NumberType::Double: case NumberType::Double:
if (result.setFloat(number.asDouble(), resources_)) if (VariantImpl(result, resources_).setFloat(number.asDouble()))
return DeserializationError::Ok; return DeserializationError::Ok;
else else
return DeserializationError::NoMemory; return DeserializationError::NoMemory;

View File

@ -27,9 +27,9 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
while (slotId != NULL_SLOT) { while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId); auto slot = resources_->getVariant(slotId);
slot->accept(*this, resources_); VariantImpl(slot, resources_).accept(*this);
slotId = slot->next(); slotId = slot->next;
if (slotId != NULL_SLOT) if (slotId != NULL_SLOT)
write(','); write(',');
@ -48,9 +48,9 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
while (slotId != NULL_SLOT) { while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId); auto slot = resources_->getVariant(slotId);
slot->accept(*this, resources_); VariantImpl(slot, resources_).accept(*this);
slotId = slot->next(); slotId = slot->next;
if (slotId != NULL_SLOT) if (slotId != NULL_SLOT)
write(isKey ? ':' : ','); write(isKey ? ':' : ',');

View File

@ -26,7 +26,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
nesting_++; nesting_++;
while (!it.done()) { while (!it.done()) {
indent(); indent();
it->accept(*this, base::resources_); VariantImpl(it.data(), base::resources_).accept(*this);
it.next(base::resources_); it.next(base::resources_);
base::write(it.done() ? "\r\n" : ",\r\n"); base::write(it.done() ? "\r\n" : ",\r\n");
@ -49,7 +49,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
while (!it.done()) { while (!it.done()) {
if (isKey) if (isKey)
indent(); indent();
it->accept(*this, base::resources_); VariantImpl(it.data(), base::resources_).accept(*this);
it.next(base::resources_); it.next(base::resources_);
if (isKey) if (isKey)
base::write(": "); base::write(": ");

View File

@ -14,7 +14,7 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantData; struct VariantData;
class VariantWithId; class VariantWithId;
class ResourceManager { class ResourceManager {

View File

@ -22,7 +22,7 @@ inline Slot<VariantData> ResourceManager::allocVariant() {
} }
inline void ResourceManager::freeVariant(Slot<VariantData> slot) { inline void ResourceManager::freeVariant(Slot<VariantData> slot) {
slot->clear(this); VariantImpl(slot.ptr(), this).clear();
variantPools_.freeSlot(slot); variantPools_.freeSlot(slot);
} }

View File

@ -29,7 +29,7 @@ struct Converter<MsgPackBinary> : private detail::VariantAttorney {
if (!data) if (!data)
return; return;
auto resources = getResourceManager(dst); auto resources = getResourceManager(dst);
data->clear(resources); detail::VariantImpl(data, resources).clear();
if (src.data()) { if (src.data()) {
size_t headerSize = src.size() >= 0x10000 ? 5 size_t headerSize = src.size() >= 0x10000 ? 5
: src.size() >= 0x100 ? 3 : src.size() >= 0x100 ? 3
@ -66,10 +66,8 @@ struct Converter<MsgPackBinary> : private detail::VariantAttorney {
} }
static MsgPackBinary fromJson(JsonVariantConst src) { static MsgPackBinary fromJson(JsonVariantConst src) {
auto data = getData(src); auto variant = VariantAttorney::getVariantImpl(src);
if (!data) auto rawstr = variant.asRawString();
return {};
auto rawstr = data->asRawString();
auto p = reinterpret_cast<const uint8_t*>(rawstr.c_str()); auto p = reinterpret_cast<const uint8_t*>(rawstr.c_str());
auto n = rawstr.size(); auto n = rawstr.size();
if (n >= 2 && p[0] == 0xc4) { // bin 8 if (n >= 2 && p[0] == 0xc4) { // bin 8

View File

@ -24,10 +24,10 @@ class MsgPackDeserializer {
foundSomething_(false) {} foundSomething_(false) {}
template <typename TFilter> template <typename TFilter>
DeserializationError parse(VariantData& variant, TFilter filter, DeserializationError parse(VariantData* variant, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) { DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err; DeserializationError::Code err;
err = parseVariant(&variant, filter, nestingLimit); err = parseVariant(variant, filter, nestingLimit);
return foundSomething_ ? err : DeserializationError::EmptyInput; return foundSomething_ ? err : DeserializationError::EmptyInput;
} }
@ -91,7 +91,7 @@ class MsgPackDeserializer {
if (code <= 0x7f || code >= 0xe0) { // fixint if (code <= 0x7f || code >= 0xe0) { // fixint
if (allowValue) if (allowValue)
variant->setInteger(static_cast<int8_t>(code), resources_); VariantImpl(variant, resources_).setInteger(static_cast<int8_t>(code));
return DeserializationError::Ok; return DeserializationError::Ok;
} }
@ -231,14 +231,14 @@ class MsgPackDeserializer {
if (isSigned) { if (isSigned) {
auto truncatedValue = static_cast<JsonInteger>(signedValue); auto truncatedValue = static_cast<JsonInteger>(signedValue);
if (truncatedValue == signedValue) { if (truncatedValue == signedValue) {
if (!variant->setInteger(truncatedValue, resources_)) if (!VariantImpl(variant, resources_).setInteger(truncatedValue))
return DeserializationError::NoMemory; return DeserializationError::NoMemory;
} }
// else set null on overflow // else set null on overflow
} else { } else {
auto truncatedValue = static_cast<JsonUInt>(unsignedValue); auto truncatedValue = static_cast<JsonUInt>(unsignedValue);
if (truncatedValue == unsignedValue) if (truncatedValue == unsignedValue)
if (!variant->setInteger(truncatedValue, resources_)) if (!VariantImpl(variant, resources_).setInteger(truncatedValue))
return DeserializationError::NoMemory; return DeserializationError::NoMemory;
// else set null on overflow // else set null on overflow
} }
@ -257,7 +257,7 @@ class MsgPackDeserializer {
return err; return err;
fixEndianness(value); fixEndianness(value);
variant->setFloat(value, resources_); VariantImpl(variant, resources_).setFloat(value);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
@ -273,7 +273,7 @@ class MsgPackDeserializer {
return err; return err;
fixEndianness(value); fixEndianness(value);
if (variant->setFloat(value, resources_)) if (VariantImpl(variant, resources_).setFloat(value))
return DeserializationError::Ok; return DeserializationError::Ok;
else else
return DeserializationError::NoMemory; return DeserializationError::NoMemory;
@ -293,7 +293,7 @@ class MsgPackDeserializer {
doubleToFloat(i, o); doubleToFloat(i, o);
fixEndianness(value); fixEndianness(value);
variant->setFloat(value, resources_); VariantImpl(variant, resources_).setFloat(value);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
@ -352,7 +352,7 @@ class MsgPackDeserializer {
ArrayImpl array; ArrayImpl array;
if (allowArray) { if (allowArray) {
ARDUINOJSON_ASSERT(variant != 0); ARDUINOJSON_ASSERT(variant != 0);
array = variant->toArray(resources_); array = VariantImpl(variant, resources_).toArray();
} }
TFilter elementFilter = filter[0U]; TFilter elementFilter = filter[0U];
@ -388,7 +388,7 @@ class MsgPackDeserializer {
ObjectImpl object; ObjectImpl object;
if (filter.allowObject()) { if (filter.allowObject()) {
ARDUINOJSON_ASSERT(variant != 0); ARDUINOJSON_ASSERT(variant != 0);
object = variant->toObject(resources_); object = VariantImpl(variant, resources_).toObject();
} }
for (; n; --n) { for (; n; --n) {

View File

@ -31,11 +31,11 @@ class MsgPackExtension {
template <> template <>
struct Converter<MsgPackExtension> : private detail::VariantAttorney { struct Converter<MsgPackExtension> : private detail::VariantAttorney {
static void toJson(MsgPackExtension src, JsonVariant dst) { static void toJson(MsgPackExtension src, JsonVariant dst) {
auto data = VariantAttorney::getData(dst); auto data = getData(dst);
if (!data) if (!data)
return; return;
auto resources = getResourceManager(dst); auto resources = getResourceManager(dst);
data->clear(resources); detail::VariantImpl(data, resources).clear();
if (src.data()) { if (src.data()) {
uint8_t format, sizeBytes; uint8_t format, sizeBytes;
if (src.size() >= 0x10000) { if (src.size() >= 0x10000) {
@ -80,10 +80,8 @@ struct Converter<MsgPackExtension> : private detail::VariantAttorney {
} }
static MsgPackExtension fromJson(JsonVariantConst src) { static MsgPackExtension fromJson(JsonVariantConst src) {
auto data = getData(src); auto variant = VariantAttorney::getVariantImpl(src);
if (!data) auto rawstr = variant.asRawString();
return {};
auto rawstr = data->asRawString();
if (rawstr.size() == 0) if (rawstr.size() == 0)
return {}; return {};
auto p = reinterpret_cast<const uint8_t*>(rawstr.c_str()); auto p = reinterpret_cast<const uint8_t*>(rawstr.c_str());

View File

@ -62,8 +62,8 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
auto slotId = array.head(); auto slotId = array.head();
while (slotId != NULL_SLOT) { while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId); auto slot = resources_->getVariant(slotId);
slot->accept(*this, resources_); VariantImpl(slot, resources_).accept(*this);
slotId = slot->next(); slotId = slot->next;
} }
return bytesWritten(); return bytesWritten();
@ -84,8 +84,8 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
auto slotId = object.head(); auto slotId = object.head();
while (slotId != NULL_SLOT) { while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId); auto slot = resources_->getVariant(slotId);
slot->accept(*this, resources_); VariantImpl(slot, resources_).accept(*this);
slotId = slot->next(); slotId = slot->next;
} }
return bytesWritten(); return bytesWritten();

View File

@ -27,7 +27,7 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
// INTERNAL USE ONLY // INTERNAL USE ONLY
JsonObject(detail::VariantData* data, detail::ResourceManager* resource) JsonObject(detail::VariantData* data, detail::ResourceManager* resource)
: impl_(detail::VariantData::asObject(data, resource)) {} : impl_(detail::VariantImpl(data, resource).asObject()) {}
operator JsonVariant() const { operator JsonVariant() const {
return JsonVariant(getData(), getResourceManager()); return JsonVariant(getData(), getResourceManager());

View File

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

View File

@ -18,7 +18,8 @@ class JsonPair {
JsonPair(detail::ObjectImpl::iterator iterator, JsonPair(detail::ObjectImpl::iterator iterator,
detail::ResourceManager* resources) { detail::ResourceManager* resources) {
if (!iterator.done()) { if (!iterator.done()) {
key_ = iterator->asString(resources); detail::VariantImpl variant(iterator.data(), resources);
key_ = variant.asString();
iterator.next(resources); iterator.next(resources);
value_ = JsonVariant(iterator.data(), resources); value_ = JsonVariant(iterator.data(), resources);
} }
@ -46,7 +47,8 @@ class JsonPairConst {
JsonPairConst(detail::ObjectImpl::iterator iterator, JsonPairConst(detail::ObjectImpl::iterator iterator,
detail::ResourceManager* resources) { detail::ResourceManager* resources) {
if (!iterator.done()) { if (!iterator.done()) {
key_ = iterator->asString(resources); detail::VariantImpl variant(iterator.data(), resources);
key_ = variant.asString();
iterator.next(resources); iterator.next(resources);
value_ = JsonVariantConst(iterator.data(), resources); value_ = JsonVariantConst(iterator.data(), resources);
} }

View File

@ -56,17 +56,12 @@ class MemberProxy
} }
VariantData* getData() const { VariantData* getData() const {
return VariantData::getMember( return VariantAttorney::getVariantImpl(upstream_).getMember(key_);
VariantAttorney::getData(upstream_), key_,
VariantAttorney::getResourceManager(upstream_));
} }
VariantData* getOrCreateData() const { VariantData* getOrCreateData() const {
auto data = VariantAttorney::getOrCreateData(upstream_); return VariantAttorney::getOrCreateVariantImpl(upstream_).getOrAddMember(
if (!data) key_);
return nullptr;
return data->getOrAddMember(key_,
VariantAttorney::getResourceManager(upstream_));
} }
private: private:

View File

@ -8,6 +8,8 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantImpl;
class ObjectImpl : public CollectionImpl { class ObjectImpl : public CollectionImpl {
public: public:
ObjectImpl() {} ObjectImpl() {}

View File

@ -33,7 +33,8 @@ inline ObjectImpl::iterator ObjectImpl::findKey(TAdaptedString key) const {
return iterator(); return iterator();
bool isKey = true; bool isKey = true;
for (auto it = createIterator(); !it.done(); it.next(resources_)) { for (auto it = createIterator(); !it.done(); it.next(resources_)) {
if (isKey && stringEquals(key, adaptString(it->asString(resources_)))) VariantImpl variant(it.data(), resources_);
if (isKey && stringEquals(key, adaptString(variant.asString())))
return it; return it;
isKey = !isKey; isKey = !isKey;
} }
@ -59,7 +60,8 @@ inline VariantData* ObjectImpl::addMember(TAdaptedString key) {
if (!valueSlot) if (!valueSlot)
return nullptr; return nullptr;
if (!keySlot->setString(key, resources_)) VariantImpl keyImpl(keySlot.ptr(), resources_);
if (!keyImpl.setString(key))
return nullptr; return nullptr;
CollectionImpl::appendPair(keySlot, valueSlot); CollectionImpl::appendPair(keySlot, valueSlot);

View File

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

View File

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

View File

@ -60,25 +60,18 @@ struct Converter<T, detail::enable_if_t<detail::is_integral<T>::value &&
: private detail::VariantAttorney { : private detail::VariantAttorney {
static bool toJson(T src, JsonVariant dst) { static bool toJson(T src, JsonVariant dst) {
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T); ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
auto data = getData(dst); auto variant = getVariantImpl(dst);
if (!data) variant.clear();
return false; return variant.setInteger(src);
auto resources = getResourceManager(dst);
data->clear(resources);
return data->setInteger(src, resources);
} }
static T fromJson(JsonVariantConst src) { static T fromJson(JsonVariantConst src) {
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T); ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
auto data = getData(src); return getVariantImpl(src).template asIntegral<T>();
auto resources = getResourceManager(src);
return data ? data->template asIntegral<T>(resources) : T();
} }
static bool checkJson(JsonVariantConst src) { static bool checkJson(JsonVariantConst src) {
auto data = getData(src); return getVariantImpl(src).template isInteger<T>();
auto resources = getResourceManager(src);
return data && data->template isInteger<T>(resources);
} }
}; };
@ -90,40 +83,29 @@ struct Converter<T, detail::enable_if_t<detail::is_enum<T>::value>>
} }
static T fromJson(JsonVariantConst src) { static T fromJson(JsonVariantConst src) {
auto data = getData(src); return static_cast<T>(getVariantImpl(src).template asIntegral<int>());
auto resources = getResourceManager(src);
return data ? static_cast<T>(data->template asIntegral<int>(resources))
: T();
} }
static bool checkJson(JsonVariantConst src) { static bool checkJson(JsonVariantConst src) {
auto data = getData(src); return getVariantImpl(src).template isInteger<int>();
auto resources = getResourceManager(src);
return data && data->template isInteger<int>(resources);
} }
}; };
template <> template <>
struct Converter<bool> : private detail::VariantAttorney { struct Converter<bool> : private detail::VariantAttorney {
static bool toJson(bool src, JsonVariant dst) { static bool toJson(bool src, JsonVariant dst) {
auto data = getData(dst); auto variant = getVariantImpl(dst);
if (!data) variant.clear();
return false; return variant.setBoolean(src);
auto resources = getResourceManager(dst);
data->clear(resources);
data->setBoolean(src);
return true;
} }
static bool fromJson(JsonVariantConst src) { static bool fromJson(JsonVariantConst src) {
auto data = getData(src); return getVariantImpl(src).asBoolean();
auto resources = getResourceManager(src);
return data ? data->asBoolean(resources) : false;
} }
static bool checkJson(JsonVariantConst src) { static bool checkJson(JsonVariantConst src) {
auto data = getData(src); auto data = getData(src);
return data && data->isBoolean(); return data && data->type == detail::VariantType::Boolean;
} }
}; };
@ -131,18 +113,13 @@ template <typename T>
struct Converter<T, detail::enable_if_t<detail::is_floating_point<T>::value>> struct Converter<T, detail::enable_if_t<detail::is_floating_point<T>::value>>
: private detail::VariantAttorney { : private detail::VariantAttorney {
static bool toJson(T src, JsonVariant dst) { static bool toJson(T src, JsonVariant dst) {
auto data = getData(dst); auto variant = getVariantImpl(dst);
if (!data) variant.clear();
return false; return variant.setFloat(src);
auto resources = getResourceManager(dst);
data->clear(resources);
return data->setFloat(src, resources);
} }
static T fromJson(JsonVariantConst src) { static T fromJson(JsonVariantConst src) {
auto data = getData(src); return getVariantImpl(src).template asFloat<T>();
auto resources = getResourceManager(src);
return data ? data->template asFloat<T>(resources) : 0;
} }
static bool checkJson(JsonVariantConst src) { static bool checkJson(JsonVariantConst src) {
@ -154,13 +131,13 @@ struct Converter<T, detail::enable_if_t<detail::is_floating_point<T>::value>>
template <> template <>
struct Converter<const char*> : private detail::VariantAttorney { struct Converter<const char*> : private detail::VariantAttorney {
static void toJson(const char* src, JsonVariant dst) { static void toJson(const char* src, JsonVariant dst) {
detail::VariantData::setString(getData(dst), detail::adaptString(src), auto variant = getVariantImpl(dst);
getResourceManager(dst)); variant.clear();
variant.setString(detail::adaptString(src));
} }
static const char* fromJson(JsonVariantConst src) { static const char* fromJson(JsonVariantConst src) {
auto data = getData(src); return getVariantImpl(src).asString().c_str();
return data ? data->asString(getResourceManager(src)).c_str() : 0;
} }
static bool checkJson(JsonVariantConst src) { static bool checkJson(JsonVariantConst src) {
@ -172,13 +149,13 @@ struct Converter<const char*> : private detail::VariantAttorney {
template <> template <>
struct Converter<JsonString> : private detail::VariantAttorney { struct Converter<JsonString> : private detail::VariantAttorney {
static void toJson(JsonString src, JsonVariant dst) { static void toJson(JsonString src, JsonVariant dst) {
detail::VariantData::setString(getData(dst), detail::adaptString(src), auto variant = getVariantImpl(dst);
getResourceManager(dst)); variant.clear();
variant.setString(detail::adaptString(src));
} }
static JsonString fromJson(JsonVariantConst src) { static JsonString fromJson(JsonVariantConst src) {
auto data = getData(src); return getVariantImpl(src).asString();
return data ? data->asString(getResourceManager(src)) : JsonString();
} }
static bool checkJson(JsonVariantConst src) { static bool checkJson(JsonVariantConst src) {
@ -190,10 +167,9 @@ struct Converter<JsonString> : private detail::VariantAttorney {
template <typename T> template <typename T>
inline detail::enable_if_t<detail::IsString<T>::value> convertToJson( inline detail::enable_if_t<detail::IsString<T>::value> convertToJson(
const T& src, JsonVariant dst) { const T& src, JsonVariant dst) {
using namespace detail; auto variant = detail::VariantAttorney::getVariantImpl(dst);
auto data = VariantAttorney::getData(dst); variant.clear();
auto resources = VariantAttorney::getResourceManager(dst); variant.setString(detail::adaptString(src));
detail::VariantData::setString(data, adaptString(src), resources);
} }
// SerializedValue<std::string> // SerializedValue<std::string>
@ -202,22 +178,23 @@ inline detail::enable_if_t<detail::IsString<T>::value> convertToJson(
template <typename T> template <typename T>
struct Converter<SerializedValue<T>> : private detail::VariantAttorney { struct Converter<SerializedValue<T>> : private detail::VariantAttorney {
static void toJson(SerializedValue<T> src, JsonVariant dst) { static void toJson(SerializedValue<T> src, JsonVariant dst) {
detail::VariantData::setRawString(getData(dst), src, auto variant = getVariantImpl(dst);
getResourceManager(dst)); variant.clear();
variant.setRawString(src);
} }
}; };
template <> template <>
struct Converter<detail::nullptr_t> : private detail::VariantAttorney { struct Converter<detail::nullptr_t> : private detail::VariantAttorney {
static void toJson(detail::nullptr_t, JsonVariant dst) { static void toJson(detail::nullptr_t, JsonVariant dst) {
detail::VariantData::clear(getData(dst), getResourceManager(dst)); getVariantImpl(dst).clear();
} }
static detail::nullptr_t fromJson(JsonVariantConst) { static detail::nullptr_t fromJson(JsonVariantConst) {
return nullptr; return nullptr;
} }
static bool checkJson(JsonVariantConst src) { static bool checkJson(JsonVariantConst src) {
auto data = getData(src); auto data = getData(src);
return data == 0 || data->isNull(); return data == 0 || data->type == detail::VariantType::Null;
} }
}; };
@ -263,7 +240,7 @@ inline void convertToJson(const ::Printable& src, JsonVariant dst) {
auto data = detail::VariantAttorney::getData(dst); auto data = detail::VariantAttorney::getData(dst);
if (!resources || !data) if (!resources || !data)
return; return;
data->clear(resources); detail::VariantImpl(data, resources).clear();
detail::StringBuilderPrint print(resources); detail::StringBuilderPrint print(resources);
src.printTo(print); src.printTo(print);
if (print.overflowed()) if (print.overflowed())
@ -334,7 +311,7 @@ struct Converter<JsonArrayConst> : private detail::VariantAttorney {
static bool checkJson(JsonVariantConst src) { static bool checkJson(JsonVariantConst src) {
auto data = getData(src); auto data = getData(src);
return data && data->isArray(); return data && data->type == detail::VariantType::Array;
} }
}; };
@ -353,7 +330,7 @@ struct Converter<JsonArray> : private detail::VariantAttorney {
static bool checkJson(JsonVariant src) { static bool checkJson(JsonVariant src) {
auto data = getData(src); auto data = getData(src);
return data && data->isArray(); return data && data->type == detail::VariantType::Array;
} }
}; };
@ -372,7 +349,7 @@ struct Converter<JsonObjectConst> : private detail::VariantAttorney {
static bool checkJson(JsonVariantConst src) { static bool checkJson(JsonVariantConst src) {
auto data = getData(src); auto data = getData(src);
return data && data->isObject(); return data && data->type == detail::VariantType::Object;
} }
}; };
@ -391,7 +368,7 @@ struct Converter<JsonObject> : private detail::VariantAttorney {
static bool checkJson(JsonVariant src) { static bool checkJson(JsonVariant src) {
auto data = getData(src); auto data = getData(src);
return data && data->isObject(); return data && data->type == detail::VariantType::Object;
} }
}; };

View File

@ -16,27 +16,29 @@ class JsonVariant : public detail::VariantRefBase<JsonVariant>,
public: public:
// Creates an unbound reference. // Creates an unbound reference.
JsonVariant() : data_(0), resources_(0) {} JsonVariant() {}
// INTERNAL USE ONLY // INTERNAL USE ONLY
JsonVariant(detail::VariantData* data, detail::ResourceManager* resources) JsonVariant(detail::VariantData* data, detail::ResourceManager* resources)
: data_(data), resources_(resources) {} : impl_(data, resources) {}
// INTERNAL USE ONLY
JsonVariant(detail::VariantImpl impl) : impl_(impl) {}
private: private:
detail::ResourceManager* getResourceManager() const { detail::ResourceManager* getResourceManager() const {
return resources_; return impl_.getResourceManager();
} }
detail::VariantData* getData() const { detail::VariantData* getData() const {
return data_; return impl_.getData();
} }
detail::VariantData* getOrCreateData() const { detail::VariantData* getOrCreateData() const {
return data_; return impl_.getData();
} }
detail::VariantData* data_; mutable detail::VariantImpl impl_;
detail::ResourceManager* resources_;
}; };
namespace detail { namespace detail {

View File

@ -35,34 +35,34 @@ class JsonVariantConst : public detail::VariantTag,
public: public:
// Creates an unbound reference. // Creates an unbound reference.
JsonVariantConst() : data_(nullptr), resources_(nullptr) {} JsonVariantConst() {}
// INTERNAL USE ONLY // INTERNAL USE ONLY
explicit JsonVariantConst(detail::VariantData* data, explicit JsonVariantConst(detail::VariantData* data,
detail::ResourceManager* resources) detail::ResourceManager* resources)
: data_(data), resources_(resources) {} : impl_(data, resources) {}
// Returns true if the value is null or the reference is unbound. // Returns true if the value is null or the reference is unbound.
// https://arduinojson.org/v7/api/jsonvariantconst/isnull/ // https://arduinojson.org/v7/api/jsonvariantconst/isnull/
bool isNull() const { bool isNull() const {
return detail::VariantData::isNull(data_); return impl_.isNull();
} }
// Returns true if the reference is unbound. // Returns true if the reference is unbound.
bool isUnbound() const { bool isUnbound() const {
return !data_; return impl_.getData() == nullptr;
} }
// Returns the depth (nesting level) of the value. // Returns the depth (nesting level) of the value.
// https://arduinojson.org/v7/api/jsonvariantconst/nesting/ // https://arduinojson.org/v7/api/jsonvariantconst/nesting/
size_t nesting() const { size_t nesting() const {
return detail::VariantData::nesting(data_, resources_); return impl_.nesting();
} }
// Returns the size of the array or object. // Returns the size of the array or object.
// https://arduinojson.org/v7/api/jsonvariantconst/size/ // https://arduinojson.org/v7/api/jsonvariantconst/size/
size_t size() const { size_t size() const {
return detail::VariantData::size(data_, resources_); return impl_.size();
} }
// Casts the value to the specified type. // Casts the value to the specified type.
@ -104,9 +104,8 @@ class JsonVariantConst : public detail::VariantTag,
template <typename T, template <typename T,
detail::enable_if_t<detail::is_integral<T>::value, int> = 0> detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
JsonVariantConst operator[](T index) const { JsonVariantConst operator[](T index) const {
return JsonVariantConst( return JsonVariantConst(impl_.getElement(size_t(index)),
detail::VariantData::getElement(data_, size_t(index), resources_), impl_.getResourceManager());
resources_);
} }
// Gets object's member with specified key. // Gets object's member with specified key.
@ -114,9 +113,8 @@ class JsonVariantConst : public detail::VariantTag,
template <typename TString, template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0> detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
JsonVariantConst operator[](const TString& key) const { JsonVariantConst operator[](const TString& key) const {
return JsonVariantConst(detail::VariantData::getMember( return JsonVariantConst(impl_.getMember(detail::adaptString(key)),
data_, detail::adaptString(key), resources_), impl_.getResourceManager());
resources_);
} }
// Gets object's member with specified key. // Gets object's member with specified key.
@ -126,9 +124,8 @@ class JsonVariantConst : public detail::VariantTag,
!detail::is_const<TChar>::value, !detail::is_const<TChar>::value,
int> = 0> int> = 0>
JsonVariantConst operator[](TChar* key) const { JsonVariantConst operator[](TChar* key) const {
return JsonVariantConst(detail::VariantData::getMember( return JsonVariantConst(impl_.getMember(detail::adaptString(key)),
data_, detail::adaptString(key), resources_), impl_.getResourceManager());
resources_);
} }
// Gets object's member with specified key or the array's element at the // Gets object's member with specified key or the array's element at the
@ -149,8 +146,7 @@ class JsonVariantConst : public detail::VariantTag,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0> detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use var[key].is<T>() instead") ARDUINOJSON_DEPRECATED("use var[key].is<T>() instead")
bool containsKey(const TString& key) const { bool containsKey(const TString& key) const {
return detail::VariantData::getMember(getData(), detail::adaptString(key), return impl_.getMember(detail::adaptString(key)) != 0;
resources_) != 0;
} }
// DEPRECATED: use obj["key"].is<T>() instead // DEPRECATED: use obj["key"].is<T>() instead
@ -161,8 +157,7 @@ class JsonVariantConst : public detail::VariantTag,
int> = 0> int> = 0>
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead") ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
bool containsKey(TChar* key) const { bool containsKey(TChar* key) const {
return detail::VariantData::getMember(getData(), detail::adaptString(key), return impl_.getMember(detail::adaptString(key)) != 0;
resources_) != 0;
} }
// DEPRECATED: use obj[key].is<T>() instead // DEPRECATED: use obj[key].is<T>() instead
@ -182,16 +177,15 @@ class JsonVariantConst : public detail::VariantTag,
protected: protected:
detail::VariantData* getData() const { detail::VariantData* getData() const {
return data_; return impl_.getData();
} }
detail::ResourceManager* getResourceManager() const { detail::ResourceManager* getResourceManager() const {
return resources_; return impl_.getResourceManager();
} }
private: private:
mutable detail::VariantData* data_; mutable detail::VariantImpl impl_;
mutable detail::ResourceManager* resources_;
}; };
ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@ -49,11 +49,9 @@ template <typename TVisitor>
typename TVisitor::result_type accept(JsonVariantConst variant, typename TVisitor::result_type accept(JsonVariantConst variant,
TVisitor& visit) { TVisitor& visit) {
auto data = VariantAttorney::getData(variant); auto data = VariantAttorney::getData(variant);
if (!data)
return visit.visit(nullptr);
auto resources = VariantAttorney::getResourceManager(variant); auto resources = VariantAttorney::getResourceManager(variant);
VisitorAdapter<TVisitor> adapter(visit); VisitorAdapter<TVisitor> adapter(visit);
return data->accept(adapter, resources); return VariantImpl(data, resources).accept(adapter);
} }
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -26,6 +26,16 @@ class VariantAttorney {
return client.getData(); return client.getData();
} }
template <typename TClient>
static VariantImpl getVariantImpl(TClient& client) {
return VariantImpl(client.getData(), client.getResourceManager());
}
template <typename TClient>
static VariantImpl getOrCreateVariantImpl(TClient& client) {
return VariantImpl(client.getOrCreateData(), client.getResourceManager());
}
template <typename TClient> template <typename TClient>
static VariantData* getOrCreateData(TClient& client) { static VariantData* getOrCreateData(TClient& client) {
return client.getOrCreateData(); return client.getOrCreateData();

View File

@ -27,12 +27,11 @@ static bool isTinyString(const T& s, size_t n) {
return !containsNul; return !containsNul;
} }
class VariantData { struct VariantData {
VariantContent content_; // must be first to allow cast from array to variant VariantContent content; // must be first to allow cast from array to variant
VariantType type_; VariantType type = VariantType::Null;
SlotId next_; SlotId next = NULL_SLOT;
public:
// Placement new // Placement new
static void* operator new(size_t, void* p) noexcept { static void* operator new(size_t, void* p) noexcept {
return p; return p;
@ -40,27 +39,79 @@ class VariantData {
static void operator delete(void*, void*) noexcept {} static void operator delete(void*, void*) noexcept {}
VariantData() : type_(VariantType::Null), next_(NULL_SLOT) {} void setBoolean(bool value) {
ARDUINOJSON_ASSERT(type == VariantType::Null);
SlotId next() const { type = VariantType::Boolean;
return next_; content.asBoolean = value;
} }
void setNext(SlotId slot) { template <typename TAdaptedString>
next_ = slot; void setTinyString(const TAdaptedString& s) {
ARDUINOJSON_ASSERT(type == VariantType::Null);
ARDUINOJSON_ASSERT(s.size() <= tinyStringMaxLength);
type = VariantType::TinyString;
auto n = uint8_t(s.size());
for (uint8_t i = 0; i < n; i++) {
char c = s[i];
ARDUINOJSON_ASSERT(c != 0); // no NUL in tiny string
content.asTinyString[i] = c;
}
content.asTinyString[n] = 0;
}
void setOwnedString(StringNode* s) {
ARDUINOJSON_ASSERT(type == VariantType::Null);
ARDUINOJSON_ASSERT(s);
type = VariantType::OwnedString;
content.asOwnedString = s;
}
void setRawString(StringNode* s) {
ARDUINOJSON_ASSERT(type == VariantType::Null);
ARDUINOJSON_ASSERT(s);
type = VariantType::RawString;
content.asOwnedString = s;
}
bool isFloat() const {
return type & VariantTypeBits::NumberBit;
}
bool isString() const {
return type == VariantType::LinkedString ||
type == VariantType::OwnedString || type == VariantType::TinyString;
}
};
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> template <typename TVisitor>
typename TVisitor::result_type accept(TVisitor& visit, typename TVisitor::result_type accept(TVisitor& visit) {
ResourceManager* resources) { if (!data_)
return visit.visit(nullptr);
#if ARDUINOJSON_USE_8_BYTE_POOL #if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte(resources); auto eightByteValue = getEightByte();
#else
(void)resources; // silence warning
#endif #endif
switch (type_) { switch (data_->type) {
case VariantType::Float: case VariantType::Float:
return visit.visit(content_.asFloat); return visit.visit(data_->content.asFloat);
#if ARDUINOJSON_USE_DOUBLE #if ARDUINOJSON_USE_DOUBLE
case VariantType::Double: case VariantType::Double:
@ -68,30 +119,30 @@ class VariantData {
#endif #endif
case VariantType::Array: case VariantType::Array:
return visit.visit(asArray(resources)); return visit.visit(asArray());
case VariantType::Object: case VariantType::Object:
return visit.visit(asObject(resources)); return visit.visit(asObject());
case VariantType::TinyString: case VariantType::TinyString:
return visit.visit(JsonString(content_.asTinyString)); return visit.visit(JsonString(data_->content.asTinyString));
case VariantType::LinkedString: case VariantType::LinkedString:
return visit.visit(JsonString(asLinkedString(resources), true)); return visit.visit(JsonString(asLinkedString(), true));
case VariantType::OwnedString: case VariantType::OwnedString:
return visit.visit(JsonString(content_.asOwnedString->data, return visit.visit(JsonString(data_->content.asOwnedString->data,
content_.asOwnedString->length)); data_->content.asOwnedString->length));
case VariantType::RawString: case VariantType::RawString:
return visit.visit(RawString(content_.asOwnedString->data, return visit.visit(RawString(data_->content.asOwnedString->data,
content_.asOwnedString->length)); data_->content.asOwnedString->length));
case VariantType::Int32: case VariantType::Int32:
return visit.visit(static_cast<JsonInteger>(content_.asInt32)); return visit.visit(static_cast<JsonInteger>(data_->content.asInt32));
case VariantType::Uint32: case VariantType::Uint32:
return visit.visit(static_cast<JsonUInt>(content_.asUint32)); return visit.visit(static_cast<JsonUInt>(data_->content.asUint32));
#if ARDUINOJSON_USE_LONG_LONG #if ARDUINOJSON_USE_LONG_LONG
case VariantType::Int64: case VariantType::Int64:
@ -102,62 +153,39 @@ class VariantData {
#endif #endif
case VariantType::Boolean: case VariantType::Boolean:
return visit.visit(content_.asBoolean != 0); return visit.visit(data_->content.asBoolean != 0);
default: default:
return visit.visit(nullptr); return visit.visit(nullptr);
} }
} }
template <typename TVisitor> VariantData* addElement() {
static typename TVisitor::result_type accept(VariantData* var, auto array = isNull() ? toArray() : asArray();
ResourceManager* resources,
TVisitor& visit) {
if (var != 0)
return var->accept(visit, resources);
else
return visit.visit(nullptr);
}
VariantData* addElement(ResourceManager* resources) {
auto array = isNull() ? toArray(resources) : asArray(resources);
return array.addElement(); return array.addElement();
} }
static VariantData* addElement(VariantData* var, ResourceManager* resources) {
if (!var)
return nullptr;
return var->addElement(resources);
}
template <typename T> template <typename T>
bool addValue(const T& value, ResourceManager* resources) { bool addValue(const T& value) {
auto array = isNull() ? toArray(resources) : asArray(resources); auto array = isNull() ? toArray() : asArray();
return array.addValue(value); return array.addValue(value);
} }
template <typename T> bool asBoolean() const {
static bool addValue(VariantData* var, const T& value, if (!data_)
ResourceManager* resources) {
if (!var)
return false; return false;
return var->addValue(value, resources);
}
bool asBoolean(const ResourceManager* resources) const {
#if ARDUINOJSON_USE_8_BYTE_POOL #if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte(resources); auto eightByteValue = getEightByte();
#else
(void)resources; // silence warning
#endif #endif
switch (type_) { switch (data_->type) {
case VariantType::Boolean: case VariantType::Boolean:
return content_.asBoolean; return data_->content.asBoolean;
case VariantType::Uint32: case VariantType::Uint32:
case VariantType::Int32: case VariantType::Int32:
return content_.asUint32 != 0; return data_->content.asUint32 != 0;
case VariantType::Float: case VariantType::Float:
return content_.asFloat != 0; return data_->content.asFloat != 0;
#if ARDUINOJSON_USE_DOUBLE #if ARDUINOJSON_USE_DOUBLE
case VariantType::Double: case VariantType::Double:
return eightByteValue->asDouble != 0; return eightByteValue->asDouble != 0;
@ -174,37 +202,33 @@ class VariantData {
} }
} }
ArrayImpl asArray(ResourceManager* resources) { ArrayImpl asArray() {
return ArrayImpl(isArray() ? &content_.asCollection : nullptr, resources); return ArrayImpl(isArray() ? &data_->content.asCollection : nullptr,
resources_);
} }
static ArrayImpl asArray(VariantData* var, ResourceManager* resources) { CollectionImpl asCollection() {
return ArrayImpl( return CollectionImpl(
var && var->isArray() ? &var->content_.asCollection : nullptr, isCollection() ? &data_->content.asCollection : nullptr, resources_);
resources);
}
CollectionImpl asCollection(ResourceManager* resources) {
return CollectionImpl(isCollection() ? &content_.asCollection : nullptr,
resources);
} }
template <typename T> template <typename T>
T asFloat(const ResourceManager* resources) const { T asFloat() const {
if (!data_)
return 0.0;
static_assert(is_floating_point<T>::value, "T must be a floating point"); static_assert(is_floating_point<T>::value, "T must be a floating point");
#if ARDUINOJSON_USE_8_BYTE_POOL #if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte(resources); auto eightByteValue = getEightByte();
#else
(void)resources; // silence warning
#endif #endif
const char* str = nullptr; const char* str = nullptr;
switch (type_) { switch (data_->type) {
case VariantType::Boolean: case VariantType::Boolean:
return static_cast<T>(content_.asBoolean); return static_cast<T>(data_->content.asBoolean);
case VariantType::Uint32: case VariantType::Uint32:
return static_cast<T>(content_.asUint32); return static_cast<T>(data_->content.asUint32);
case VariantType::Int32: case VariantType::Int32:
return static_cast<T>(content_.asInt32); return static_cast<T>(data_->content.asInt32);
#if ARDUINOJSON_USE_LONG_LONG #if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64: case VariantType::Uint64:
return static_cast<T>(eightByteValue->asUint64); return static_cast<T>(eightByteValue->asUint64);
@ -212,16 +236,16 @@ class VariantData {
return static_cast<T>(eightByteValue->asInt64); return static_cast<T>(eightByteValue->asInt64);
#endif #endif
case VariantType::TinyString: case VariantType::TinyString:
str = content_.asTinyString; str = data_->content.asTinyString;
break; break;
case VariantType::LinkedString: case VariantType::LinkedString:
str = asLinkedString(resources); str = asLinkedString();
break; break;
case VariantType::OwnedString: case VariantType::OwnedString:
str = content_.asOwnedString->data; str = data_->content.asOwnedString->data;
break; break;
case VariantType::Float: case VariantType::Float:
return static_cast<T>(content_.asFloat); return static_cast<T>(data_->content.asFloat);
#if ARDUINOJSON_USE_DOUBLE #if ARDUINOJSON_USE_DOUBLE
case VariantType::Double: case VariantType::Double:
return static_cast<T>(eightByteValue->asDouble); return static_cast<T>(eightByteValue->asDouble);
@ -235,21 +259,22 @@ class VariantData {
} }
template <typename T> template <typename T>
T asIntegral(const ResourceManager* resources) const { T asIntegral() const {
if (!data_)
return 0;
static_assert(is_integral<T>::value, "T must be an integral type"); static_assert(is_integral<T>::value, "T must be an integral type");
#if ARDUINOJSON_USE_8_BYTE_POOL #if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte(resources); auto eightByteValue = getEightByte();
#else
(void)resources; // silence warning
#endif #endif
const char* str = nullptr; const char* str = nullptr;
switch (type_) { switch (data_->type) {
case VariantType::Boolean: case VariantType::Boolean:
return content_.asBoolean; return data_->content.asBoolean;
case VariantType::Uint32: case VariantType::Uint32:
return convertNumber<T>(content_.asUint32); return convertNumber<T>(data_->content.asUint32);
case VariantType::Int32: case VariantType::Int32:
return convertNumber<T>(content_.asInt32); return convertNumber<T>(data_->content.asInt32);
#if ARDUINOJSON_USE_LONG_LONG #if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64: case VariantType::Uint64:
return convertNumber<T>(eightByteValue->asUint64); return convertNumber<T>(eightByteValue->asUint64);
@ -257,16 +282,16 @@ class VariantData {
return convertNumber<T>(eightByteValue->asInt64); return convertNumber<T>(eightByteValue->asInt64);
#endif #endif
case VariantType::TinyString: case VariantType::TinyString:
str = content_.asTinyString; str = data_->content.asTinyString;
break; break;
case VariantType::LinkedString: case VariantType::LinkedString:
str = asLinkedString(resources); str = asLinkedString();
break; break;
case VariantType::OwnedString: case VariantType::OwnedString:
str = content_.asOwnedString->data; str = data_->content.asOwnedString->data;
break; break;
case VariantType::Float: case VariantType::Float:
return convertNumber<T>(content_.asFloat); return convertNumber<T>(data_->content.asFloat);
#if ARDUINOJSON_USE_DOUBLE #if ARDUINOJSON_USE_DOUBLE
case VariantType::Double: case VariantType::Double:
return convertNumber<T>(eightByteValue->asDouble); return convertNumber<T>(eightByteValue->asDouble);
@ -279,112 +304,93 @@ class VariantData {
return parseNumber<T>(str); return parseNumber<T>(str);
} }
ObjectImpl asObject(ResourceManager* resources) { ObjectImpl asObject() {
return ObjectImpl(isObject() ? &content_.asCollection : nullptr, resources); return ObjectImpl(isObject() ? &data_->content.asCollection : nullptr,
} resources_);
static ObjectImpl asObject(VariantData* var, ResourceManager* resources) {
return ObjectImpl(
var && var->isObject() ? &var->content_.asCollection : nullptr,
resources);
} }
JsonString asRawString() const { JsonString asRawString() const {
switch (type_) { switch (type()) {
case VariantType::RawString: case VariantType::RawString:
return JsonString(content_.asOwnedString->data, return JsonString(data_->content.asOwnedString->data,
content_.asOwnedString->length); data_->content.asOwnedString->length);
default: default:
return JsonString(); return JsonString();
} }
} }
const char* asLinkedString(const ResourceManager* resources) const; const char* asLinkedString() const;
JsonString asString(const ResourceManager* resources) const { JsonString asString() const {
switch (type_) { switch (type()) {
case VariantType::TinyString: case VariantType::TinyString:
return JsonString(content_.asTinyString); return JsonString(data_->content.asTinyString);
case VariantType::LinkedString: case VariantType::LinkedString:
return JsonString(asLinkedString(resources), true); return JsonString(asLinkedString(), true);
case VariantType::OwnedString: case VariantType::OwnedString:
return JsonString(content_.asOwnedString->data, return JsonString(data_->content.asOwnedString->data,
content_.asOwnedString->length); data_->content.asOwnedString->length);
default: default:
return JsonString(); return JsonString();
} }
} }
#if ARDUINOJSON_USE_8_BYTE_POOL #if ARDUINOJSON_USE_8_BYTE_POOL
const EightByteValue* getEightByte(const ResourceManager* resources) const; const EightByteValue* getEightByte() const;
#endif #endif
VariantData* getElement(size_t index, ResourceManager* resources) { VariantData* getElement(size_t index) {
return asArray(resources).getElement(index); return asArray().getElement(index);
}
static VariantData* getElement(VariantData* var, size_t index,
ResourceManager* resources) {
if (!var)
return nullptr;
return var->asArray(resources).getElement(index);
} }
template <typename TAdaptedString> template <typename TAdaptedString>
VariantData* getMember(TAdaptedString key, ResourceManager* resources) { VariantData* getMember(TAdaptedString key) {
return asObject(resources).getMember(key); return asObject().getMember(key);
} }
template <typename TAdaptedString> VariantData* getOrAddElement(size_t index) {
static VariantData* getMember(VariantData* var, TAdaptedString key, auto array = isNull() ? toArray() : asArray();
ResourceManager* resources) {
if (!var)
return 0;
return var->getMember(key, resources);
}
VariantData* getOrAddElement(size_t index, ResourceManager* resources) {
auto array = isNull() ? toArray(resources) : asArray(resources);
return array.getOrAddElement(index); return array.getOrAddElement(index);
} }
template <typename TAdaptedString> template <typename TAdaptedString>
VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources) { VariantData* getOrAddMember(TAdaptedString key) {
if (key.isNull()) if (key.isNull())
return nullptr; return nullptr;
auto obj = isNull() ? toObject(resources) : asObject(resources); auto obj = isNull() ? toObject() : asObject();
return obj.getOrAddMember(key); return obj.getOrAddMember(key);
} }
bool isArray() const { bool isArray() const {
return type_ == VariantType::Array; return type() == VariantType::Array;
} }
bool isBoolean() const { bool isBoolean() const {
return type_ == VariantType::Boolean; return type() == VariantType::Boolean;
} }
bool isCollection() const { bool isCollection() const {
return type_ & VariantTypeBits::CollectionMask; return type() & VariantTypeBits::CollectionMask;
} }
bool isFloat() const { bool isFloat() const {
return type_ & VariantTypeBits::NumberBit; return data_ && data_->isFloat();
} }
template <typename T> template <typename T>
bool isInteger(const ResourceManager* resources) const { bool isInteger() const {
if (!data_)
return false;
#if ARDUINOJSON_USE_LONG_LONG #if ARDUINOJSON_USE_LONG_LONG
auto eightByteValue = getEightByte(resources); auto eightByteValue = getEightByte();
#else
(void)resources; // silence warning
#endif #endif
switch (type_) { switch (data_->type) {
case VariantType::Uint32: case VariantType::Uint32:
return canConvertNumber<T>(content_.asUint32); return canConvertNumber<T>(data_->content.asUint32);
case VariantType::Int32: case VariantType::Int32:
return canConvertNumber<T>(content_.asInt32); return canConvertNumber<T>(data_->content.asInt32);
#if ARDUINOJSON_USE_LONG_LONG #if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64: case VariantType::Uint64:
@ -400,197 +406,102 @@ class VariantData {
} }
bool isNull() const { bool isNull() const {
return type_ == VariantType::Null; return type() == VariantType::Null;
}
static bool isNull(const VariantData* var) {
if (!var)
return true;
return var->isNull();
} }
bool isObject() const { bool isObject() const {
return type_ == VariantType::Object; return type() == VariantType::Object;
} }
bool isString() const { bool isString() const {
return type_ == VariantType::LinkedString || return data_ && data_->isString();
type_ == VariantType::OwnedString ||
type_ == VariantType::TinyString;
} }
size_t nesting(ResourceManager* resources) { size_t nesting() {
return asCollection(resources).nesting(); return asCollection().nesting();
} }
static size_t nesting(VariantData* var, ResourceManager* resources) { void removeElement(size_t index) {
if (!var) asArray().removeElement(index);
return 0;
return var->nesting(resources);
}
void removeElement(size_t index, ResourceManager* resources) {
asArray(resources).removeElement(index);
}
static void removeElement(VariantData* var, size_t index,
ResourceManager* resources) {
if (!var)
return;
var->removeElement(index, resources);
} }
template <typename TAdaptedString> template <typename TAdaptedString>
void removeMember(TAdaptedString key, ResourceManager* resources) { void removeMember(TAdaptedString key) {
asObject(resources).removeMember(key); asObject().removeMember(key);
} }
template <typename TAdaptedString> bool setBoolean(bool value) {
static void removeMember(VariantData* var, TAdaptedString key, if (!data_)
ResourceManager* resources) { return false;
if (!var) data_->setBoolean(value);
return;
var->removeMember(key, resources);
}
void reset() { // TODO: remove
type_ = VariantType::Null;
}
void setBoolean(bool value) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
type_ = VariantType::Boolean;
content_.asBoolean = value;
}
template <typename T>
enable_if_t<sizeof(T) == 4, bool> setFloat(T value, ResourceManager*) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
type_ = VariantType::Float;
content_.asFloat = value;
return true; return true;
} }
template <typename T> template <typename T>
enable_if_t<sizeof(T) == 8, bool> setFloat(T value, ResourceManager*); enable_if_t<sizeof(T) == 4, bool> setFloat(T value) {
ARDUINOJSON_ASSERT(type() == VariantType::Null); // must call clear() first
template <typename T> if (!data_)
enable_if_t<is_signed<T>::value, bool> setInteger(T value, return false;
ResourceManager* resources); data_->type = VariantType::Float;
data_->content.asFloat = value;
template <typename T> return true;
enable_if_t<is_unsigned<T>::value, bool> setInteger(
T value, ResourceManager* resources);
void setRawString(StringNode* s) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
ARDUINOJSON_ASSERT(s);
type_ = VariantType::RawString;
content_.asOwnedString = s;
} }
template <typename T> template <typename T>
void setRawString(SerializedValue<T> value, ResourceManager* resources); enable_if_t<sizeof(T) == 8, bool> setFloat(T value);
template <typename T> template <typename T>
static void setRawString(VariantData* var, SerializedValue<T> value, enable_if_t<is_signed<T>::value, bool> setInteger(T value);
ResourceManager* resources) {
if (!var) template <typename T>
return; enable_if_t<is_unsigned<T>::value, bool> setInteger(T value);
var->clear(resources);
var->setRawString(value, resources); template <typename T>
} void setRawString(SerializedValue<T> value);
template <typename TAdaptedString> template <typename TAdaptedString>
bool setString(TAdaptedString value, ResourceManager* resources); bool setString(TAdaptedString value);
template <typename TAdaptedString> bool setLinkedString(const char* s);
static void setString(VariantData* var, TAdaptedString value,
ResourceManager* resources) {
if (!var)
return;
var->clear(resources);
var->setString(value, resources);
}
bool setLinkedString(const char* s, ResourceManager* resources); size_t size() {
template <typename TAdaptedString>
void setTinyString(const TAdaptedString& s) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
ARDUINOJSON_ASSERT(s.size() <= tinyStringMaxLength);
type_ = VariantType::TinyString;
auto n = uint8_t(s.size());
for (uint8_t i = 0; i < n; i++) {
char c = s[i];
ARDUINOJSON_ASSERT(c != 0); // no NUL in tiny string
content_.asTinyString[i] = c;
}
content_.asTinyString[n] = 0;
}
void setOwnedString(StringNode* s) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
ARDUINOJSON_ASSERT(s);
type_ = VariantType::OwnedString;
content_.asOwnedString = s;
}
size_t size(ResourceManager* resources) {
if (isObject()) if (isObject())
return asObject(resources).size(); return asObject().size();
if (isArray()) if (isArray())
return asArray(resources).size(); return asArray().size();
return 0; return 0;
} }
static size_t size(VariantData* var, ResourceManager* resources) { ArrayImpl toArray() {
return var != 0 ? var->size(resources) : 0; 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_);
} }
ArrayImpl toArray(ResourceManager* resources) { ObjectImpl toObject() {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first ARDUINOJSON_ASSERT(type() == VariantType::Null); // must call clear() first
type_ = VariantType::Array; if (!data_)
return ArrayImpl(new (&content_.asCollection) CollectionData(), resources);
}
static ArrayImpl toArray(VariantData* var, ResourceManager* resources) {
if (!var)
return ArrayImpl(nullptr, resources);
var->clear(resources);
return var->toArray(resources);
}
ObjectImpl toObject(ResourceManager* resources) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
type_ = VariantType::Object;
return ObjectImpl(new (&content_.asCollection) CollectionData(), resources);
}
static ObjectImpl toObject(VariantData* var, ResourceManager* resources) {
if (!var)
return ObjectImpl(); return ObjectImpl();
var->clear(resources); data_->type = VariantType::Object;
return var->toObject(resources); return ObjectImpl(new (&data_->content.asCollection) CollectionData(),
resources_);
} }
VariantType type() const { VariantType type() const {
return type_; return data_ ? data_->type : VariantType::Null;
} }
// Release the resources used by this variant and set it to null. // Release the resources used by this variant and set it to null.
void clear(ResourceManager* resources); void clear();
static void clear(VariantData* var, ResourceManager* resources) { private:
if (!var) VariantData* data_;
return; ResourceManager* resources_;
var->clear(resources);
}
}; };
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -10,126 +10,130 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename T> template <typename T>
inline void VariantData::setRawString(SerializedValue<T> value, inline void VariantImpl::setRawString(SerializedValue<T> value) {
ResourceManager* resources) { if (!data_)
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first return;
auto dup = resources->saveString(adaptString(value.data(), value.size())); auto dup = resources_->saveString(adaptString(value.data(), value.size()));
if (dup) if (dup)
setRawString(dup); data_->setRawString(dup);
} }
inline bool VariantData::setLinkedString(const char* s, inline bool VariantImpl::setLinkedString(const char* s) {
ResourceManager* resources) { ARDUINOJSON_ASSERT(isNull()); // must call clear() first
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
ARDUINOJSON_ASSERT(s); ARDUINOJSON_ASSERT(s);
auto slotId = resources->saveStaticString(s); auto slotId = resources_->saveStaticString(s);
if (slotId == NULL_SLOT) if (slotId == NULL_SLOT)
return false; return false;
type_ = VariantType::LinkedString; data_->type = VariantType::LinkedString;
content_.asSlotId = slotId; data_->content.asSlotId = slotId;
return true; return true;
} }
template <typename TAdaptedString> template <typename TAdaptedString>
inline bool VariantData::setString(TAdaptedString value, inline bool VariantImpl::setString(TAdaptedString value) {
ResourceManager* resources) { ARDUINOJSON_ASSERT(isNull()); // must call clear() first
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
if (!data_)
return false;
if (value.isNull()) if (value.isNull())
return false; return false;
if (value.isStatic()) if (value.isStatic())
return setLinkedString(value.data(), resources); return setLinkedString(value.data());
if (isTinyString(value, value.size())) { if (isTinyString(value, value.size())) {
setTinyString(value); data_->setTinyString(value);
return true; return true;
} }
auto dup = resources->saveString(value); auto dup = resources_->saveString(value);
if (dup) { if (dup) {
setOwnedString(dup); data_->setOwnedString(dup);
return true; return true;
} }
return false; return false;
} }
inline void VariantData::clear(ResourceManager* resources) { inline void VariantImpl::clear() {
if (type_ & VariantTypeBits::OwnedStringBit) if (!data_)
resources->dereferenceString(content_.asOwnedString->data); return;
if (data_->type & VariantTypeBits::OwnedStringBit)
resources_->dereferenceString(data_->content.asOwnedString->data);
#if ARDUINOJSON_USE_8_BYTE_POOL #if ARDUINOJSON_USE_8_BYTE_POOL
if (type_ & VariantTypeBits::EightByteBit) if (data_->type & VariantTypeBits::EightByteBit)
resources->freeEightByte(content_.asSlotId); resources_->freeEightByte(data_->content.asSlotId);
#endif #endif
asCollection(resources).clear(); asCollection().clear();
type_ = VariantType::Null; data_->type = VariantType::Null;
} }
#if ARDUINOJSON_USE_8_BYTE_POOL #if ARDUINOJSON_USE_8_BYTE_POOL
inline const EightByteValue* VariantData::getEightByte( inline const EightByteValue* VariantImpl::getEightByte() const {
const ResourceManager* resources) const { return type() & VariantTypeBits::EightByteBit
return type_ & VariantTypeBits::EightByteBit ? resources_->getEightByte(data_->content.asSlotId)
? resources->getEightByte(content_.asSlotId)
: 0; : 0;
} }
#endif #endif
inline const char* VariantData::asLinkedString( inline const char* VariantImpl::asLinkedString() const {
const ResourceManager* resources) const { ARDUINOJSON_ASSERT(type() == VariantType::LinkedString);
ARDUINOJSON_ASSERT(type_ == VariantType::LinkedString); return resources_->getStaticString(data_->content.asSlotId);
return resources->getStaticString(content_.asSlotId);
} }
template <typename T> template <typename T>
enable_if_t<sizeof(T) == 8, bool> VariantData::setFloat( enable_if_t<sizeof(T) == 8, bool> VariantImpl::setFloat(T value) {
T value, ResourceManager* resources) { ARDUINOJSON_ASSERT(isNull()); // must call clear() first
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
(void)resources; // silence warning if (!data_)
return false;
float valueAsFloat = static_cast<float>(value); float valueAsFloat = static_cast<float>(value);
#if ARDUINOJSON_USE_DOUBLE #if ARDUINOJSON_USE_DOUBLE
if (value == valueAsFloat) { if (value == valueAsFloat) {
type_ = VariantType::Float; data_->type = VariantType::Float;
content_.asFloat = valueAsFloat; data_->content.asFloat = valueAsFloat;
} else { } else {
auto slot = resources->allocEightByte(); auto slot = resources_->allocEightByte();
if (!slot) if (!slot)
return false; return false;
type_ = VariantType::Double; data_->type = VariantType::Double;
content_.asSlotId = slot.id(); data_->content.asSlotId = slot.id();
slot->asDouble = value; slot->asDouble = value;
} }
#else #else
type_ = VariantType::Float; data_->type = VariantType::Float;
content_.asFloat = valueAsFloat; data_->content.asFloat = valueAsFloat;
#endif #endif
return true; return true;
} }
template <typename T> template <typename T>
enable_if_t<is_signed<T>::value, bool> VariantData::setInteger( enable_if_t<is_signed<T>::value, bool> VariantImpl::setInteger(T value) {
T value, ResourceManager* resources) { ARDUINOJSON_ASSERT(isNull()); // must call clear() first
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
(void)resources; // silence warning if (!data_)
return false;
if (canConvertNumber<int32_t>(value)) { if (canConvertNumber<int32_t>(value)) {
type_ = VariantType::Int32; data_->type = VariantType::Int32;
content_.asInt32 = static_cast<int32_t>(value); data_->content.asInt32 = static_cast<int32_t>(value);
} }
#if ARDUINOJSON_USE_LONG_LONG #if ARDUINOJSON_USE_LONG_LONG
else { else {
auto slot = resources->allocEightByte(); auto slot = resources_->allocEightByte();
if (!slot) if (!slot)
return false; return false;
type_ = VariantType::Int64; data_->type = VariantType::Int64;
content_.asSlotId = slot.id(); data_->content.asSlotId = slot.id();
slot->asInt64 = value; slot->asInt64 = value;
} }
#endif #endif
@ -137,22 +141,23 @@ enable_if_t<is_signed<T>::value, bool> VariantData::setInteger(
} }
template <typename T> template <typename T>
enable_if_t<is_unsigned<T>::value, bool> VariantData::setInteger( enable_if_t<is_unsigned<T>::value, bool> VariantImpl::setInteger(T value) {
T value, ResourceManager* resources) { ARDUINOJSON_ASSERT(isNull()); // must call clear() first
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
(void)resources; // silence warning if (!data_)
return false;
if (canConvertNumber<uint32_t>(value)) { if (canConvertNumber<uint32_t>(value)) {
type_ = VariantType::Uint32; data_->type = VariantType::Uint32;
content_.asUint32 = static_cast<uint32_t>(value); data_->content.asUint32 = static_cast<uint32_t>(value);
} }
#if ARDUINOJSON_USE_LONG_LONG #if ARDUINOJSON_USE_LONG_LONG
else { else {
auto slot = resources->allocEightByte(); auto slot = resources_->allocEightByte();
if (!slot) if (!slot)
return false; return false;
type_ = VariantType::Uint64; data_->type = VariantType::Uint64;
content_.asSlotId = slot.id(); data_->content.asSlotId = slot.id();
slot->asUint64 = value; slot->asUint64 = value;
} }
#endif #endif

View File

@ -29,13 +29,13 @@ class VariantRefBase : public VariantTag {
// Sets the value to null. // Sets the value to null.
// https://arduinojson.org/v7/api/jsonvariant/clear/ // https://arduinojson.org/v7/api/jsonvariant/clear/
void clear() const { void clear() const {
VariantData::clear(getOrCreateData(), getResourceManager()); getOrCreateVariantImpl().clear();
} }
// Returns true if the value is null or the reference is unbound. // Returns true if the value is null or the reference is unbound.
// https://arduinojson.org/v7/api/jsonvariant/isnull/ // https://arduinojson.org/v7/api/jsonvariant/isnull/
bool isNull() const { bool isNull() const {
return VariantData::isNull(getData()); return getVariantImpl().isNull();
} }
// Returns true if the reference is unbound. // Returns true if the reference is unbound.
@ -93,13 +93,13 @@ class VariantRefBase : public VariantTag {
// Returns the size of the array or object. // Returns the size of the array or object.
// https://arduinojson.org/v7/api/jsonvariant/size/ // https://arduinojson.org/v7/api/jsonvariant/size/
size_t size() const { size_t size() const {
return VariantData::size(getData(), getResourceManager()); return getVariantImpl().size();
} }
// Returns the depth (nesting level) of the value. // Returns the depth (nesting level) of the value.
// https://arduinojson.org/v7/api/jsonvariant/nesting/ // https://arduinojson.org/v7/api/jsonvariant/nesting/
size_t nesting() const { size_t nesting() const {
return VariantData::nesting(getData(), getResourceManager()); return getVariantImpl().nesting();
} }
// Appends a new (empty) element to the array. // Appends a new (empty) element to the array.
@ -120,38 +120,34 @@ class VariantRefBase : public VariantTag {
// https://arduinojson.org/v7/api/jsonvariant/add/ // https://arduinojson.org/v7/api/jsonvariant/add/
template <typename T> template <typename T>
bool add(const T& value) const { bool add(const T& value) const {
return detail::VariantData::addValue(getOrCreateData(), value, return getOrCreateVariantImpl().addValue(value);
getResourceManager());
} }
// Appends a value to the array. // Appends a value to the array.
// https://arduinojson.org/v7/api/jsonvariant/add/ // https://arduinojson.org/v7/api/jsonvariant/add/
template <typename T, enable_if_t<!is_const<T>::value, int> = 0> template <typename T, enable_if_t<!is_const<T>::value, int> = 0>
bool add(T* value) const { bool add(T* value) const {
return detail::VariantData::addValue(getOrCreateData(), value, return getOrCreateVariantImpl().addValue(value);
getResourceManager());
} }
// Removes an element of the array. // Removes an element of the array.
// https://arduinojson.org/v7/api/jsonvariant/remove/ // https://arduinojson.org/v7/api/jsonvariant/remove/
void remove(size_t index) const { void remove(size_t index) const {
VariantData::removeElement(getData(), index, getResourceManager()); getVariantImpl().removeElement(index);
} }
// Removes a member of the object. // Removes a member of the object.
// https://arduinojson.org/v7/api/jsonvariant/remove/ // https://arduinojson.org/v7/api/jsonvariant/remove/
template <typename TChar, enable_if_t<IsString<TChar*>::value, int> = 0> template <typename TChar, enable_if_t<IsString<TChar*>::value, int> = 0>
void remove(TChar* key) const { void remove(TChar* key) const {
VariantData::removeMember(getData(), adaptString(key), getVariantImpl().removeMember(adaptString(key));
getResourceManager());
} }
// Removes a member of the object. // Removes a member of the object.
// https://arduinojson.org/v7/api/jsonvariant/remove/ // https://arduinojson.org/v7/api/jsonvariant/remove/
template <typename TString, enable_if_t<IsString<TString>::value, int> = 0> template <typename TString, enable_if_t<IsString<TString>::value, int> = 0>
void remove(const TString& key) const { void remove(const TString& key) const {
VariantData::removeMember(getData(), adaptString(key), getVariantImpl().removeMember(adaptString(key));
getResourceManager());
} }
// Removes a member of the object or an element of the array. // Removes a member of the object or an element of the array.
@ -275,6 +271,14 @@ class VariantRefBase : public VariantTag {
return VariantAttorney::getOrCreateData(derived()); return VariantAttorney::getOrCreateData(derived());
} }
VariantImpl getVariantImpl() const {
return VariantImpl(getData(), getResourceManager());
}
VariantImpl getOrCreateVariantImpl() const {
return VariantImpl(getOrCreateData(), getResourceManager());
}
FORCE_INLINE ArduinoJson::JsonVariant getVariant() const; FORCE_INLINE ArduinoJson::JsonVariant getVariant() const;
FORCE_INLINE ArduinoJson::JsonVariantConst getVariantConst() const { FORCE_INLINE ArduinoJson::JsonVariantConst getVariantConst() const {

View File

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