From c1a636c77eba8e8b2c7dfc5954e30d5e0ea23877 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Wed, 2 Jul 2025 18:21:51 +0200 Subject: [PATCH] Decouple `VariantImpl` from `ArrayImpl` and `ObjectImpl` --- src/ArduinoJson/Array/JsonArray.hpp | 5 +- src/ArduinoJson/Array/JsonArrayConst.hpp | 2 +- src/ArduinoJson/Json/JsonDeserializer.hpp | 15 +-- .../MsgPack/MsgPackDeserializer.hpp | 4 +- src/ArduinoJson/Object/JsonObject.hpp | 5 +- src/ArduinoJson/Object/JsonObjectConst.hpp | 2 +- src/ArduinoJson/Variant/VariantData.hpp | 47 ++++++++++ src/ArduinoJson/Variant/VariantImpl.hpp | 94 ++++++++----------- .../Variant/VariantRefBaseImpl.hpp | 18 ++-- 9 files changed, 118 insertions(+), 74 deletions(-) diff --git a/src/ArduinoJson/Array/JsonArray.hpp b/src/ArduinoJson/Array/JsonArray.hpp index 20083ecc..636cfe5a 100644 --- a/src/ArduinoJson/Array/JsonArray.hpp +++ b/src/ArduinoJson/Array/JsonArray.hpp @@ -24,10 +24,11 @@ class JsonArray : public detail::VariantOperators { // INTERNAL USE ONLY JsonArray(detail::VariantData* data, detail::ResourceManager* resources) - : impl_(detail::VariantImpl(data, resources).asArray()) {} + : impl_(data ? data->asArray() : nullptr, resources) {} // 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. // https://arduinojson.org/v7/api/jsonvariant/ diff --git a/src/ArduinoJson/Array/JsonArrayConst.hpp b/src/ArduinoJson/Array/JsonArrayConst.hpp index 87073511..2d9938b7 100644 --- a/src/ArduinoJson/Array/JsonArrayConst.hpp +++ b/src/ArduinoJson/Array/JsonArrayConst.hpp @@ -38,7 +38,7 @@ class JsonArrayConst : public detail::VariantOperators { // INTERNAL USE ONLY JsonArrayConst(detail::VariantData* data, detail::ResourceManager* resources) - : impl_(detail::VariantImpl(data, resources).asArray()) {} + : impl_(data ? data->asArray() : nullptr, resources) {} // INTERNAL USE ONLY JsonArrayConst(const detail::ArrayImpl& impl) : impl_(impl) {} diff --git a/src/ArduinoJson/Json/JsonDeserializer.hpp b/src/ArduinoJson/Json/JsonDeserializer.hpp index 25b81f42..bddb631f 100644 --- a/src/ArduinoJson/Json/JsonDeserializer.hpp +++ b/src/ArduinoJson/Json/JsonDeserializer.hpp @@ -64,6 +64,8 @@ class JsonDeserializer { DeserializationOption::NestingLimit nestingLimit) { DeserializationError::Code err; + ARDUINOJSON_ASSERT(variant != nullptr); + err = skipSpacesAndComments(); if (err) return err; @@ -71,15 +73,13 @@ class JsonDeserializer { switch (current()) { case '[': if (filter.allowArray()) - return parseArray(VariantImpl(variant, resources_).toArray(), filter, - nestingLimit); + return parseArray(variant->toArray(), filter, nestingLimit); else return skipArray(nestingLimit); case '{': if (filter.allowObject()) - return parseObject(VariantImpl(variant, resources_).toObject(), - filter, nestingLimit); + return parseObject(variant->toObject(), filter, nestingLimit); else return skipObject(nestingLimit); @@ -148,7 +148,7 @@ class JsonDeserializer { template DeserializationError::Code parseArray( - ArrayImpl array, TFilter filter, + CollectionData* arrayData, TFilter filter, DeserializationOption::NestingLimit nestingLimit) { DeserializationError::Code err; @@ -173,6 +173,8 @@ class JsonDeserializer { // Read each value for (;;) { if (elementFilter.allow()) { + ArrayImpl array(arrayData, resources_); + // Allocate slot in array VariantData* value = array.addElement(); if (!value) @@ -234,7 +236,7 @@ class JsonDeserializer { template DeserializationError::Code parseObject( - ObjectImpl object, TFilter filter, + CollectionData* objectData, TFilter filter, DeserializationOption::NestingLimit nestingLimit) { DeserializationError::Code err; @@ -275,6 +277,7 @@ class JsonDeserializer { TFilter memberFilter = filter[key]; if (memberFilter.allow()) { + ObjectImpl object(objectData, resources_); auto member = object.getMember(adaptString(key)); if (!member) { auto keyVariant = object.addPair(&member); diff --git a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp index cb81e5c5..b423bdc2 100644 --- a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -352,7 +352,7 @@ class MsgPackDeserializer { ArrayImpl array; if (allowArray) { ARDUINOJSON_ASSERT(variant != 0); - array = VariantImpl(variant, resources_).toArray(); + array = ArrayImpl(variant->toArray(), resources_); } TFilter elementFilter = filter[0U]; @@ -388,7 +388,7 @@ class MsgPackDeserializer { ObjectImpl object; if (filter.allowObject()) { ARDUINOJSON_ASSERT(variant != 0); - object = VariantImpl(variant, resources_).toObject(); + object = ObjectImpl(variant->toObject(), resources_); } for (; n; --n) { diff --git a/src/ArduinoJson/Object/JsonObject.hpp b/src/ArduinoJson/Object/JsonObject.hpp index 759b1f1d..76f7ace8 100644 --- a/src/ArduinoJson/Object/JsonObject.hpp +++ b/src/ArduinoJson/Object/JsonObject.hpp @@ -23,11 +23,12 @@ class JsonObject : public detail::VariantOperators { JsonObject() {} // INTERNAL USE ONLY - JsonObject(const detail::ObjectImpl& impl) : impl_(impl) {} + JsonObject(detail::CollectionData* data, detail::ResourceManager* resource) + : impl_(data, resource) {} // INTERNAL USE ONLY JsonObject(detail::VariantData* data, detail::ResourceManager* resource) - : impl_(detail::VariantImpl(data, resource).asObject()) {} + : impl_(data ? data->asObject() : nullptr, resource) {} operator JsonVariant() const { return JsonVariant(getData(), getResourceManager()); diff --git a/src/ArduinoJson/Object/JsonObjectConst.hpp b/src/ArduinoJson/Object/JsonObjectConst.hpp index f38d6d55..685a6162 100644 --- a/src/ArduinoJson/Object/JsonObjectConst.hpp +++ b/src/ArduinoJson/Object/JsonObjectConst.hpp @@ -23,7 +23,7 @@ class JsonObjectConst : public detail::VariantOperators { // INTERNAL USE ONLY JsonObjectConst(detail::VariantData* data, detail::ResourceManager* resources) - : impl_(detail::VariantImpl(data, resources).asObject()) {} + : impl_(data ? data->asObject() : nullptr, resources) {} // INTERNAL USE ONLY JsonObjectConst(const detail::ObjectImpl& impl) : impl_(impl) {} diff --git a/src/ArduinoJson/Variant/VariantData.hpp b/src/ArduinoJson/Variant/VariantData.hpp index fc916626..1b900007 100644 --- a/src/ArduinoJson/Variant/VariantData.hpp +++ b/src/ArduinoJson/Variant/VariantData.hpp @@ -71,6 +71,19 @@ struct VariantData { 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 { return type & VariantTypeBits::NumberBit; } @@ -79,6 +92,40 @@ struct VariantData { return type == VariantType::LinkedString || 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 diff --git a/src/ArduinoJson/Variant/VariantImpl.hpp b/src/ArduinoJson/Variant/VariantImpl.hpp index 3fbf39bd..4b4951a5 100644 --- a/src/ArduinoJson/Variant/VariantImpl.hpp +++ b/src/ArduinoJson/Variant/VariantImpl.hpp @@ -46,10 +46,11 @@ class VariantImpl { #endif case VariantType::Array: - return visit.visit(asArray()); + return visit.visit(ArrayImpl(&data_->content.asCollection, resources_)); case VariantType::Object: - return visit.visit(asObject()); + return visit.visit( + ObjectImpl(&data_->content.asCollection, resources_)); case VariantType::TinyString: return visit.visit(JsonString(data_->content.asTinyString)); @@ -88,14 +89,16 @@ class VariantImpl { } VariantData* addElement() { - auto array = isNull() ? toArray() : asArray(); - return array.addElement(); + if (!data_) + return nullptr; + return ArrayImpl(data_->getOrCreateArray(), resources_).addElement(); } template bool addValue(const T& value) { - auto array = isNull() ? toArray() : asArray(); - return array.addValue(value); + if (!data_) + return false; + return ArrayImpl(data_->getOrCreateArray(), resources_).addValue(value); } 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 T asFloat() const { if (!data_) @@ -231,11 +224,6 @@ class VariantImpl { return parseNumber(str); } - ObjectImpl asObject() { - return ObjectImpl(isObject() ? &data_->content.asCollection : nullptr, - resources_); - } - JsonString asRawString() const { switch (type()) { case VariantType::RawString: @@ -274,25 +262,33 @@ class VariantImpl { #endif VariantData* getElement(size_t index) { - return asArray().getElement(index); + if (!data_) + return nullptr; + return ArrayImpl(data_->asArray(), resources_).getElement(index); } template VariantData* getMember(TAdaptedString key) { - return asObject().getMember(key); + if (!data_) + return nullptr; + return ObjectImpl(data_->asObject(), resources_).getMember(key); } VariantData* getOrAddElement(size_t index) { - auto array = isNull() ? toArray() : asArray(); - return array.getOrAddElement(index); + if (!data_) + return nullptr; + return ArrayImpl(data_->getOrCreateArray(), resources_) + .getOrAddElement(index); } template VariantData* getOrAddMember(TAdaptedString key) { if (key.isNull()) return nullptr; - auto obj = isNull() ? toObject() : asObject(); - return obj.getOrAddMember(key); + if (!data_) + return nullptr; + return ObjectImpl(data_->getOrCreateObject(), resources_) + .getOrAddMember(key); } bool isArray() const { @@ -352,16 +348,22 @@ class VariantImpl { } size_t nesting() { - return asCollection().nesting(); + if (!data_) + return 0; + return CollectionImpl(data_->asCollection(), resources_).nesting(); } void removeElement(size_t index) { - asArray().removeElement(index); + if (!data_) + return; + ArrayImpl(data_->asArray(), resources_).removeElement(index); } template void removeMember(TAdaptedString key) { - asObject().removeMember(key); + if (!data_) + return; + ObjectImpl(data_->asObject(), resources_).removeMember(key); } bool setBoolean(bool value) { @@ -466,31 +468,15 @@ class VariantImpl { bool setLinkedString(const char* s); size_t size() { - if (isObject()) - return asObject().size(); - - if (isArray()) - return asArray().size(); - - return 0; - } - - ArrayImpl toArray() { - ARDUINOJSON_ASSERT(type() == VariantType::Null); // must call clear() first if (!data_) - return ArrayImpl(); - data_->type = VariantType::Array; - return ArrayImpl(new (&data_->content.asCollection) CollectionData(), - resources_); - } + return 0; - ObjectImpl toObject() { - ARDUINOJSON_ASSERT(type() == VariantType::Null); // must call clear() first - if (!data_) - return ObjectImpl(); - data_->type = VariantType::Object; - return ObjectImpl(new (&data_->content.asCollection) CollectionData(), - resources_); + auto size = CollectionImpl(data_->asCollection(), resources_).size(); + + if (data_->type == VariantType::Object) + size /= 2; + + return size; } VariantType type() const { @@ -566,7 +552,7 @@ inline void VariantImpl::clear() { resources_->freeEightByte(data_->content.asSlotId); #endif - asCollection().clear(); + CollectionImpl(data_->asCollection(), resources_).clear(); data_->type = VariantType::Null; } diff --git a/src/ArduinoJson/Variant/VariantRefBaseImpl.hpp b/src/ArduinoJson/Variant/VariantRefBaseImpl.hpp index 106b3857..8a2da2f6 100644 --- a/src/ArduinoJson/Variant/VariantRefBaseImpl.hpp +++ b/src/ArduinoJson/Variant/VariantRefBaseImpl.hpp @@ -147,17 +147,23 @@ inline bool VariantRefBase::doSet(const T& value, true_type) const { template template ::value, int>> inline JsonArray VariantRefBase::to() const { - auto variant = getOrCreateVariantImpl(); - variant.clear(); - return JsonArray(variant.toArray()); + auto data = getOrCreateData(); + if (!data) + return JsonArray(); + auto resources = getResourceManager(); + VariantImpl(data, resources).clear(); + return JsonArray(data->toArray(), resources); } template template ::value, int>> JsonObject VariantRefBase::to() const { - auto variant = getOrCreateVariantImpl(); - variant.clear(); - return JsonObject(variant.toObject()); + auto data = getOrCreateData(); + if (!data) + return JsonObject(); + auto resources = getResourceManager(); + VariantImpl(data, resources).clear(); + return JsonObject(data->toObject(), resources); } template