Decouple VariantImpl from ArrayImpl and ObjectImpl

This commit is contained in:
Benoit Blanchon
2025-07-02 18:21:51 +02:00
parent f1b06b588e
commit 8790802ce3
9 changed files with 118 additions and 74 deletions

View File

@ -24,10 +24,11 @@ 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::VariantImpl(data, resources).asArray()) {} : impl_(data ? data->asArray() : nullptr, resources) {}
// INTERNAL USE ONLY // INTERNAL USE ONLY
JsonArray(const detail::ArrayImpl& impl) : impl_(impl) {} JsonArray(detail::CollectionData* data, detail::ResourceManager* resources)
: impl_(data, resources) {}
// Returns a JsonVariant pointing to the array. // Returns a JsonVariant pointing to the array.
// https://arduinojson.org/v7/api/jsonvariant/ // https://arduinojson.org/v7/api/jsonvariant/

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::VariantImpl(data, resources).asArray()) {} : impl_(data ? data->asArray() : nullptr, resources) {}
// INTERNAL USE ONLY // INTERNAL USE ONLY
JsonArrayConst(const detail::ArrayImpl& impl) : impl_(impl) {} JsonArrayConst(const detail::ArrayImpl& impl) : impl_(impl) {}

View File

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

View File

@ -352,7 +352,7 @@ class MsgPackDeserializer {
ArrayImpl array; ArrayImpl array;
if (allowArray) { if (allowArray) {
ARDUINOJSON_ASSERT(variant != 0); ARDUINOJSON_ASSERT(variant != 0);
array = VariantImpl(variant, resources_).toArray(); array = ArrayImpl(variant->toArray(), resources_);
} }
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 = VariantImpl(variant, resources_).toObject(); object = ObjectImpl(variant->toObject(), resources_);
} }
for (; n; --n) { for (; n; --n) {

View File

@ -23,11 +23,12 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
JsonObject() {} JsonObject() {}
// INTERNAL USE ONLY // INTERNAL USE ONLY
JsonObject(const detail::ObjectImpl& impl) : impl_(impl) {} JsonObject(detail::CollectionData* data, detail::ResourceManager* resource)
: impl_(data, resource) {}
// INTERNAL USE ONLY // INTERNAL USE ONLY
JsonObject(detail::VariantData* data, detail::ResourceManager* resource) JsonObject(detail::VariantData* data, detail::ResourceManager* resource)
: impl_(detail::VariantImpl(data, resource).asObject()) {} : impl_(data ? data->asObject() : nullptr, resource) {}
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::VariantImpl(data, resources).asObject()) {} : impl_(data ? data->asObject() : nullptr, resources) {}
// INTERNAL USE ONLY // INTERNAL USE ONLY
JsonObjectConst(const detail::ObjectImpl& impl) : impl_(impl) {} JsonObjectConst(const detail::ObjectImpl& impl) : impl_(impl) {}

View File

@ -71,6 +71,19 @@ struct VariantData {
content.asOwnedString = s; content.asOwnedString = s;
} }
CollectionData* asCollection() {
return type & VariantTypeBits::CollectionMask ? &content.asCollection
: nullptr;
}
CollectionData* asArray() {
return type == VariantType::Array ? &content.asCollection : nullptr;
}
CollectionData* asObject() {
return type == VariantType::Object ? &content.asCollection : nullptr;
}
bool isFloat() const { bool isFloat() const {
return type & VariantTypeBits::NumberBit; return type & VariantTypeBits::NumberBit;
} }
@ -79,6 +92,40 @@ struct VariantData {
return type == VariantType::LinkedString || return type == VariantType::LinkedString ||
type == VariantType::OwnedString || type == VariantType::TinyString; type == VariantType::OwnedString || type == VariantType::TinyString;
} }
CollectionData* toArray() {
ARDUINOJSON_ASSERT(type == VariantType::Null);
type = VariantType::Array;
return new (&content.asCollection) CollectionData();
}
CollectionData* toObject() {
ARDUINOJSON_ASSERT(type == VariantType::Null);
type = VariantType::Object;
return new (&content.asCollection) CollectionData();
}
CollectionData* getOrCreateArray() {
switch (type) {
case VariantType::Null:
return toArray();
case VariantType::Array:
return &content.asCollection;
default:
return nullptr;
}
}
CollectionData* getOrCreateObject() {
switch (type) {
case VariantType::Null:
return toObject();
case VariantType::Object:
return &content.asCollection;
default:
return nullptr;
}
}
}; };
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -46,10 +46,11 @@ class VariantImpl {
#endif #endif
case VariantType::Array: case VariantType::Array:
return visit.visit(asArray()); return visit.visit(ArrayImpl(&data_->content.asCollection, resources_));
case VariantType::Object: case VariantType::Object:
return visit.visit(asObject()); return visit.visit(
ObjectImpl(&data_->content.asCollection, resources_));
case VariantType::TinyString: case VariantType::TinyString:
return visit.visit(JsonString(data_->content.asTinyString)); return visit.visit(JsonString(data_->content.asTinyString));
@ -88,14 +89,16 @@ class VariantImpl {
} }
VariantData* addElement() { VariantData* addElement() {
auto array = isNull() ? toArray() : asArray(); if (!data_)
return array.addElement(); return nullptr;
return ArrayImpl(data_->getOrCreateArray(), resources_).addElement();
} }
template <typename T> template <typename T>
bool addValue(const T& value) { bool addValue(const T& value) {
auto array = isNull() ? toArray() : asArray(); if (!data_)
return array.addValue(value); return false;
return ArrayImpl(data_->getOrCreateArray(), resources_).addValue(value);
} }
bool asBoolean() const { bool asBoolean() const {
@ -129,16 +132,6 @@ class VariantImpl {
} }
} }
ArrayImpl asArray() {
return ArrayImpl(isArray() ? &data_->content.asCollection : nullptr,
resources_);
}
CollectionImpl asCollection() {
return CollectionImpl(
isCollection() ? &data_->content.asCollection : nullptr, resources_);
}
template <typename T> template <typename T>
T asFloat() const { T asFloat() const {
if (!data_) if (!data_)
@ -231,11 +224,6 @@ class VariantImpl {
return parseNumber<T>(str); return parseNumber<T>(str);
} }
ObjectImpl asObject() {
return ObjectImpl(isObject() ? &data_->content.asCollection : nullptr,
resources_);
}
JsonString asRawString() const { JsonString asRawString() const {
switch (type()) { switch (type()) {
case VariantType::RawString: case VariantType::RawString:
@ -274,25 +262,33 @@ class VariantImpl {
#endif #endif
VariantData* getElement(size_t index) { VariantData* getElement(size_t index) {
return asArray().getElement(index); if (!data_)
return nullptr;
return ArrayImpl(data_->asArray(), resources_).getElement(index);
} }
template <typename TAdaptedString> template <typename TAdaptedString>
VariantData* getMember(TAdaptedString key) { VariantData* getMember(TAdaptedString key) {
return asObject().getMember(key); if (!data_)
return nullptr;
return ObjectImpl(data_->asObject(), resources_).getMember(key);
} }
VariantData* getOrAddElement(size_t index) { VariantData* getOrAddElement(size_t index) {
auto array = isNull() ? toArray() : asArray(); if (!data_)
return array.getOrAddElement(index); return nullptr;
return ArrayImpl(data_->getOrCreateArray(), resources_)
.getOrAddElement(index);
} }
template <typename TAdaptedString> template <typename TAdaptedString>
VariantData* getOrAddMember(TAdaptedString key) { VariantData* getOrAddMember(TAdaptedString key) {
if (key.isNull()) if (key.isNull())
return nullptr; return nullptr;
auto obj = isNull() ? toObject() : asObject(); if (!data_)
return obj.getOrAddMember(key); return nullptr;
return ObjectImpl(data_->getOrCreateObject(), resources_)
.getOrAddMember(key);
} }
bool isArray() const { bool isArray() const {
@ -352,16 +348,22 @@ class VariantImpl {
} }
size_t nesting() { size_t nesting() {
return asCollection().nesting(); if (!data_)
return 0;
return CollectionImpl(data_->asCollection(), resources_).nesting();
} }
void removeElement(size_t index) { void removeElement(size_t index) {
asArray().removeElement(index); if (!data_)
return;
ArrayImpl(data_->asArray(), resources_).removeElement(index);
} }
template <typename TAdaptedString> template <typename TAdaptedString>
void removeMember(TAdaptedString key) { void removeMember(TAdaptedString key) {
asObject().removeMember(key); if (!data_)
return;
ObjectImpl(data_->asObject(), resources_).removeMember(key);
} }
bool setBoolean(bool value) { bool setBoolean(bool value) {
@ -466,31 +468,15 @@ class VariantImpl {
bool setLinkedString(const char* s); bool setLinkedString(const char* s);
size_t size() { size_t size() {
if (isObject())
return asObject().size();
if (isArray())
return asArray().size();
return 0;
}
ArrayImpl toArray() {
ARDUINOJSON_ASSERT(type() == VariantType::Null); // must call clear() first
if (!data_) if (!data_)
return ArrayImpl(); return 0;
data_->type = VariantType::Array;
return ArrayImpl(new (&data_->content.asCollection) CollectionData(),
resources_);
}
ObjectImpl toObject() { auto size = CollectionImpl(data_->asCollection(), resources_).size();
ARDUINOJSON_ASSERT(type() == VariantType::Null); // must call clear() first
if (!data_) if (data_->type == VariantType::Object)
return ObjectImpl(); size /= 2;
data_->type = VariantType::Object;
return ObjectImpl(new (&data_->content.asCollection) CollectionData(), return size;
resources_);
} }
VariantType type() const { VariantType type() const {
@ -566,7 +552,7 @@ inline void VariantImpl::clear() {
resources_->freeEightByte(data_->content.asSlotId); resources_->freeEightByte(data_->content.asSlotId);
#endif #endif
asCollection().clear(); CollectionImpl(data_->asCollection(), resources_).clear();
data_->type = VariantType::Null; data_->type = VariantType::Null;
} }

View File

@ -147,17 +147,23 @@ 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 {
auto variant = getOrCreateVariantImpl(); auto data = getOrCreateData();
variant.clear(); if (!data)
return JsonArray(variant.toArray()); return JsonArray();
auto resources = getResourceManager();
VariantImpl(data, resources).clear();
return JsonArray(data->toArray(), resources);
} }
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 {
auto variant = getOrCreateVariantImpl(); auto data = getOrCreateData();
variant.clear(); if (!data)
return JsonObject(variant.toObject()); return JsonObject();
auto resources = getResourceManager();
VariantImpl(data, resources).clear();
return JsonObject(data->toObject(), resources);
} }
template <typename TDerived> template <typename TDerived>