From 09c89dcacfa08902a4fb7358d42e97eb82ba2277 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Fri, 23 Aug 2024 15:31:47 +0200 Subject: [PATCH] Store object members with two slots: one for the key and one for the value --- CHANGELOG.md | 5 ++ extras/conf_test/avr.cpp | 2 +- extras/conf_test/linux32.cpp | 2 +- extras/conf_test/linux64.cpp | 2 +- extras/conf_test/win64.cpp | 2 +- extras/tests/JsonArray/remove.cpp | 6 ++ extras/tests/JsonDeserializer/object.cpp | 4 +- extras/tests/JsonDocument/MemberProxy.cpp | 40 +++++++++-- extras/tests/JsonDocument/shrinkToFit.cpp | 2 +- src/ArduinoJson/Array/ArrayData.hpp | 14 +++- src/ArduinoJson/Array/ArrayImpl.hpp | 10 ++- src/ArduinoJson/Collection/CollectionData.hpp | 23 ++----- src/ArduinoJson/Collection/CollectionImpl.hpp | 68 +++++++++---------- src/ArduinoJson/Json/JsonSerializer.hpp | 9 +-- src/ArduinoJson/Json/PrettyJsonSerializer.hpp | 13 ++-- .../Memory/ResourceManagerImpl.hpp | 2 - src/ArduinoJson/MsgPack/MsgPackSerializer.hpp | 1 - src/ArduinoJson/Object/JsonObjectIterator.hpp | 6 +- src/ArduinoJson/Object/JsonPair.hpp | 44 ++++++------ src/ArduinoJson/Object/ObjectData.hpp | 51 +++++++------- src/ArduinoJson/Object/ObjectImpl.hpp | 35 ++++++++-- src/ArduinoJson/Variant/VariantContent.hpp | 4 -- src/ArduinoJson/Variant/VariantData.hpp | 30 +++++--- src/ArduinoJson/Variant/VariantSlot.hpp | 27 ++------ 24 files changed, 229 insertions(+), 173 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b348d564..9d6a4def 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ ArduinoJson: change log ======================= +HEAD +---- + +* Store object members with two slots: one for the key and one for the value + v7.1.0 (2024-06-27) ------ diff --git a/extras/conf_test/avr.cpp b/extras/conf_test/avr.cpp index e0061eaf..4ffff564 100644 --- a/extras/conf_test/avr.cpp +++ b/extras/conf_test/avr.cpp @@ -10,7 +10,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN"); static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE"); -static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 8, +static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 6, "sizeof(VariantSlot)"); void setup() {} diff --git a/extras/conf_test/linux32.cpp b/extras/conf_test/linux32.cpp index a9874efd..ad0c97d5 100644 --- a/extras/conf_test/linux32.cpp +++ b/extras/conf_test/linux32.cpp @@ -8,7 +8,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN"); static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE"); -static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 16, +static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 12, "sizeof(VariantSlot)"); int main() {} diff --git a/extras/conf_test/linux64.cpp b/extras/conf_test/linux64.cpp index 2b533bf7..e03e6cca 100644 --- a/extras/conf_test/linux64.cpp +++ b/extras/conf_test/linux64.cpp @@ -8,7 +8,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN"); static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE"); -static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 24, +static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 16, "sizeof(VariantSlot)"); int main() {} diff --git a/extras/conf_test/win64.cpp b/extras/conf_test/win64.cpp index 2b533bf7..e03e6cca 100644 --- a/extras/conf_test/win64.cpp +++ b/extras/conf_test/win64.cpp @@ -8,7 +8,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN"); static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE"); -static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 24, +static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 16, "sizeof(VariantSlot)"); int main() {} diff --git a/extras/tests/JsonArray/remove.cpp b/extras/tests/JsonArray/remove.cpp index 9cbd90bb..82e3bab0 100644 --- a/extras/tests/JsonArray/remove.cpp +++ b/extras/tests/JsonArray/remove.cpp @@ -68,6 +68,12 @@ TEST_CASE("JsonArray::remove()") { REQUIRE(array[1] == 2); } + SECTION("remove end()") { + array.remove(array.end()); + + REQUIRE(3 == array.size()); + } + SECTION("In a loop") { for (JsonArray::iterator it = array.begin(); it != array.end(); ++it) { if (*it == 2) diff --git a/extras/tests/JsonDeserializer/object.cpp b/extras/tests/JsonDeserializer/object.cpp index 501e87a0..1f980f24 100644 --- a/extras/tests/JsonDeserializer/object.cpp +++ b/extras/tests/JsonDeserializer/object.cpp @@ -308,12 +308,12 @@ TEST_CASE("deserialize JSON object") { REQUIRE(doc["a"] == 2); } - SECTION("NUL in keys") { // we don't support NULs in keys + SECTION("NUL in keys") { DeserializationError err = deserializeJson(doc, "{\"x\\u0000a\":1,\"x\\u0000b\":2}"); REQUIRE(err == DeserializationError::Ok); - REQUIRE(doc.as() == "{\"x\":2}"); + REQUIRE(doc.as() == "{\"x\\u0000a\":1,\"x\\u0000b\":2}"); } } diff --git a/extras/tests/JsonDocument/MemberProxy.cpp b/extras/tests/JsonDocument/MemberProxy.cpp index 8361377c..dc00e4db 100644 --- a/extras/tests/JsonDocument/MemberProxy.cpp +++ b/extras/tests/JsonDocument/MemberProxy.cpp @@ -345,12 +345,12 @@ TEST_CASE("Deduplicate keys") { } TEST_CASE("MemberProxy under memory constraints") { - KillswitchAllocator killswitch; - SpyingAllocator spy(&killswitch); + TimebombAllocator timebomb(1); + SpyingAllocator spy(&timebomb); JsonDocument doc(&spy); - SECTION("key allocation fails") { - killswitch.on(); + SECTION("key slot allocation fails") { + timebomb.setCountdown(0); doc["hello"_s] = "world"; @@ -361,4 +361,36 @@ TEST_CASE("MemberProxy under memory constraints") { AllocateFail(sizeofPool()), }); } + + SECTION("value slot allocation fails") { + timebomb.setCountdown(1); + + // fill the pool entirely, but leave one slot for the key + doc["foo"][ARDUINOJSON_POOL_CAPACITY - 4] = 1; + REQUIRE(doc.overflowed() == false); + + doc["hello"_s] = "world"; + + REQUIRE(doc.is()); + REQUIRE(doc.size() == 1); + REQUIRE(doc.overflowed() == true); + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofPool()), + AllocateFail(sizeofPool()), + }); + } + + SECTION("key string allocation fails") { + timebomb.setCountdown(1); + + doc["hello"_s] = "world"; + + REQUIRE(doc.is()); + REQUIRE(doc.size() == 0); + REQUIRE(doc.overflowed() == true); + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofPool()), + AllocateFail(sizeofString("hello")), + }); + } } diff --git a/extras/tests/JsonDocument/shrinkToFit.cpp b/extras/tests/JsonDocument/shrinkToFit.cpp index 98656db9..91b9d917 100644 --- a/extras/tests/JsonDocument/shrinkToFit.cpp +++ b/extras/tests/JsonDocument/shrinkToFit.cpp @@ -178,7 +178,7 @@ TEST_CASE("JsonDocument::shrinkToFit()") { AllocatorLog{ Allocate(sizeofPool()), Allocate(sizeofString("abcdefg")), - Reallocate(sizeofPool(), sizeofPool(1)), + Reallocate(sizeofPool(), sizeofPool(2)), }); } } diff --git a/src/ArduinoJson/Array/ArrayData.hpp b/src/ArduinoJson/Array/ArrayData.hpp index a35aba5d..c2ae1fb3 100644 --- a/src/ArduinoJson/Array/ArrayData.hpp +++ b/src/ArduinoJson/Array/ArrayData.hpp @@ -10,9 +10,7 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE class ArrayData : public CollectionData { public: - VariantData* addElement(ResourceManager* resources) { - return addSlot(resources).data(); - } + VariantData* addElement(ResourceManager* resources); static VariantData* addElement(ArrayData* array, ResourceManager* resources) { if (!array) @@ -51,6 +49,16 @@ class ArrayData : public CollectionData { array->removeElement(index, resources); } + void remove(iterator it, ResourceManager* resources) { + CollectionData::removeOne(it, resources); + } + + static void remove(ArrayData* array, iterator it, + ResourceManager* resources) { + if (array) + return array->remove(it, resources); + } + private: iterator at(size_t index, const ResourceManager* resources) const; }; diff --git a/src/ArduinoJson/Array/ArrayImpl.hpp b/src/ArduinoJson/Array/ArrayImpl.hpp index 9b96138a..6e64b016 100644 --- a/src/ArduinoJson/Array/ArrayImpl.hpp +++ b/src/ArduinoJson/Array/ArrayImpl.hpp @@ -19,6 +19,14 @@ inline ArrayData::iterator ArrayData::at( return it; } +inline VariantData* ArrayData::addElement(ResourceManager* resources) { + auto slot = resources->allocSlot(); + if (!slot) + return nullptr; + CollectionData::appendOne(slot, resources); + return slot->data(); +} + inline VariantData* ArrayData::getOrAddElement(size_t index, ResourceManager* resources) { auto it = createIterator(resources); @@ -58,7 +66,7 @@ inline bool ArrayData::addValue(T&& value, ResourceManager* resources) { resources->freeSlot(slot); return false; } - addSlot(slot, resources); + CollectionData::appendOne(slot, resources); return true; } diff --git a/src/ArduinoJson/Collection/CollectionData.hpp b/src/ArduinoJson/Collection/CollectionData.hpp index 07e69823..b4960897 100644 --- a/src/ArduinoJson/Collection/CollectionData.hpp +++ b/src/ArduinoJson/Collection/CollectionData.hpp @@ -49,12 +49,6 @@ class CollectionIterator { return *data(); } - const char* key() const; - bool ownsKey() const; - - void setKey(StringNode*); - void setKey(const char*); - VariantData* data() { return reinterpret_cast(slot_); } @@ -99,22 +93,17 @@ class CollectionData { collection->clear(resources); } - void remove(iterator it, ResourceManager* resources); - - static void remove(CollectionData* collection, iterator it, - ResourceManager* resources) { - if (collection) - return collection->remove(it, resources); - } - SlotId head() const { return head_; } - void addSlot(SlotWithId slot, ResourceManager* resources); - protected: - iterator addSlot(ResourceManager*); + void appendOne(SlotWithId slot, const ResourceManager* resources); + void appendPair(SlotWithId key, SlotWithId value, + const ResourceManager* resources); + + void removeOne(iterator it, ResourceManager* resources); + void removePair(iterator it, ResourceManager* resources); private: SlotWithId getPreviousSlot(VariantSlot*, const ResourceManager*) const; diff --git a/src/ArduinoJson/Collection/CollectionImpl.hpp b/src/ArduinoJson/Collection/CollectionImpl.hpp index c7e23a35..e6ae3f22 100644 --- a/src/ArduinoJson/Collection/CollectionImpl.hpp +++ b/src/ArduinoJson/Collection/CollectionImpl.hpp @@ -17,28 +17,6 @@ inline CollectionIterator::CollectionIterator(VariantSlot* slot, SlotId slotId) nextId_ = slot_ ? slot_->next() : NULL_SLOT; } -inline const char* CollectionIterator::key() const { - ARDUINOJSON_ASSERT(slot_ != nullptr); - return slot_->key(); -} - -inline void CollectionIterator::setKey(const char* s) { - ARDUINOJSON_ASSERT(slot_ != nullptr); - ARDUINOJSON_ASSERT(s != nullptr); - return slot_->setKey(s); -} - -inline void CollectionIterator::setKey(StringNode* s) { - ARDUINOJSON_ASSERT(slot_ != nullptr); - ARDUINOJSON_ASSERT(s != nullptr); - return slot_->setKey(s); -} - -inline bool CollectionIterator::ownsKey() const { - ARDUINOJSON_ASSERT(slot_ != nullptr); - return slot_->ownsKey(); -} - inline void CollectionIterator::next(const ResourceManager* resources) { ARDUINOJSON_ASSERT(currentId_ != NULL_SLOT); slot_ = resources->getSlot(nextId_); @@ -47,11 +25,8 @@ inline void CollectionIterator::next(const ResourceManager* resources) { nextId_ = slot_->next(); } -inline CollectionData::iterator CollectionData::addSlot( - ResourceManager* resources) { - auto slot = resources->allocSlot(); - if (!slot) - return {}; +inline void CollectionData::appendOne(SlotWithId slot, + const ResourceManager* resources) { if (tail_ != NULL_SLOT) { auto tail = resources->getSlot(tail_); tail->setNext(slot.id()); @@ -60,18 +35,19 @@ inline CollectionData::iterator CollectionData::addSlot( head_ = slot.id(); tail_ = slot.id(); } - return iterator(slot, slot.id()); } -inline void CollectionData::addSlot(SlotWithId slot, - ResourceManager* resources) { +inline void CollectionData::appendPair(SlotWithId key, SlotWithId value, + const ResourceManager* resources) { + key->setNext(value.id()); + if (tail_ != NULL_SLOT) { auto tail = resources->getSlot(tail_); - tail->setNext(slot.id()); - tail_ = slot.id(); + tail->setNext(key.id()); + tail_ = value.id(); } else { - head_ = slot.id(); - tail_ = slot.id(); + head_ = key.id(); + tail_ = value.id(); } } @@ -95,14 +71,14 @@ inline SlotWithId CollectionData::getPreviousSlot( while (currentId != NULL_SLOT) { auto currentSlot = resources->getSlot(currentId); if (currentSlot == target) - return prev; + break; prev = SlotWithId(currentSlot, currentId); currentId = currentSlot->next(); } - return SlotWithId(); + return prev; } -inline void CollectionData::remove(iterator it, ResourceManager* resources) { +inline void CollectionData::removeOne(iterator it, ResourceManager* resources) { if (it.done()) return; auto curr = it.slot_; @@ -117,6 +93,24 @@ inline void CollectionData::remove(iterator it, ResourceManager* resources) { resources->freeSlot({it.slot_, it.currentId_}); } +inline void CollectionData::removePair(ObjectData::iterator it, + ResourceManager* resources) { + if (it.done()) + return; + + auto keySlot = it.slot_; + + auto valueId = it.nextId_; + auto valueSlot = resources->getSlot(valueId); + + // remove value slot + keySlot->setNext(valueSlot->next()); + resources->freeSlot({valueSlot, valueId}); + + // remove key slot + removeOne(it, resources); +} + inline size_t CollectionData::nesting(const ResourceManager* resources) const { size_t maxChildNesting = 0; for (auto it = createIterator(resources); !it.done(); it.next(resources)) { diff --git a/src/ArduinoJson/Json/JsonSerializer.hpp b/src/ArduinoJson/Json/JsonSerializer.hpp index 189ae33f..4f3c16e6 100644 --- a/src/ArduinoJson/Json/JsonSerializer.hpp +++ b/src/ArduinoJson/Json/JsonSerializer.hpp @@ -44,17 +44,18 @@ class JsonSerializer : public VariantDataVisitor { auto slotId = object.head(); + bool isKey = true; + while (slotId != NULL_SLOT) { auto slot = resources_->getSlot(slotId); - - formatter_.writeString(slot->key()); - write(':'); slot->data()->accept(*this); slotId = slot->next(); if (slotId != NULL_SLOT) - write(','); + write(isKey ? ':' : ','); + + isKey = !isKey; } write('}'); diff --git a/src/ArduinoJson/Json/PrettyJsonSerializer.hpp b/src/ArduinoJson/Json/PrettyJsonSerializer.hpp index ab049ad6..fd2c99fa 100644 --- a/src/ArduinoJson/Json/PrettyJsonSerializer.hpp +++ b/src/ArduinoJson/Json/PrettyJsonSerializer.hpp @@ -45,14 +45,17 @@ class PrettyJsonSerializer : public JsonSerializer { if (!it.done()) { base::write("{\r\n"); nesting_++; + bool isKey = true; while (!it.done()) { - indent(); - base::visit(it.key()); - base::write(": "); + if (isKey) + indent(); it->accept(*this); - it.next(base::resources_); - base::write(it.done() ? "\r\n" : ",\r\n"); + if (isKey) + base::write(": "); + else + base::write(it.done() ? "\r\n" : ",\r\n"); + isKey = !isKey; } nesting_--; indent(); diff --git a/src/ArduinoJson/Memory/ResourceManagerImpl.hpp b/src/ArduinoJson/Memory/ResourceManagerImpl.hpp index 136dc2dd..c9f004d8 100644 --- a/src/ArduinoJson/Memory/ResourceManagerImpl.hpp +++ b/src/ArduinoJson/Memory/ResourceManagerImpl.hpp @@ -11,8 +11,6 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE inline void ResourceManager::freeSlot(SlotWithId slot) { - if (slot->ownsKey()) - dereferenceString(slot->key()); slot->data()->setNull(this); variantPools_.freeSlot(slot); } diff --git a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp index ce5f66a2..e34959cf 100644 --- a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp @@ -84,7 +84,6 @@ class MsgPackSerializer : public VariantDataVisitor { auto slotId = object.head(); while (slotId != NULL_SLOT) { auto slot = resources_->getSlot(slotId); - visit(slot->key()); slot->data()->accept(*this); slotId = slot->next(); } diff --git a/src/ArduinoJson/Object/JsonObjectIterator.hpp b/src/ArduinoJson/Object/JsonObjectIterator.hpp index b849e62a..74f75bfc 100644 --- a/src/ArduinoJson/Object/JsonObjectIterator.hpp +++ b/src/ArduinoJson/Object/JsonObjectIterator.hpp @@ -34,7 +34,8 @@ class JsonObjectIterator { } JsonObjectIterator& operator++() { - iterator_.next(resources_); + iterator_.next(resources_); // key + iterator_.next(resources_); // value return *this; } @@ -69,7 +70,8 @@ class JsonObjectConstIterator { } JsonObjectConstIterator& operator++() { - iterator_.next(resources_); + iterator_.next(resources_); // key + iterator_.next(resources_); // value return *this; } diff --git a/src/ArduinoJson/Object/JsonPair.hpp b/src/ArduinoJson/Object/JsonPair.hpp index 4d621a16..923bd430 100644 --- a/src/ArduinoJson/Object/JsonPair.hpp +++ b/src/ArduinoJson/Object/JsonPair.hpp @@ -16,27 +16,27 @@ class JsonPair { public: // INTERNAL USE ONLY JsonPair(detail::ObjectData::iterator iterator, - detail::ResourceManager* resources) - : iterator_(iterator), resources_(resources) {} + detail::ResourceManager* resources) { + if (!iterator.done()) { + key_ = iterator->asString(); + iterator.next(resources); + value_ = JsonVariant(iterator.data(), resources); + } + } // Returns the key. JsonString key() const { - if (!iterator_.done()) - return JsonString(iterator_.key(), iterator_.ownsKey() - ? JsonString::Copied - : JsonString::Linked); - else - return JsonString(); + return key_; } // Returns the value. JsonVariant value() { - return JsonVariant(iterator_.data(), resources_); + return value_; } private: - detail::ObjectData::iterator iterator_; - detail::ResourceManager* resources_; + JsonString key_; + JsonVariant value_; }; // A read-only key-value pair. @@ -44,27 +44,27 @@ class JsonPair { class JsonPairConst { public: JsonPairConst(detail::ObjectData::iterator iterator, - const detail::ResourceManager* resources) - : iterator_(iterator), resources_(resources) {} + const detail::ResourceManager* resources) { + if (!iterator.done()) { + key_ = iterator->asString(); + iterator.next(resources); + value_ = JsonVariantConst(iterator.data(), resources); + } + } // Returns the key. JsonString key() const { - if (!iterator_.done()) - return JsonString(iterator_.key(), iterator_.ownsKey() - ? JsonString::Copied - : JsonString::Linked); - else - return JsonString(); + return key_; } // Returns the value. JsonVariantConst value() const { - return JsonVariantConst(iterator_.data(), resources_); + return value_; } private: - detail::ObjectData::iterator iterator_; - const detail::ResourceManager* resources_; + JsonString key_; + JsonVariantConst value_; }; ARDUINOJSON_END_PUBLIC_NAMESPACE diff --git a/src/ArduinoJson/Object/ObjectData.hpp b/src/ArduinoJson/Object/ObjectData.hpp index 40e743ee..7290e93c 100644 --- a/src/ArduinoJson/Object/ObjectData.hpp +++ b/src/ArduinoJson/Object/ObjectData.hpp @@ -10,34 +10,8 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE class ObjectData : public CollectionData { public: - VariantData* addMember(StringNode* key, ResourceManager* resources) { - ARDUINOJSON_ASSERT(key != nullptr); - auto it = addSlot(resources); - if (it.done()) - return nullptr; - - it.setKey(key); - return it.data(); - } - - template - VariantData* addMember(TAdaptedString key, ResourceManager* resources) { - ARDUINOJSON_ASSERT(!key.isNull()); - if (key.isLinked()) { - auto it = addSlot(resources); - if (!it.done()) - it.setKey(key.data()); - return it.data(); - } else { - auto storedKey = resources->saveString(key); - if (!storedKey) - return nullptr; - auto it = addSlot(resources); - if (!it.done()) - it.setKey(storedKey); - return it.data(); - } - } + template // also works with StringNode* + VariantData* addMember(TAdaptedString key, ResourceManager* resources); template VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources); @@ -65,6 +39,27 @@ class ObjectData : public CollectionData { obj->removeMember(key, resources); } + void remove(iterator it, ResourceManager* resources) { + CollectionData::removePair(it, resources); + } + + static void remove(ObjectData* obj, ObjectData::iterator it, + ResourceManager* resources) { + if (!obj) + return; + obj->remove(it, resources); + } + + size_t size(const ResourceManager* resources) const { + return CollectionData::size(resources) / 2; + } + + static size_t size(const ObjectData* obj, const ResourceManager* resources) { + if (!obj) + return 0; + return obj->size(resources); + } + private: template iterator findKey(TAdaptedString key, const ResourceManager* resources) const; diff --git a/src/ArduinoJson/Object/ObjectImpl.hpp b/src/ArduinoJson/Object/ObjectImpl.hpp index eed39cad..67c1c089 100644 --- a/src/ArduinoJson/Object/ObjectImpl.hpp +++ b/src/ArduinoJson/Object/ObjectImpl.hpp @@ -12,15 +12,19 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template inline VariantData* ObjectData::getMember( TAdaptedString key, const ResourceManager* resources) const { - return findKey(key, resources).data(); + auto it = findKey(key, resources); + if (it.done()) + return nullptr; + it.next(resources); + return it.data(); } template VariantData* ObjectData::getOrAddMember(TAdaptedString key, ResourceManager* resources) { - auto it = findKey(key, resources); - if (!it.done()) - return it.data(); + auto data = getMember(key, resources); + if (data) + return data; return addMember(key, resources); } @@ -29,9 +33,11 @@ inline ObjectData::iterator ObjectData::findKey( TAdaptedString key, const ResourceManager* resources) const { if (key.isNull()) return iterator(); + bool isKey = true; for (auto it = createIterator(resources); !it.done(); it.next(resources)) { - if (stringEquals(key, adaptString(it.key()))) + if (isKey && stringEquals(key, adaptString(it->asString()))) return it; + isKey = !isKey; } return iterator(); } @@ -42,4 +48,23 @@ inline void ObjectData::removeMember(TAdaptedString key, remove(findKey(key, resources), resources); } +template +inline VariantData* ObjectData::addMember(TAdaptedString key, + ResourceManager* resources) { + auto keySlot = resources->allocSlot(); + if (!keySlot) + return nullptr; + + auto valueSlot = resources->allocSlot(); + if (!valueSlot) + return nullptr; + + if (!keySlot->data()->setString(key, resources)) + return nullptr; + + CollectionData::appendPair(keySlot, valueSlot, resources); + + return valueSlot->data(); +} + ARDUINOJSON_END_PRIVATE_NAMESPACE diff --git a/src/ArduinoJson/Variant/VariantContent.hpp b/src/ArduinoJson/Variant/VariantContent.hpp index fa2cb50e..cff5e09c 100644 --- a/src/ArduinoJson/Variant/VariantContent.hpp +++ b/src/ArduinoJson/Variant/VariantContent.hpp @@ -14,8 +14,6 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE enum { - VALUE_MASK = 0x7F, - OWNED_VALUE_BIT = 0x01, VALUE_IS_NULL = 0, VALUE_IS_RAW_STRING = 0x03, @@ -34,8 +32,6 @@ enum { COLLECTION_MASK = 0x60, VALUE_IS_OBJECT = 0x20, VALUE_IS_ARRAY = 0x40, - - OWNED_KEY_BIT = 0x80 }; union VariantContent { diff --git a/src/ArduinoJson/Variant/VariantData.hpp b/src/ArduinoJson/Variant/VariantData.hpp index 5a2974ba..41d98a3f 100644 --- a/src/ArduinoJson/Variant/VariantData.hpp +++ b/src/ArduinoJson/Variant/VariantData.hpp @@ -410,20 +410,29 @@ class VariantData { } template - void setString(TAdaptedString value, ResourceManager* resources) { + bool setString(TAdaptedString value, ResourceManager* resources) { setNull(resources); if (value.isNull()) - return; + return false; if (value.isLinked()) { setLinkedString(value.data()); - return; + return true; } auto dup = resources->saveString(value); - if (dup) + if (dup) { setOwnedString(dup); + return true; + } + + return false; + } + + bool setString(StringNode* s, ResourceManager*) { + setOwnedString(s); + return true; } template @@ -447,7 +456,13 @@ class VariantData { } size_t size(const ResourceManager* resources) const { - return isCollection() ? content_.asCollection.size(resources) : 0; + if (isObject()) + return content_.asObject.size(resources); + + if (isArray()) + return content_.asArray.size(resources); + + return 0; } static size_t size(const VariantData* var, const ResourceManager* resources) { @@ -489,7 +504,7 @@ class VariantData { } uint8_t type() const { - return flags_ & VALUE_MASK; + return flags_; } private: @@ -503,8 +518,7 @@ class VariantData { } void setType(uint8_t t) { - flags_ &= OWNED_KEY_BIT; - flags_ |= t; + flags_ = t; } }; diff --git a/src/ArduinoJson/Variant/VariantSlot.hpp b/src/ArduinoJson/Variant/VariantSlot.hpp index 35107fd2..5409c458 100644 --- a/src/ArduinoJson/Variant/VariantSlot.hpp +++ b/src/ArduinoJson/Variant/VariantSlot.hpp @@ -20,7 +20,6 @@ class VariantSlot { VariantContent content_; uint8_t flags_; SlotId next_; - const char* key_; public: // Placement new @@ -30,7 +29,9 @@ class VariantSlot { static void operator delete(void*, void*) noexcept {} - VariantSlot() : flags_(0), next_(NULL_SLOT), key_(0) {} + VariantSlot() : flags_(0), next_(NULL_SLOT) { + (void)flags_; // HACK: suppress Clang warning "private field is not used" + } VariantData* data() { return reinterpret_cast(&content_); @@ -47,26 +48,6 @@ class VariantSlot { void setNext(SlotId slot) { next_ = slot; } - - void setKey(const char* k) { - ARDUINOJSON_ASSERT(k); - flags_ &= VALUE_MASK; - key_ = k; - } - - void setKey(StringNode* k) { - ARDUINOJSON_ASSERT(k); - flags_ |= OWNED_KEY_BIT; - key_ = k->data; - } - - const char* key() const { - return key_; - } - - bool ownsKey() const { - return (flags_ & OWNED_KEY_BIT) != 0; - } }; inline VariantData* slotData(VariantSlot* slot) { @@ -80,7 +61,7 @@ constexpr size_t sizeofArray(size_t n) { // Returns the size (in bytes) of an object with n members. constexpr size_t sizeofObject(size_t n) { - return n * sizeof(VariantSlot); + return 2 * n * sizeof(VariantSlot); } ARDUINOJSON_END_PRIVATE_NAMESPACE