VariantImpl: add copy functions

This commit is contained in:
Benoit Blanchon
2025-07-20 18:23:03 +02:00
parent f680598a2f
commit 274fe06b33
12 changed files with 142 additions and 94 deletions

View File

@ -48,7 +48,6 @@
#include "ArduinoJson/Object/MemberProxy.hpp"
#include "ArduinoJson/Object/ObjectImpl.hpp"
#include "ArduinoJson/Variant/ConverterImpl.hpp"
#include "ArduinoJson/Variant/JsonVariantCopier.hpp"
#include "ArduinoJson/Variant/VariantCompare.hpp"
#include "ArduinoJson/Variant/VariantRefBaseImpl.hpp"

View File

@ -76,6 +76,31 @@ inline void VariantImpl::removeElement(size_t index) {
removeElement(at(index));
}
inline bool VariantImpl::copyArray(const VariantImpl& src) {
ARDUINOJSON_ASSERT(isNull());
if (!data_)
return false;
data_->toArray();
for (auto it = src.createIterator(); !it.done(); it.move()) {
auto slot = allocVariant();
if (!slot)
return false;
VariantImpl element(slot.ptr(), resources_);
if (!element.copyVariant(*it)) {
freeVariant(slot);
return false;
}
addElement(slot);
}
return true;
}
// Returns the size (in bytes) of an array with n elements.
constexpr size_t sizeofArray(size_t n) {
return n * sizeof(VariantData);

View File

@ -89,16 +89,8 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// Copies an array.
// https://arduinojson.org/v7/api/jsonarray/set/
bool set(JsonArrayConst src) const {
if (isNull())
return false;
clear();
for (auto element : src) {
if (!add(element))
return false;
}
return true;
impl_.clear();
return impl_.copyArray(detail::VariantAttorney::getImpl(src));
}
// Removes the element at the specified iterator.

View File

@ -18,8 +18,12 @@ inline VariantImpl::iterator VariantImpl::createIterator() const {
return iterator(coll->head, resources_);
}
inline void VariantImpl::appendPair(Slot<VariantData> key,
Slot<VariantData> value) {
inline void VariantImpl::addMember(Slot<VariantData> key,
Slot<VariantData> value) {
ARDUINOJSON_ASSERT(isObject());
ARDUINOJSON_ASSERT(key);
ARDUINOJSON_ASSERT(value);
auto coll = getCollectionData();
key->next = value.id();

View File

@ -280,11 +280,18 @@ class JsonDeserializer {
VariantImpl object(objectData, resources_);
auto member = object.getMember(adaptString(key));
if (!member) {
auto keyVariant = object.addPair(&member);
if (!keyVariant)
auto keySlot = resources_->allocVariant();
if (!keySlot)
return DeserializationError::NoMemory;
stringBuilder_.save(keyVariant);
auto valueSlot = resources_->allocVariant();
if (!valueSlot)
return DeserializationError::NoMemory;
object.addMember(keySlot, valueSlot);
stringBuilder_.save(keySlot.ptr());
member = valueSlot.ptr();
} else {
VariantImpl(member, resources_).clear();
}

View File

@ -401,11 +401,18 @@ class MsgPackDeserializer {
VariantData* member = 0;
if (memberFilter.allow()) {
auto keyVariant = object.addPair(&member);
if (!keyVariant)
auto keySlot = resources_->allocVariant();
if (!keySlot)
return DeserializationError::NoMemory;
stringBuffer_.save(keyVariant);
auto valueSlot = resources_->allocVariant();
if (!valueSlot)
return DeserializationError::NoMemory;
object.addMember(keySlot, valueSlot);
member = valueSlot.ptr();
stringBuffer_.save(keySlot.ptr());
}
err = parseVariant(member, memberFilter, nestingLimit.decrement());

View File

@ -83,16 +83,11 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
// Copies an object.
// https://arduinojson.org/v7/api/jsonobject/set/
bool set(JsonObjectConst src) {
if (isNull() || src.isNull())
if (isNull() ||
src.isNull()) // TODO: this check is not consistent with JsonArray
return false;
clear();
for (auto kvp : src) {
if (!operator[](kvp.key()).set(kvp.value()))
return false;
}
return true;
impl_.clear();
return impl_.copyObject(detail::VariantAttorney::getImpl(src));
}
// Gets or sets the member with specified key.

View File

@ -9,6 +9,46 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline bool VariantImpl::copyObject(const VariantImpl& src) {
ARDUINOJSON_ASSERT(isNull());
if (!data_)
return false;
data_->toObject();
for (auto it = src.createIterator(); !it.done(); it.move()) {
auto keySlot = allocVariant();
if (!keySlot)
return false;
auto key = VariantImpl(keySlot.ptr(), resources_);
if (!key.copyVariant(*it)) {
freeVariant(keySlot);
return false;
}
it.move(); // move to value
ARDUINOJSON_ASSERT(!it.done());
auto valueSlot = allocVariant();
if (!valueSlot) {
freeVariant(keySlot);
return false;
}
// TODO: we add the pair before copying the value to be keep the old
// behavior but this is not consistent with issue #2081
addMember(keySlot, valueSlot);
auto value = VariantImpl(valueSlot.ptr(), resources_);
if (!value.copyVariant(*it))
return false;
}
return true;
}
template <typename TAdaptedString>
inline VariantData* VariantImpl::getMember(TAdaptedString key) const {
auto it = findKey(key);
@ -70,28 +110,11 @@ inline VariantData* VariantImpl::addMember(TAdaptedString key) {
if (!keyImpl.setString(key))
return nullptr;
VariantImpl::appendPair(keySlot, valueSlot);
addMember(keySlot, valueSlot);
return valueSlot.ptr();
}
inline VariantData* VariantImpl::addPair(VariantData** value) {
ARDUINOJSON_ASSERT(isObject());
auto keySlot = allocVariant();
if (!keySlot)
return nullptr;
auto valueSlot = allocVariant();
if (!valueSlot)
return nullptr;
*value = valueSlot.ptr();
VariantImpl::appendPair(keySlot, valueSlot);
return keySlot.ptr();
}
// Returns the size (in bytes) of an object with n members.
constexpr size_t sizeofObject(size_t n) {
return 2 * n * sizeof(VariantData);

View File

@ -37,14 +37,12 @@ class JsonVariant : public detail::VariantRefBase<JsonVariant>,
mutable detail::VariantImpl impl_;
};
namespace detail {
bool copyVariant(JsonVariant dst, JsonVariantConst src);
}
template <>
struct Converter<JsonVariant> : private detail::VariantAttorney {
static bool toJson(JsonVariantConst src, JsonVariant dst) {
return copyVariant(dst, src);
auto impl = getImpl(dst);
impl.clear();
return impl.copyVariant(getImpl(src));
}
static JsonVariant fromJson(JsonVariant src) {
@ -59,7 +57,9 @@ struct Converter<JsonVariant> : private detail::VariantAttorney {
template <>
struct Converter<JsonVariantConst> : private detail::VariantAttorney {
static bool toJson(JsonVariantConst src, JsonVariant dst) {
return copyVariant(dst, src);
auto impl = getImpl(dst);
impl.clear();
return impl.copyVariant(getImpl(src));
}
static JsonVariantConst fromJson(JsonVariantConst src) {

View File

@ -1,34 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Variant/JsonVariant.hpp>
#include <ArduinoJson/Variant/JsonVariantVisitor.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class JsonVariantCopier {
public:
using result_type = bool;
JsonVariantCopier(JsonVariant dst) : dst_(dst) {}
template <typename T>
bool visit(T src) {
return dst_.set(src);
}
private:
JsonVariant dst_;
};
inline bool copyVariant(JsonVariant dst, JsonVariantConst src) {
if (dst.isUnbound())
return false;
JsonVariantCopier copier(dst);
return accept(src, copier);
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -85,17 +85,19 @@ struct VariantData {
}
VariantData* toArray() {
return toCollection(VariantType::Array);
}
VariantData* toCollection(VariantType collectionType) {
ARDUINOJSON_ASSERT(type == VariantType::Null);
type = VariantType::Array;
ARDUINOJSON_ASSERT(collectionType & VariantTypeBits::CollectionMask);
type = collectionType;
new (&content.asCollection) CollectionData();
return this;
}
VariantData* toObject() {
ARDUINOJSON_ASSERT(type == VariantType::Null);
type = VariantType::Object;
new (&content.asCollection) CollectionData();
return this;
return toCollection(VariantType::Object);
}
VariantData* getOrCreateArray() {

View File

@ -276,7 +276,7 @@ class VariantImpl {
VariantData* getOrAddElement(size_t index);
VariantData* addPair(VariantData** value);
void addMember(Slot<VariantData> key, Slot<VariantData> value);
template <typename TAdaptedString>
VariantData* addMember(TAdaptedString key);
@ -354,6 +354,36 @@ class VariantImpl {
void removeMember(iterator it);
bool copyVariant(const VariantImpl& src) {
switch (src.type()) {
case VariantType::Null:
return true;
case VariantType::Array:
return copyArray(src);
case VariantType::Object:
return copyObject(src);
case VariantType::RawString:
return setRawString(adaptString(src.asRawString()));
case VariantType::LinkedString:
return setLinkedString(src.asLinkedString());
case VariantType::OwnedString:
return setOwnedString(adaptString(src.asString()));
default:
data_->content = src.data_->content;
data_->type = src.data_->type;
return true;
}
}
bool copyArray(const VariantImpl& src);
bool copyObject(const VariantImpl& src);
bool setBoolean(bool value) {
if (!data_)
return false;
@ -548,8 +578,6 @@ class VariantImpl {
iterator at(size_t index) const;
void appendPair(Slot<VariantData> key, Slot<VariantData> value);
void removeOne(iterator it);
void removePair(iterator it);