From 08b240059222eea545fae5218b5df2edf647d354 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Mon, 24 Feb 2025 17:48:37 +0100 Subject: [PATCH] Store static string in a dedicated pool --- extras/tests/Deprecated/BasicJsonDocument.cpp | 2 +- extras/tests/Helpers/Allocators.hpp | 6 +++++ extras/tests/JsonArray/add.cpp | 1 + .../JsonDeserializer/destination_types.cpp | 4 ++- extras/tests/JsonDeserializer/filter.cpp | 8 +++--- extras/tests/JsonDocument/ElementProxy.cpp | 1 + extras/tests/JsonDocument/MemberProxy.cpp | 9 +++++-- extras/tests/JsonDocument/add.cpp | 1 + extras/tests/JsonDocument/constructor.cpp | 2 ++ extras/tests/JsonDocument/set.cpp | 4 ++- extras/tests/JsonDocument/shrinkToFit.cpp | 19 +++++++++++--- extras/tests/JsonDocument/subscript.cpp | 1 + extras/tests/JsonObject/set.cpp | 22 ++++------------ extras/tests/JsonObject/subscript.cpp | 18 ++++++++----- extras/tests/JsonVariant/copy.cpp | 6 +++-- extras/tests/JsonVariant/set.cpp | 8 ++++-- .../MsgPackDeserializer/destination_types.cpp | 4 ++- src/ArduinoJson/Memory/MemoryPool.hpp | 5 ++++ src/ArduinoJson/Memory/ResourceManager.hpp | 17 ++++++++++++ src/ArduinoJson/Object/JsonPair.hpp | 4 +-- src/ArduinoJson/Object/ObjectImpl.hpp | 2 +- src/ArduinoJson/Variant/ConverterImpl.hpp | 4 +-- src/ArduinoJson/Variant/VariantContent.hpp | 3 --- src/ArduinoJson/Variant/VariantData.hpp | 19 ++++++-------- src/ArduinoJson/Variant/VariantImpl.hpp | 26 ++++++++++++++++--- 25 files changed, 133 insertions(+), 63 deletions(-) diff --git a/extras/tests/Deprecated/BasicJsonDocument.cpp b/extras/tests/Deprecated/BasicJsonDocument.cpp index 788fc009..73049212 100644 --- a/extras/tests/Deprecated/BasicJsonDocument.cpp +++ b/extras/tests/Deprecated/BasicJsonDocument.cpp @@ -54,7 +54,7 @@ TEST_CASE("BasicJsonDocument") { doc["hello"] = "world"; auto copy = doc; REQUIRE(copy.as() == "{\"hello\":\"world\"}"); - REQUIRE(allocatorLog == "AA"); + REQUIRE(allocatorLog == "AAAA"); } SECTION("capacity") { diff --git a/extras/tests/Helpers/Allocators.hpp b/extras/tests/Helpers/Allocators.hpp index 17e05cab..6b65ad01 100644 --- a/extras/tests/Helpers/Allocators.hpp +++ b/extras/tests/Helpers/Allocators.hpp @@ -275,6 +275,12 @@ inline size_t sizeofPool( return MemoryPool::slotsToBytes(n); } +inline size_t sizeofStaticStringPool( + ArduinoJson::detail::SlotCount n = ARDUINOJSON_POOL_CAPACITY) { + using namespace ArduinoJson::detail; + return MemoryPool::slotsToBytes(n); +} + inline size_t sizeofStringBuffer(size_t iteration = 1) { // returns 31, 63, 127, 255, etc. auto capacity = ArduinoJson::detail::StringBuilder::initialCapacity; diff --git a/extras/tests/JsonArray/add.cpp b/extras/tests/JsonArray/add.cpp index 0983e3bd..bb64ef06 100644 --- a/extras/tests/JsonArray/add.cpp +++ b/extras/tests/JsonArray/add.cpp @@ -56,6 +56,7 @@ TEST_CASE("JsonArray::add(T)") { REQUIRE(array[0].is() == false); REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), }); } diff --git a/extras/tests/JsonDeserializer/destination_types.cpp b/extras/tests/JsonDeserializer/destination_types.cpp index 0f96b23e..2a51c411 100644 --- a/extras/tests/JsonDeserializer/destination_types.cpp +++ b/extras/tests/JsonDeserializer/destination_types.cpp @@ -104,6 +104,8 @@ TEST_CASE("deserializeJson(MemberProxy)") { REQUIRE(err == DeserializationError::Ok); REQUIRE(doc.as() == "{\"hello\":\"world\",\"value\":[42]}"); - REQUIRE(spy.log() == AllocatorLog{}); + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofStaticStringPool()), + }); } } diff --git a/extras/tests/JsonDeserializer/filter.cpp b/extras/tests/JsonDeserializer/filter.cpp index 4f9d9b9b..d57ea962 100644 --- a/extras/tests/JsonDeserializer/filter.cpp +++ b/extras/tests/JsonDeserializer/filter.cpp @@ -825,7 +825,9 @@ TEST_CASE("shrink filter") { deserializeJson(doc, "{}", DeserializationOption::Filter(filter)); - REQUIRE(spy.log() == AllocatorLog{ - Reallocate(sizeofPool(), sizeofObject(1)), - }); + REQUIRE(spy.log() == + AllocatorLog{ + Reallocate(sizeofPool(), sizeofObject(1)), + Reallocate(sizeofStaticStringPool(), sizeofStaticStringPool(1)), + }); } diff --git a/extras/tests/JsonDocument/ElementProxy.cpp b/extras/tests/JsonDocument/ElementProxy.cpp index 9cf88551..1da97c7d 100644 --- a/extras/tests/JsonDocument/ElementProxy.cpp +++ b/extras/tests/JsonDocument/ElementProxy.cpp @@ -31,6 +31,7 @@ TEST_CASE("ElementProxy::add()") { REQUIRE(doc.as() == "[[\"world\"]]"); REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), }); } diff --git a/extras/tests/JsonDocument/MemberProxy.cpp b/extras/tests/JsonDocument/MemberProxy.cpp index c42edcc1..816054f4 100644 --- a/extras/tests/JsonDocument/MemberProxy.cpp +++ b/extras/tests/JsonDocument/MemberProxy.cpp @@ -25,6 +25,7 @@ TEST_CASE("MemberProxy::add()") { REQUIRE(doc.as() == "{\"hello\":[42]}"); REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), }); } @@ -34,6 +35,7 @@ TEST_CASE("MemberProxy::add()") { REQUIRE(doc.as() == "{\"hello\":[\"world\"]}"); REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), }); } @@ -44,6 +46,7 @@ TEST_CASE("MemberProxy::add()") { REQUIRE(doc.as() == "{\"hello\":[\"world\"]}"); REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), Allocate(sizeofString("world")), }); } @@ -55,8 +58,8 @@ TEST_CASE("MemberProxy::add()") { REQUIRE(doc.as() == "{\"hello\":[\"world\"]}"); REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), Allocate(sizeofString("world")), - }); } @@ -71,6 +74,7 @@ TEST_CASE("MemberProxy::add()") { REQUIRE(doc.as() == "{\"hello\":[\"world\"]}"); REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), Allocate(sizeofString("world")), }); } @@ -399,7 +403,7 @@ TEST_CASE("MemberProxy under memory constraints") { } SECTION("value slot allocation fails") { - timebomb.setCountdown(1); + timebomb.setCountdown(2); // fill the pool entirely, but leave one slot for the key doc["foo"][ARDUINOJSON_POOL_CAPACITY - 4] = 1; @@ -412,6 +416,7 @@ TEST_CASE("MemberProxy under memory constraints") { REQUIRE(doc.overflowed() == true); REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), AllocateFail(sizeofPool()), }); } diff --git a/extras/tests/JsonDocument/add.cpp b/extras/tests/JsonDocument/add.cpp index da898e66..b7f442a9 100644 --- a/extras/tests/JsonDocument/add.cpp +++ b/extras/tests/JsonDocument/add.cpp @@ -32,6 +32,7 @@ TEST_CASE("JsonDocument::add(T)") { REQUIRE(doc.as() == "[\"hello\"]"); REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), }); } diff --git a/extras/tests/JsonDocument/constructor.cpp b/extras/tests/JsonDocument/constructor.cpp index 1eaec202..24c7643d 100644 --- a/extras/tests/JsonDocument/constructor.cpp +++ b/extras/tests/JsonDocument/constructor.cpp @@ -62,6 +62,7 @@ TEST_CASE("JsonDocument constructor") { REQUIRE(doc2.as() == "{\"hello\":\"world\"}"); REQUIRE(spyingAllocator.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), }); } @@ -85,6 +86,7 @@ TEST_CASE("JsonDocument constructor") { REQUIRE(doc2.as() == "[\"hello\"]"); REQUIRE(spyingAllocator.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), }); } diff --git a/extras/tests/JsonDocument/set.cpp b/extras/tests/JsonDocument/set.cpp index 1205acf5..cc88b2fd 100644 --- a/extras/tests/JsonDocument/set.cpp +++ b/extras/tests/JsonDocument/set.cpp @@ -37,7 +37,9 @@ TEST_CASE("JsonDocument::set()") { doc.set("example"); REQUIRE(doc.as() == "example"_s); - REQUIRE(spy.log() == AllocatorLog{}); + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofStaticStringPool()), + }); } SECTION("const char*") { diff --git a/extras/tests/JsonDocument/shrinkToFit.cpp b/extras/tests/JsonDocument/shrinkToFit.cpp index 148b552d..ea8a5b5b 100644 --- a/extras/tests/JsonDocument/shrinkToFit.cpp +++ b/extras/tests/JsonDocument/shrinkToFit.cpp @@ -75,7 +75,11 @@ TEST_CASE("JsonDocument::shrinkToFit()") { doc.shrinkToFit(); REQUIRE(doc.as() == "hello"); - REQUIRE(spyingAllocator.log() == AllocatorLog{}); + REQUIRE(spyingAllocator.log() == + AllocatorLog{ + Allocate(sizeofStaticStringPool()), + Reallocate(sizeofStaticStringPool(), sizeofStaticStringPool(1)), + }); } SECTION("owned string") { @@ -110,7 +114,9 @@ TEST_CASE("JsonDocument::shrinkToFit()") { REQUIRE(spyingAllocator.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), Reallocate(sizeofPool(), sizeofObject(1)), + Reallocate(sizeofStaticStringPool(), sizeofStaticStringPool(1)), }); } @@ -137,7 +143,9 @@ TEST_CASE("JsonDocument::shrinkToFit()") { REQUIRE(spyingAllocator.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), Reallocate(sizeofPool(), sizeofArray(1)), + Reallocate(sizeofStaticStringPool(), sizeofStaticStringPool(1)), }); } @@ -164,20 +172,23 @@ TEST_CASE("JsonDocument::shrinkToFit()") { REQUIRE(spyingAllocator.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), Reallocate(sizeofPool(), sizeofObject(1)), + Reallocate(sizeofStaticStringPool(), sizeofStaticStringPool(2)), }); } SECTION("owned string in object") { - doc["key"] = "abcdefg"_s; + doc["key"_s] = "value"_s; doc.shrinkToFit(); - REQUIRE(doc.as() == "{\"key\":\"abcdefg\"}"); + REQUIRE(doc.as() == "{\"key\":\"value\"}"); REQUIRE(spyingAllocator.log() == AllocatorLog{ Allocate(sizeofPool()), - Allocate(sizeofString("abcdefg")), + Allocate(sizeofString("key")), + Allocate(sizeofString("value")), Reallocate(sizeofPool(), sizeofPool(2)), }); } diff --git a/extras/tests/JsonDocument/subscript.cpp b/extras/tests/JsonDocument/subscript.cpp index bbe1937e..aa53e551 100644 --- a/extras/tests/JsonDocument/subscript.cpp +++ b/extras/tests/JsonDocument/subscript.cpp @@ -112,6 +112,7 @@ TEST_CASE("JsonDocument::operator[] key storage") { REQUIRE(doc.as() == "{\"hello\":0}"); REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), }); } diff --git a/extras/tests/JsonObject/set.cpp b/extras/tests/JsonObject/set.cpp index f58dae06..7c8c5c75 100644 --- a/extras/tests/JsonObject/set.cpp +++ b/extras/tests/JsonObject/set.cpp @@ -26,25 +26,12 @@ TEST_CASE("JsonObject::set()") { REQUIRE(obj2["hello"] == "world"_s); REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), }); } - SECTION("copy local string value") { - obj1["hello"] = "world"_s; - spy.clearLog(); - - bool success = obj2.set(obj1); - - REQUIRE(success == true); - REQUIRE(obj2["hello"] == "world"_s); - REQUIRE(spy.log() == AllocatorLog{ - Allocate(sizeofPool()), - Allocate(sizeofString("world")), - }); - } - - SECTION("copy local key") { - obj1["hello"_s] = "world"; + SECTION("copy local string key and value") { + obj1["hello"_s] = "world"_s; spy.clearLog(); bool success = obj2.set(obj1); @@ -54,6 +41,7 @@ TEST_CASE("JsonObject::set()") { REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), Allocate(sizeofString("hello")), + Allocate(sizeofString("world")), }); } @@ -110,7 +98,7 @@ TEST_CASE("JsonObject::set()") { } SECTION("copy fails in the middle of an array") { - TimebombAllocator timebomb(1); + TimebombAllocator timebomb(2); JsonDocument doc3(&timebomb); JsonObject obj3 = doc3.to(); diff --git a/extras/tests/JsonObject/subscript.cpp b/extras/tests/JsonObject/subscript.cpp index bdf900f9..a7548f94 100644 --- a/extras/tests/JsonObject/subscript.cpp +++ b/extras/tests/JsonObject/subscript.cpp @@ -102,21 +102,25 @@ TEST_CASE("JsonObject::operator[]") { REQUIRE(42 == obj[key]); } - SECTION("should not duplicate const char*") { + SECTION("string literals") { obj["hello"] = "world"; - REQUIRE(spy.log() == AllocatorLog{Allocate(sizeofPool())}); + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), + }); } SECTION("should duplicate char* value") { obj["hello"] = const_cast("world"); REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), Allocate(sizeofString("world")), }); } SECTION("should duplicate char* key") { - obj[const_cast("hello")] = "world"; + obj[const_cast("hello")] = 42; REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), Allocate(sizeofString("hello")), @@ -136,12 +140,13 @@ TEST_CASE("JsonObject::operator[]") { obj["hello"] = "world"_s; REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), Allocate(sizeofString("world")), }); } SECTION("should duplicate std::string key") { - obj["hello"_s] = "world"; + obj["hello"_s] = 42; REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), Allocate(sizeofString("hello")), @@ -158,7 +163,7 @@ TEST_CASE("JsonObject::operator[]") { } SECTION("should duplicate a non-static JsonString key") { - obj[JsonString("hello", false)] = "world"; + obj[JsonString("hello", false)] = 42; REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), Allocate(sizeofString("hello")), @@ -166,9 +171,10 @@ TEST_CASE("JsonObject::operator[]") { } SECTION("should not duplicate a static JsonString key") { - obj[JsonString("hello", true)] = "world"; + obj[JsonString("hello", true)] = 42; REQUIRE(spy.log() == AllocatorLog{ Allocate(sizeofPool()), + Allocate(sizeofStaticStringPool()), }); } diff --git a/extras/tests/JsonVariant/copy.cpp b/extras/tests/JsonVariant/copy.cpp index b5da71f5..89fe9ce4 100644 --- a/extras/tests/JsonVariant/copy.cpp +++ b/extras/tests/JsonVariant/copy.cpp @@ -38,13 +38,15 @@ TEST_CASE("JsonVariant::set(JsonVariant)") { REQUIRE(var1.as() == "{\"value\":[42]}"); } - SECTION("stores const char* by reference") { + SECTION("stores string literals by pointer") { var1.set("hello!!"); spyingAllocator.clearLog(); var2.set(var1); - REQUIRE(spyingAllocator.log() == AllocatorLog{}); + REQUIRE(spyingAllocator.log() == AllocatorLog{ + Allocate(sizeofStaticStringPool()), + }); } SECTION("stores char* by copy") { diff --git a/extras/tests/JsonVariant/set.cpp b/extras/tests/JsonVariant/set.cpp index a8eafe6c..d548f33d 100644 --- a/extras/tests/JsonVariant/set.cpp +++ b/extras/tests/JsonVariant/set.cpp @@ -23,7 +23,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { REQUIRE(result == true); CHECK(variant == "hello"_s); // linked string cannot contain '\0' at the moment - CHECK(spy.log() == AllocatorLog{}); + CHECK(spy.log() == AllocatorLog{ + Allocate(sizeofStaticStringPool()), + }); } SECTION("const char*") { @@ -137,7 +139,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { REQUIRE(result == true); REQUIRE(variant == "world"); // stores by pointer - REQUIRE(spy.log() == AllocatorLog{}); + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofStaticStringPool()), + }); } SECTION("non-static JsonString") { diff --git a/extras/tests/MsgPackDeserializer/destination_types.cpp b/extras/tests/MsgPackDeserializer/destination_types.cpp index 6b437ec7..362a9651 100644 --- a/extras/tests/MsgPackDeserializer/destination_types.cpp +++ b/extras/tests/MsgPackDeserializer/destination_types.cpp @@ -104,6 +104,8 @@ TEST_CASE("deserializeMsgPack(MemberProxy)") { REQUIRE(err == DeserializationError::Ok); REQUIRE(doc.as() == "{\"hello\":\"world\",\"value\":[42]}"); - REQUIRE(spy.log() == AllocatorLog{}); + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofStaticStringPool()), + }); } } diff --git a/src/ArduinoJson/Memory/MemoryPool.hpp b/src/ArduinoJson/Memory/MemoryPool.hpp index 4663a61e..531da0a6 100644 --- a/src/ArduinoJson/Memory/MemoryPool.hpp +++ b/src/ArduinoJson/Memory/MemoryPool.hpp @@ -34,6 +34,11 @@ class Slot { return ptr_; } + T& operator*() const { + ARDUINOJSON_ASSERT(ptr_ != nullptr); + return *ptr_; + } + T* operator->() const { ARDUINOJSON_ASSERT(ptr_ != nullptr); return ptr_; diff --git a/src/ArduinoJson/Memory/ResourceManager.hpp b/src/ArduinoJson/Memory/ResourceManager.hpp index f74c91bc..24f1e941 100644 --- a/src/ArduinoJson/Memory/ResourceManager.hpp +++ b/src/ArduinoJson/Memory/ResourceManager.hpp @@ -34,6 +34,7 @@ class ResourceManager { ~ResourceManager() { stringPool_.clear(allocator_); variantPools_.clear(allocator_); + staticStringsPools_.clear(allocator_); } ResourceManager(const ResourceManager&) = delete; @@ -42,6 +43,7 @@ class ResourceManager { friend void swap(ResourceManager& a, ResourceManager& b) { swap(a.stringPool_, b.stringPool_); swap(a.variantPools_, b.variantPools_); + swap(a.staticStringsPools_, b.staticStringsPools_); swap_(a.allocator_, b.allocator_); swap_(a.overflowed_, b.overflowed_); } @@ -111,14 +113,28 @@ class ResourceManager { stringPool_.dereference(s, allocator_); } + SlotId saveStaticString(const char* s) { + auto slot = staticStringsPools_.allocSlot(allocator_); + if (!slot) + return NULL_SLOT; + *slot = s; + return slot.id(); + } + + const char* getStaticString(SlotId id) const { + return *staticStringsPools_.getSlot(id); + } + void clear() { variantPools_.clear(allocator_); overflowed_ = false; stringPool_.clear(allocator_); + staticStringsPools_.clear(allocator_); } void shrinkToFit() { variantPools_.shrinkToFit(allocator_); + staticStringsPools_.shrinkToFit(allocator_); } private: @@ -126,6 +142,7 @@ class ResourceManager { bool overflowed_; StringPool stringPool_; MemoryPoolList variantPools_; + MemoryPoolList staticStringsPools_; }; ARDUINOJSON_END_PRIVATE_NAMESPACE diff --git a/src/ArduinoJson/Object/JsonPair.hpp b/src/ArduinoJson/Object/JsonPair.hpp index f0dcb50a..9b2d4072 100644 --- a/src/ArduinoJson/Object/JsonPair.hpp +++ b/src/ArduinoJson/Object/JsonPair.hpp @@ -18,7 +18,7 @@ class JsonPair { JsonPair(detail::ObjectData::iterator iterator, detail::ResourceManager* resources) { if (!iterator.done()) { - key_ = iterator->asString(); + key_ = iterator->asString(resources); iterator.next(resources); value_ = JsonVariant(iterator.data(), resources); } @@ -46,7 +46,7 @@ class JsonPairConst { JsonPairConst(detail::ObjectData::iterator iterator, const detail::ResourceManager* resources) { if (!iterator.done()) { - key_ = iterator->asString(); + key_ = iterator->asString(resources); iterator.next(resources); value_ = JsonVariantConst(iterator.data(), resources); } diff --git a/src/ArduinoJson/Object/ObjectImpl.hpp b/src/ArduinoJson/Object/ObjectImpl.hpp index 0f78cac4..d5561109 100644 --- a/src/ArduinoJson/Object/ObjectImpl.hpp +++ b/src/ArduinoJson/Object/ObjectImpl.hpp @@ -36,7 +36,7 @@ inline ObjectData::iterator ObjectData::findKey( return iterator(); bool isKey = true; for (auto it = createIterator(resources); !it.done(); it.next(resources)) { - if (isKey && stringEquals(key, adaptString(it->asString()))) + if (isKey && stringEquals(key, adaptString(it->asString(resources)))) return it; isKey = !isKey; } diff --git a/src/ArduinoJson/Variant/ConverterImpl.hpp b/src/ArduinoJson/Variant/ConverterImpl.hpp index dddc6fa1..63ac6b4c 100644 --- a/src/ArduinoJson/Variant/ConverterImpl.hpp +++ b/src/ArduinoJson/Variant/ConverterImpl.hpp @@ -160,7 +160,7 @@ struct Converter : private detail::VariantAttorney { static const char* fromJson(JsonVariantConst src) { auto data = getData(src); - return data ? data->asString().c_str() : 0; + return data ? data->asString(getResourceManager(src)).c_str() : 0; } static bool checkJson(JsonVariantConst src) { @@ -178,7 +178,7 @@ struct Converter : private detail::VariantAttorney { static JsonString fromJson(JsonVariantConst src) { auto data = getData(src); - return data ? data->asString() : JsonString(); + return data ? data->asString(getResourceManager(src)) : JsonString(); } static bool checkJson(JsonVariantConst src) { diff --git a/src/ArduinoJson/Variant/VariantContent.hpp b/src/ArduinoJson/Variant/VariantContent.hpp index ac59172b..df5fa708 100644 --- a/src/ArduinoJson/Variant/VariantContent.hpp +++ b/src/ArduinoJson/Variant/VariantContent.hpp @@ -53,13 +53,10 @@ union VariantContent { bool asBoolean; uint32_t asUint32; int32_t asInt32; -#if ARDUINOJSON_USE_EXTENSIONS SlotId asSlotId; -#endif ArrayData asArray; ObjectData asObject; CollectionData asCollection; - const char* asLinkedString; struct StringNode* asOwnedString; }; diff --git a/src/ArduinoJson/Variant/VariantData.hpp b/src/ArduinoJson/Variant/VariantData.hpp index 8ebd8255..3be3f0f2 100644 --- a/src/ArduinoJson/Variant/VariantData.hpp +++ b/src/ArduinoJson/Variant/VariantData.hpp @@ -64,7 +64,7 @@ class VariantData { return visit.visit(content_.asObject); case VariantType::LinkedString: - return visit.visit(JsonString(content_.asLinkedString, true)); + return visit.visit(JsonString(asLinkedString(resources), true)); case VariantType::OwnedString: return visit.visit(JsonString(content_.asOwnedString->data, @@ -200,7 +200,7 @@ class VariantData { return static_cast(extension->asInt64); #endif case VariantType::LinkedString: - str = content_.asLinkedString; + str = asLinkedString(resources); break; case VariantType::OwnedString: str = content_.asOwnedString->data; @@ -242,7 +242,7 @@ class VariantData { return convertNumber(extension->asInt64); #endif case VariantType::LinkedString: - str = content_.asLinkedString; + str = asLinkedString(resources); break; case VariantType::OwnedString: str = content_.asOwnedString->data; @@ -279,10 +279,12 @@ class VariantData { } } - JsonString asString() const { + const char* asLinkedString(const ResourceManager* resources) const; + + JsonString asString(const ResourceManager* resources) const { switch (type_) { case VariantType::LinkedString: - return JsonString(content_.asLinkedString, true); + return JsonString(asLinkedString(resources), true); case VariantType::OwnedString: return JsonString(content_.asOwnedString->data, content_.asOwnedString->length); @@ -502,12 +504,7 @@ class VariantData { var->setString(value, resources); } - void setLinkedString(const char* s) { - ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first - ARDUINOJSON_ASSERT(s); - type_ = VariantType::LinkedString; - content_.asLinkedString = s; - } + bool setLinkedString(const char* s, ResourceManager* resources); void setOwnedString(StringNode* s) { ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first diff --git a/src/ArduinoJson/Variant/VariantImpl.hpp b/src/ArduinoJson/Variant/VariantImpl.hpp index cc5fd527..0df24c3a 100644 --- a/src/ArduinoJson/Variant/VariantImpl.hpp +++ b/src/ArduinoJson/Variant/VariantImpl.hpp @@ -18,6 +18,20 @@ inline void VariantData::setRawString(SerializedValue value, setRawString(dup); } +inline bool VariantData::setLinkedString(const char* s, + ResourceManager* resources) { + ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first + ARDUINOJSON_ASSERT(s); + + auto slotId = resources->saveStaticString(s); + if (slotId == NULL_SLOT) + return false; + + type_ = VariantType::LinkedString; + content_.asSlotId = slotId; + return true; +} + template inline bool VariantData::setString(TAdaptedString value, ResourceManager* resources) { @@ -26,10 +40,8 @@ inline bool VariantData::setString(TAdaptedString value, if (value.isNull()) return false; - if (value.isStatic()) { - setLinkedString(value.data()); - return true; - } + if (value.isStatic()) + return setLinkedString(value.data(), resources); auto dup = resources->saveString(value); if (dup) { @@ -65,6 +77,12 @@ inline const VariantExtension* VariantData::getExtension( } #endif +inline const char* VariantData::asLinkedString( + const ResourceManager* resources) const { + ARDUINOJSON_ASSERT(type_ == VariantType::LinkedString); + return resources->getStaticString(content_.asSlotId); +} + template enable_if_t VariantData::setFloat( T value, ResourceManager* resources) {