From 67bbb4c90db640869411c3b2a5946c497865046e Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Fri, 23 Jun 2023 19:07:41 +0200 Subject: [PATCH] Manage resources in `CollectionData` --- src/ArduinoJson/Array/JsonArrayConst.hpp | 3 +- src/ArduinoJson/Collection/CollectionData.hpp | 52 +++++-- .../Collection/CollectionFunctions.hpp | 57 ++------ src/ArduinoJson/Collection/CollectionImpl.hpp | 131 ++++++++++++++++-- src/ArduinoJson/Json/JsonDeserializer.hpp | 16 +-- .../MsgPack/MsgPackDeserializer.hpp | 9 +- src/ArduinoJson/Object/JsonObject.hpp | 5 - src/ArduinoJson/Variant/VariantContent.hpp | 2 + src/ArduinoJson/Variant/VariantData.hpp | 29 +--- 9 files changed, 196 insertions(+), 108 deletions(-) diff --git a/src/ArduinoJson/Array/JsonArrayConst.hpp b/src/ArduinoJson/Array/JsonArrayConst.hpp index fb360423..ecce75fc 100644 --- a/src/ArduinoJson/Array/JsonArrayConst.hpp +++ b/src/ArduinoJson/Array/JsonArrayConst.hpp @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include @@ -51,7 +52,7 @@ class JsonArrayConst : public detail::VariantOperators { // Returns the element at the specified index. // https://arduinojson.org/v6/api/jsonarrayconst/subscript/ FORCE_INLINE JsonVariantConst operator[](size_t index) const { - return JsonVariantConst(data_ ? slotData(data_->get(index)) : 0); + return JsonVariantConst(collectionGetElement(data_, index)); } operator JsonVariantConst() const { diff --git a/src/ArduinoJson/Collection/CollectionData.hpp b/src/ArduinoJson/Collection/CollectionData.hpp index 354d38dc..141e2bd2 100644 --- a/src/ArduinoJson/Collection/CollectionData.hpp +++ b/src/ArduinoJson/Collection/CollectionData.hpp @@ -15,16 +15,46 @@ class VariantData; class VariantSlot; class CollectionData { - VariantSlot* head_; - VariantSlot* tail_; + VariantSlot* head_ = 0; + VariantSlot* tail_ = 0; public: - void clear(); + // Placement new + static void* operator new(size_t, void* p) noexcept { + return p; + } + + static void operator delete(void*, void*) noexcept {} + size_t memoryUsage() const; size_t size() const; - void add(VariantSlot*); - void remove(VariantSlot* slot); + VariantData* addElement(ResourceManager* resources); + + VariantData* addMember(StringNode* key, ResourceManager* resources); + + template + VariantData* addMember(TAdaptedString key, ResourceManager* resources); + + void clear(ResourceManager* resources); + bool copyFrom(const CollectionData& src, ResourceManager* resources); + + VariantData* getOrAddElement(size_t index, ResourceManager* resources); + + VariantData* getElement(size_t index) const; + + template + VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources); + + template + VariantData* getMember(TAdaptedString key) const; + + void removeSlot(VariantSlot* slot, ResourceManager* resources); + + void removeElement(size_t index, ResourceManager* resources); + + template + void removeMember(TAdaptedString key, ResourceManager* resources); VariantSlot* head() const { return head_; @@ -32,13 +62,15 @@ class CollectionData { void movePointers(ptrdiff_t variantDistance); - VariantSlot* get(size_t index) const; + private: + void addSlot(VariantSlot*); + + VariantSlot* getPreviousSlot(VariantSlot*) const; + + VariantSlot* getSlot(size_t index) const; template - VariantSlot* get(TAdaptedString key) const; - - private: - VariantSlot* getPrevious(VariantSlot*) const; + VariantSlot* getSlot(TAdaptedString key) const; }; inline const VariantData* collectionToVariant( diff --git a/src/ArduinoJson/Collection/CollectionFunctions.hpp b/src/ArduinoJson/Collection/CollectionFunctions.hpp index 08dcad65..80db6bb0 100644 --- a/src/ArduinoJson/Collection/CollectionFunctions.hpp +++ b/src/ArduinoJson/Collection/CollectionFunctions.hpp @@ -12,39 +12,20 @@ inline VariantData* collectionAddElement(CollectionData* array, ResourceManager* resources) { if (!array) return nullptr; - auto slot = resources->allocVariant(); - if (!slot) - return nullptr; - array->add(slot); - return slot->data(); + return array->addElement(resources); } template inline VariantData* collectionAddMember(CollectionData* obj, TAdaptedString key, ResourceManager* resources) { - ARDUINOJSON_ASSERT(!key.isNull()); ARDUINOJSON_ASSERT(obj != nullptr); - auto slot = resources->allocVariant(); - if (!slot) - return nullptr; - if (key.isLinked()) - slot->setKey(key.data()); - else { - auto storedKey = resources->saveString(key); - if (!storedKey) - return nullptr; - slot->setKey(storedKey); - } - obj->add(slot); - return slot->data(); + return obj->addMember(key, resources); } inline void collectionClear(CollectionData* c, ResourceManager* resources) { if (!c) return; - for (auto slot = c->head(); slot; slot = slot->next()) - slotRelease(slot, resources); - c->clear(); + c->clear(resources); } inline bool collectionCopy(CollectionData* dst, const CollectionData* src, @@ -52,21 +33,14 @@ inline bool collectionCopy(CollectionData* dst, const CollectionData* src, if (!dst || !src) return false; - collectionClear(dst, resources); + return dst->copyFrom(*src, resources); +} - for (VariantSlot* s = src->head(); s; s = s->next()) { - VariantData* var; - if (s->key() != 0) { - JsonString key(s->key(), - s->ownsKey() ? JsonString::Copied : JsonString::Linked); - var = collectionAddMember(dst, adaptString(key), resources); - } else { - var = collectionAddElement(dst, resources); - } - if (!variantCopyFrom(var, s->data(), resources)) - return false; - } - return true; +inline VariantData* collectionGetElement(const CollectionData* obj, + size_t index) { + if (!obj) + return nullptr; + return obj->getElement(index); } template @@ -74,22 +48,21 @@ inline VariantData* collectionGetMember(const CollectionData* obj, TAdaptedString key) { if (!obj) return nullptr; - return slotData(obj->get(key)); + return obj->getMember(key); } inline void collectionRemove(CollectionData* data, VariantSlot* slot, ResourceManager* resources) { - if (!data || !slot) + if (!data) return; - data->remove(slot); - slotRelease(slot, resources); + data->removeSlot(slot, resources); } inline void collectionRemoveElement(CollectionData* array, size_t index, ResourceManager* resources) { if (!array) return; - collectionRemove(array, array->get(index), resources); + array->removeElement(index, resources); } template @@ -97,7 +70,7 @@ inline void collectionRemoveMember(CollectionData* obj, TAdaptedString key, ResourceManager* resources) { if (!obj) return; - collectionRemove(obj, obj->get(key), resources); + obj->removeMember(key, resources); } ARDUINOJSON_END_PRIVATE_NAMESPACE diff --git a/src/ArduinoJson/Collection/CollectionImpl.hpp b/src/ArduinoJson/Collection/CollectionImpl.hpp index 45433568..42097ffb 100644 --- a/src/ArduinoJson/Collection/CollectionImpl.hpp +++ b/src/ArduinoJson/Collection/CollectionImpl.hpp @@ -11,7 +11,7 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -inline void CollectionData::add(VariantSlot* slot) { +inline void CollectionData::addSlot(VariantSlot* slot) { ARDUINOJSON_ASSERT(slot != nullptr); if (tail_) { @@ -23,13 +23,110 @@ inline void CollectionData::add(VariantSlot* slot) { } } -inline void CollectionData::clear() { +inline VariantData* CollectionData::addElement(ResourceManager* resources) { + auto slot = resources->allocVariant(); + if (!slot) + return nullptr; + addSlot(slot); + return slot->data(); +} + +inline VariantData* CollectionData::addMember(StringNode* key, + ResourceManager* resources) { + ARDUINOJSON_ASSERT(key != nullptr); + auto slot = resources->allocVariant(); + if (!slot) + return nullptr; + + slot->setKey(key); + addSlot(slot); + return slot->data(); +} + +template +inline VariantData* CollectionData::addMember(TAdaptedString key, + ResourceManager* resources) { + ARDUINOJSON_ASSERT(!key.isNull()); + auto slot = resources->allocVariant(); + if (!slot) + return nullptr; + if (key.isLinked()) + slot->setKey(key.data()); + else { + auto storedKey = resources->saveString(key); + if (!storedKey) + return nullptr; + slot->setKey(storedKey); + } + addSlot(slot); + return slot->data(); +} + +template +inline VariantData* CollectionData::getMember(TAdaptedString key) const { + return slotData(getSlot(key)); +} + +inline VariantData* CollectionData::getOrAddElement( + size_t index, ResourceManager* resources) { + VariantSlot* slot = head_; + while (slot && index > 0) { + slot = slot->next(); + index--; + } + if (!slot) + index++; + while (index > 0) { + slot = resources->allocVariant(); + if (!slot) + return nullptr; + addSlot(slot); + index--; + } + return slot->data(); +} + +template +VariantData* CollectionData::getOrAddMember(TAdaptedString key, + ResourceManager* resources) { + auto slot = getSlot(key); + if (slot) + return slot->data(); + return addMember(key, resources); +} + +inline VariantData* CollectionData::getElement(size_t index) const { + return slotData(getSlot(index)); +} + +inline void CollectionData::clear(ResourceManager* resources) { + for (auto slot = head_; slot; slot = slot->next()) + slotRelease(slot, resources); head_ = 0; tail_ = 0; } +inline bool CollectionData::copyFrom(const CollectionData& src, + ResourceManager* resources) { + clear(resources); + + for (VariantSlot* s = src.head(); s; s = s->next()) { + VariantData* var; + if (s->key() != 0) { + JsonString key(s->key(), + s->ownsKey() ? JsonString::Copied : JsonString::Linked); + var = addMember(adaptString(key), resources); + } else { + var = addElement(resources); + } + if (!variantCopyFrom(var, s->data(), resources)) + return false; + } + return true; +} + template -inline VariantSlot* CollectionData::get(TAdaptedString key) const { +inline VariantSlot* CollectionData::getSlot(TAdaptedString key) const { if (key.isNull()) return 0; VariantSlot* slot = head_; @@ -41,13 +138,13 @@ inline VariantSlot* CollectionData::get(TAdaptedString key) const { return slot; } -inline VariantSlot* CollectionData::get(size_t index) const { +inline VariantSlot* CollectionData::getSlot(size_t index) const { if (!head_) return 0; return head_->next(index); } -inline VariantSlot* CollectionData::getPrevious(VariantSlot* target) const { +inline VariantSlot* CollectionData::getPreviousSlot(VariantSlot* target) const { VariantSlot* current = head_; while (current) { VariantSlot* next = current->next(); @@ -58,9 +155,11 @@ inline VariantSlot* CollectionData::getPrevious(VariantSlot* target) const { return 0; } -inline void CollectionData::remove(VariantSlot* slot) { - ARDUINOJSON_ASSERT(slot != nullptr); - VariantSlot* prev = getPrevious(slot); +inline void CollectionData::removeSlot(VariantSlot* slot, + ResourceManager* resources) { + if (!slot) + return; + VariantSlot* prev = getPreviousSlot(slot); VariantSlot* next = slot->next(); if (prev) prev->setNext(next); @@ -68,6 +167,18 @@ inline void CollectionData::remove(VariantSlot* slot) { head_ = next; if (!next) tail_ = prev; + slotRelease(slot, resources); +} + +inline void CollectionData::removeElement(size_t index, + ResourceManager* resources) { + removeSlot(getSlot(index), resources); +} + +template +inline void CollectionData::removeMember(TAdaptedString key, + ResourceManager* resources) { + removeSlot(getSlot(key), resources); } inline size_t CollectionData::memoryUsage() const { @@ -128,10 +239,10 @@ inline bool arrayEquals(const CollectionData* lhs, const CollectionData* rhs) { inline bool objectEquals(const CollectionData& lhs, const CollectionData& rhs) { size_t count = 0; for (auto a = lhs.head(); a; a = a->next()) { - auto b = rhs.get(adaptString(a->key())); + auto b = rhs.getMember(adaptString(a->key())); if (!b) return false; - if (compare(a->data(), b->data()) != COMPARE_RESULT_EQUAL) + if (compare(a->data(), b) != COMPARE_RESULT_EQUAL) return false; count++; } diff --git a/src/ArduinoJson/Json/JsonDeserializer.hpp b/src/ArduinoJson/Json/JsonDeserializer.hpp index a585564f..962e2536 100644 --- a/src/ArduinoJson/Json/JsonDeserializer.hpp +++ b/src/ArduinoJson/Json/JsonDeserializer.hpp @@ -273,25 +273,21 @@ class JsonDeserializer { TFilter memberFilter = filter[key.c_str()]; if (memberFilter.allow()) { - VariantSlot* slot = object.get(adaptString(key.c_str())); - if (!slot) { + auto member = object.getMember(adaptString(key.c_str())); + if (!member) { // Save key in memory pool. auto savedKey = stringBuilder_.save(); // Allocate slot in object - slot = resources_->allocVariant(); - if (!slot) + member = object.addMember(savedKey, resources_); + if (!member) return DeserializationError::NoMemory; - - slot->setKey(savedKey); - object.add(slot); } else { - slot->data()->setNull(resources_); + member->setNull(resources_); } // Parse value - err = - parseVariant(*slot->data(), memberFilter, nestingLimit.decrement()); + err = parseVariant(*member, memberFilter, nestingLimit.decrement()); if (err) return err; } else { diff --git a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp index 7fe8e602..a152dcad 100644 --- a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -495,14 +495,9 @@ class MsgPackDeserializer { // Save key in memory pool. auto savedKey = stringBuilder_.save(); - VariantSlot* slot = resources_->allocVariant(); - if (!slot) + member = object->addMember(savedKey, resources_); + if (!member) return DeserializationError::NoMemory; - - slot->setKey(savedKey); - object->add(slot); - - member = slot->data(); } else { member = 0; } diff --git a/src/ArduinoJson/Object/JsonObject.hpp b/src/ArduinoJson/Object/JsonObject.hpp index 7b528f83..56ce3a65 100644 --- a/src/ArduinoJson/Object/JsonObject.hpp +++ b/src/ArduinoJson/Object/JsonObject.hpp @@ -201,11 +201,6 @@ class JsonObject : public detail::VariantOperators { return detail::collectionToVariant(data_); } - template - void removeMember(TAdaptedString key) const { - collectionRemove(data_, data_->get(key), resources_); - } - detail::CollectionData* data_; detail::ResourceManager* resources_; }; diff --git a/src/ArduinoJson/Variant/VariantContent.hpp b/src/ArduinoJson/Variant/VariantContent.hpp index 4e7d97bb..ab0c68fd 100644 --- a/src/ArduinoJson/Variant/VariantContent.hpp +++ b/src/ArduinoJson/Variant/VariantContent.hpp @@ -38,6 +38,8 @@ enum { }; union VariantContent { + VariantContent() {} + JsonFloat asFloat; bool asBoolean; JsonUInt asUnsignedInteger; diff --git a/src/ArduinoJson/Variant/VariantData.hpp b/src/ArduinoJson/Variant/VariantData.hpp index dcd437fd..1c0705a4 100644 --- a/src/ArduinoJson/Variant/VariantData.hpp +++ b/src/ArduinoJson/Variant/VariantData.hpp @@ -211,7 +211,7 @@ class VariantData { auto array = asArray(); if (!array) return nullptr; - return slotData(array->get(index)); + return array->getElement(index); } template @@ -219,28 +219,14 @@ class VariantData { auto object = asObject(); if (!object) return nullptr; - return slotData(object->get(key)); + return object->getMember(key); } VariantData* getOrAddElement(size_t index, ResourceManager* resources) { auto array = isNull() ? &toArray() : asArray(); if (!array) return nullptr; - VariantSlot* slot = array->head(); - while (slot && index > 0) { - slot = slot->next(); - index--; - } - if (!slot) - index++; - while (index > 0) { - slot = resources->allocVariant(); - if (!slot) - return nullptr; - array->add(slot); - index--; - } - return slot->data(); + return array->getOrAddElement(index, resources); } template @@ -250,10 +236,7 @@ class VariantData { auto obj = isNull() ? &toObject() : asObject(); if (!obj) return nullptr; - auto slot = obj->get(key); - if (slot) - return slot->data(); - return collectionAddMember(obj, key, resources); + return obj->getOrAddMember(key, resources); } bool isArray() const { @@ -446,7 +429,7 @@ class VariantData { CollectionData& toArray() { setType(VALUE_IS_ARRAY); - content_.asCollection.clear(); + new (&content_.asCollection) CollectionData(); return content_.asCollection; } @@ -457,7 +440,7 @@ class VariantData { CollectionData& toObject() { setType(VALUE_IS_OBJECT); - content_.asCollection.clear(); + new (&content_.asCollection) CollectionData(); return content_.asCollection; }