diff --git a/extras/tests/JsonDocument/MemberProxy.cpp b/extras/tests/JsonDocument/MemberProxy.cpp index 816054f4..e3e0bec8 100644 --- a/extras/tests/JsonDocument/MemberProxy.cpp +++ b/extras/tests/JsonDocument/MemberProxy.cpp @@ -382,6 +382,24 @@ TEST_CASE("Deduplicate keys") { Allocate(sizeofString("example")), }); } + + SECTION("string literals") { + doc[0]["example"] = 1; + doc[1]["example"] = 2; + doc.shrinkToFit(); + + const char* key1 = doc[0].as().begin()->key().c_str(); + const char* key2 = doc[1].as().begin()->key().c_str(); + CHECK(key1 == key2); + + REQUIRE(spy.log() == + AllocatorLog{ + Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), + Reallocate(sizeofPool(), sizeofPool(6)), + Reallocate(sizeofStaticStringPool(), sizeofStaticStringPool(1)), + }); + } } TEST_CASE("MemberProxy under memory constraints") { diff --git a/extras/tests/ResourceManager/saveString.cpp b/extras/tests/ResourceManager/saveString.cpp index 3c7a228b..544b8090 100644 --- a/extras/tests/ResourceManager/saveString.cpp +++ b/extras/tests/ResourceManager/saveString.cpp @@ -19,6 +19,25 @@ static StringNode* saveString(ResourceManager& resources, const char* s, return resources.saveString(adaptString(s, n)); } +TEST_CASE("ResourceManager::saveStaticString()") { + SpyingAllocator spy; + ResourceManager resources(&spy); + + auto a = resources.saveStaticString("hello"); + auto b = resources.saveStaticString("world"); + REQUIRE(a != b); + + auto c = resources.saveStaticString("hello"); + REQUIRE(a == c); + + resources.shrinkToFit(); + REQUIRE(spy.log() == + AllocatorLog{ + Allocate(sizeofStaticStringPool()), + Reallocate(sizeofStaticStringPool(), sizeofStaticStringPool(2)), + }); +} + TEST_CASE("ResourceManager::saveString()") { ResourceManager resources; diff --git a/src/ArduinoJson/Memory/MemoryPool.hpp b/src/ArduinoJson/Memory/MemoryPool.hpp index 531da0a6..79f1bf53 100644 --- a/src/ArduinoJson/Memory/MemoryPool.hpp +++ b/src/ArduinoJson/Memory/MemoryPool.hpp @@ -81,6 +81,14 @@ class MemoryPool { return slots_ + id; } + SlotId find(const T& value) const { + for (SlotId i = 0; i < usage_; i++) { + if (slots_[i] == value) + return i; + } + return NULL_SLOT; + } + void clear() { usage_ = 0; } diff --git a/src/ArduinoJson/Memory/MemoryPoolList.hpp b/src/ArduinoJson/Memory/MemoryPoolList.hpp index 7da08063..14b668c0 100644 --- a/src/ArduinoJson/Memory/MemoryPoolList.hpp +++ b/src/ArduinoJson/Memory/MemoryPoolList.hpp @@ -114,6 +114,15 @@ class MemoryPoolList { return pools_[poolIndex].getSlot(indexInPool); } + SlotId find(const T& value) const { + for (PoolCount i = 0; i < count_; i++) { + SlotId id = pools_[i].find(value); + if (id != NULL_SLOT) + return SlotId(i * ARDUINOJSON_POOL_CAPACITY + id); + } + return NULL_SLOT; + } + void clear(Allocator* allocator) { for (PoolCount i = 0; i < count_; i++) pools_[i].destroy(allocator); diff --git a/src/ArduinoJson/Memory/ResourceManager.hpp b/src/ArduinoJson/Memory/ResourceManager.hpp index 24f1e941..18aa198c 100644 --- a/src/ArduinoJson/Memory/ResourceManager.hpp +++ b/src/ArduinoJson/Memory/ResourceManager.hpp @@ -114,10 +114,14 @@ class ResourceManager { } SlotId saveStaticString(const char* s) { + auto existingSlotId = staticStringsPools_.find(s); + if (existingSlotId != NULL_SLOT) + return existingSlotId; + auto slot = staticStringsPools_.allocSlot(allocator_); - if (!slot) - return NULL_SLOT; - *slot = s; + if (slot) + *slot = s; + return slot.id(); }