Эх сурвалжийг харах

Store index of slot in the pool instead of a pointer or a distance

Benoit Blanchon 2 жил өмнө
parent
commit
c4e5051a7a
44 өөрчлөгдсөн 343 нэмэгдсэн , 310 устгасан
  1. 1 2
      extras/conf_test/avr.cpp
  2. 1 2
      extras/conf_test/esp8266.cpp
  3. 2 3
      extras/conf_test/x64.cpp
  4. 1 2
      extras/conf_test/x86.cpp
  5. 6 4
      extras/tests/JsonDocument/assignment.cpp
  6. 6 5
      extras/tests/JsonDocument/constructor.cpp
  7. 5 4
      extras/tests/JsonDocument/garbageCollect.cpp
  8. 3 0
      extras/tests/MsgPackSerializer/serializeArray.cpp
  9. 15 9
      extras/tests/ResourceManager/allocVariant.cpp
  10. 1 1
      extras/tests/ResourceManager/clear.cpp
  11. 3 3
      extras/tests/ResourceManager/size.cpp
  12. 5 4
      src/ArduinoJson/Array/ArrayData.hpp
  13. 10 8
      src/ArduinoJson/Array/ArrayImpl.hpp
  14. 3 1
      src/ArduinoJson/Array/ElementProxy.hpp
  15. 4 4
      src/ArduinoJson/Array/JsonArray.hpp
  16. 6 6
      src/ArduinoJson/Array/JsonArrayConst.hpp
  17. 2 2
      src/ArduinoJson/Array/JsonArrayIterator.hpp
  18. 9 11
      src/ArduinoJson/Collection/CollectionData.hpp
  19. 41 48
      src/ArduinoJson/Collection/CollectionImpl.hpp
  20. 5 6
      src/ArduinoJson/Configuration.hpp
  21. 20 21
      src/ArduinoJson/Document/JsonDocument.hpp
  22. 1 1
      src/ArduinoJson/Json/JsonDeserializer.hpp
  23. 9 5
      src/ArduinoJson/Json/JsonSerializer.hpp
  24. 6 5
      src/ArduinoJson/Json/PrettyJsonSerializer.hpp
  25. 8 4
      src/ArduinoJson/Memory/ResourceManager.hpp
  26. 38 8
      src/ArduinoJson/Memory/VariantPool.hpp
  27. 18 16
      src/ArduinoJson/Memory/VariantPoolImpl.hpp
  28. 9 5
      src/ArduinoJson/MsgPack/MsgPackSerializer.hpp
  29. 1 1
      src/ArduinoJson/Namespace.hpp
  30. 8 6
      src/ArduinoJson/Object/JsonObject.hpp
  31. 11 9
      src/ArduinoJson/Object/JsonObjectConst.hpp
  32. 4 4
      src/ArduinoJson/Object/JsonObjectImpl.hpp
  33. 2 2
      src/ArduinoJson/Object/JsonObjectIterator.hpp
  34. 3 2
      src/ArduinoJson/Object/MemberProxy.hpp
  35. 6 4
      src/ArduinoJson/Object/ObjectData.hpp
  36. 8 6
      src/ArduinoJson/Object/ObjectImpl.hpp
  37. 7 7
      src/ArduinoJson/Polyfills/integer.hpp
  38. 2 1
      src/ArduinoJson/Serialization/measure.hpp
  39. 2 1
      src/ArduinoJson/Serialization/serialize.hpp
  40. 1 1
      src/ArduinoJson/Variant/JsonVariant.hpp
  41. 15 15
      src/ArduinoJson/Variant/JsonVariantConst.hpp
  42. 23 29
      src/ArduinoJson/Variant/VariantData.hpp
  43. 6 6
      src/ArduinoJson/Variant/VariantRefBase.hpp
  44. 6 26
      src/ArduinoJson/Variant/VariantSlot.hpp

+ 1 - 2
extras/conf_test/avr.cpp

@@ -4,8 +4,7 @@ static_assert(ARDUINOJSON_ENABLE_PROGMEM == 1, "ARDUINOJSON_ENABLE_PROGMEM");
 
 static_assert(ARDUINOJSON_USE_LONG_LONG == 0, "ARDUINOJSON_USE_LONG_LONG");
 
-static_assert(ARDUINOJSON_SLOT_OFFSET_SIZE == 1,
-              "ARDUINOJSON_SLOT_OFFSET_SIZE");
+static_assert(ARDUINOJSON_SLOT_ID_SIZE == 1, "ARDUINOJSON_SLOT_ID_SIZE");
 
 static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
 

+ 1 - 2
extras/conf_test/esp8266.cpp

@@ -2,8 +2,7 @@
 
 static_assert(ARDUINOJSON_USE_LONG_LONG == 1, "ARDUINOJSON_USE_LONG_LONG");
 
-static_assert(ARDUINOJSON_SLOT_OFFSET_SIZE == 2,
-              "ARDUINOJSON_SLOT_OFFSET_SIZE");
+static_assert(ARDUINOJSON_SLOT_ID_SIZE == 2, "ARDUINOJSON_SLOT_ID_SIZE");
 
 static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
 

+ 2 - 3
extras/conf_test/x64.cpp

@@ -2,14 +2,13 @@
 
 static_assert(ARDUINOJSON_USE_LONG_LONG == 1, "ARDUINOJSON_USE_LONG_LONG");
 
-static_assert(ARDUINOJSON_SLOT_OFFSET_SIZE == 4,
-              "ARDUINOJSON_SLOT_OFFSET_SIZE");
+static_assert(ARDUINOJSON_SLOT_ID_SIZE == 4, "ARDUINOJSON_SLOT_ID_SIZE");
 
 static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
 
 static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
 
-static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 32,
+static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 24,
               "sizeof(VariantSlot)");
 
 int main() {}

+ 1 - 2
extras/conf_test/x86.cpp

@@ -2,8 +2,7 @@
 
 static_assert(ARDUINOJSON_USE_LONG_LONG == 1, "ARDUINOJSON_USE_LONG_LONG");
 
-static_assert(ARDUINOJSON_SLOT_OFFSET_SIZE == 2,
-              "ARDUINOJSON_SLOT_OFFSET_SIZE");
+static_assert(ARDUINOJSON_SLOT_ID_SIZE == 2, "ARDUINOJSON_SLOT_ID_SIZE");
 
 static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
 

+ 6 - 4
extras/tests/JsonDocument/assignment.cpp

@@ -31,7 +31,8 @@ TEST_CASE("JsonDocument assignment") {
   }
 
   SECTION("Copy assignment reallocates when capacity is smaller") {
-    JsonDocument doc1(4096, &spyingAllocator);
+    const size_t capacity = 100 * sizeof(ArduinoJson::detail::VariantSlot);
+    JsonDocument doc1(capacity, &spyingAllocator);
     deserializeJson(doc1, "[{\"hello\":\"world\"}]");
     JsonDocument doc2(sizeofArray(1), &spyingAllocator);
     spyingAllocator.clearLog();
@@ -41,14 +42,15 @@ TEST_CASE("JsonDocument assignment") {
     REQUIRE(doc2.as<std::string>() == "[{\"hello\":\"world\"}]");
     REQUIRE(spyingAllocator.log() ==
             AllocatorLog() << AllocatorLog::Deallocate(sizeofArray(1))
-                           << AllocatorLog::Allocate(4096)
+                           << AllocatorLog::Allocate(capacity)
                            << AllocatorLog::Allocate(sizeofString(5))  // hello
                            << AllocatorLog::Allocate(sizeofString(5))  // world
     );
   }
 
   SECTION("Copy assignment reallocates when capacity is larger") {
-    JsonDocument doc1(1024, &spyingAllocator);
+    const size_t capacity1 = 100 * sizeof(ArduinoJson::detail::VariantSlot);
+    JsonDocument doc1(capacity1, &spyingAllocator);
     deserializeJson(doc1, "{\"hello\":\"world\"}");
     JsonDocument doc2(4096, &spyingAllocator);
     spyingAllocator.clearLog();
@@ -58,7 +60,7 @@ TEST_CASE("JsonDocument assignment") {
     REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
     REQUIRE(spyingAllocator.log() ==
             AllocatorLog() << AllocatorLog::Deallocate(4096)
-                           << AllocatorLog::Allocate(1024)
+                           << AllocatorLog::Allocate(capacity1)
                            << AllocatorLog::Allocate(sizeofString(5))  // hello
                            << AllocatorLog::Allocate(sizeofString(5))  // world
     );

+ 6 - 5
extras/tests/JsonDocument/constructor.cpp

@@ -22,8 +22,9 @@ TEST_CASE("JsonDocument constructor") {
   }
 
   SECTION("JsonDocument(const JsonDocument&)") {
+    const size_t capacity = 100 * sizeof(ArduinoJson::detail::VariantSlot);
     {
-      JsonDocument doc1(4096, &spyingAllocator);
+      JsonDocument doc1(capacity, &spyingAllocator);
       doc1.set(std::string("The size of this string is 32!!"));
 
       JsonDocument doc2(doc1);
@@ -32,14 +33,14 @@ TEST_CASE("JsonDocument constructor") {
       REQUIRE(doc2.as<std::string>() == "The size of this string is 32!!");
     }
     REQUIRE(spyingAllocator.log() ==
-            AllocatorLog() << AllocatorLog::Allocate(4096)
+            AllocatorLog() << AllocatorLog::Allocate(capacity)
                            << AllocatorLog::Allocate(sizeofString(31))
-                           << AllocatorLog::Allocate(4096)
+                           << AllocatorLog::Allocate(capacity)
                            << AllocatorLog::Allocate(sizeofString(31))
                            << AllocatorLog::Deallocate(sizeofString(31))
-                           << AllocatorLog::Deallocate(4096)
+                           << AllocatorLog::Deallocate(capacity)
                            << AllocatorLog::Deallocate(sizeofString(31))
-                           << AllocatorLog::Deallocate(4096));
+                           << AllocatorLog::Deallocate(capacity));
   }
 
   SECTION("JsonDocument(JsonDocument&&)") {

+ 5 - 4
extras/tests/JsonDocument/garbageCollect.cpp

@@ -13,9 +13,10 @@ using ArduinoJson::detail::sizeofObject;
 using ArduinoJson::detail::sizeofString;
 
 TEST_CASE("JsonDocument::garbageCollect()") {
+  const size_t capacity = 100 * sizeof(ArduinoJson::detail::VariantSlot);
   ControllableAllocator controllableAllocator;
   SpyingAllocator spyingAllocator(&controllableAllocator);
-  JsonDocument doc(4096, &spyingAllocator);
+  JsonDocument doc(capacity, &spyingAllocator);
 
   SECTION("when allocation succeeds") {
     deserializeJson(doc, "{\"blanket\":1,\"dancing\":2}");
@@ -29,10 +30,10 @@ TEST_CASE("JsonDocument::garbageCollect()") {
     REQUIRE(doc.memoryUsage() == sizeofObject(1) + sizeofString(7));
     REQUIRE(doc.as<std::string>() == "{\"dancing\":2}");
     REQUIRE(spyingAllocator.log() ==
-            AllocatorLog() << AllocatorLog::Allocate(4096)
+            AllocatorLog() << AllocatorLog::Allocate(capacity)
                            << AllocatorLog::Allocate(sizeofString(7))
                            << AllocatorLog::Deallocate(sizeofString(7))
-                           << AllocatorLog::Deallocate(4096));
+                           << AllocatorLog::Deallocate(capacity));
   }
 
   SECTION("when allocation fails") {
@@ -49,7 +50,7 @@ TEST_CASE("JsonDocument::garbageCollect()") {
     REQUIRE(doc.as<std::string>() == "{\"dancing\":2}");
 
     REQUIRE(spyingAllocator.log() ==
-            AllocatorLog() << AllocatorLog::AllocateFail(4096)
+            AllocatorLog() << AllocatorLog::AllocateFail(capacity)
                            << AllocatorLog::AllocateFail(sizeofString(7)));
   }
 }

+ 3 - 0
extras/tests/MsgPackSerializer/serializeArray.cpp

@@ -2,6 +2,8 @@
 // Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
+#define ARDUINOJSON_SLOT_ID_SIZE 4  // required to reach 65536 elements
+
 #include <ArduinoJson.h>
 #include <catch.hpp>
 
@@ -55,6 +57,7 @@ TEST_CASE("serialize MsgPack array") {
     const char* nil = 0;
     for (int i = 0; i < 65536; i++)
       array.add(nil);
+    REQUIRE(array.size() == 65536);
 
     check(array,
           std::string("\xDD\x00\x01\x00\x00", 5) + std::string(65536, '\xc0'));

+ 15 - 9
extras/tests/ResourceManager/allocVariant.cpp

@@ -11,13 +11,13 @@
 
 using namespace ArduinoJson::detail;
 
-TEST_CASE("ResourceManager::allocVariant()") {
+TEST_CASE("ResourceManager::allocSlot()") {
   SECTION("Returns different pointer") {
     ResourceManager resources(4096);
 
-    VariantSlot* s1 = resources.allocVariant();
+    VariantSlot* s1 = resources.allocSlot();
     REQUIRE(s1 != 0);
-    VariantSlot* s2 = resources.allocVariant();
+    VariantSlot* s2 = resources.allocSlot();
     REQUIRE(s2 != 0);
 
     REQUIRE(s1 != s2);
@@ -26,27 +26,33 @@ TEST_CASE("ResourceManager::allocVariant()") {
   SECTION("Returns aligned pointers") {
     ResourceManager resources(4096);
 
-    REQUIRE(isAligned(resources.allocVariant()));
-    REQUIRE(isAligned(resources.allocVariant()));
+    REQUIRE(isAligned(resources.allocSlot().operator VariantSlot*()));
+    REQUIRE(isAligned(resources.allocSlot().operator VariantSlot*()));
   }
 
   SECTION("Returns zero if capacity is 0") {
     ResourceManager resources(0);
 
-    REQUIRE(resources.allocVariant() == 0);
+    auto variant = resources.allocSlot();
+    REQUIRE(variant.id() == NULL_SLOT);
+    REQUIRE(static_cast<VariantSlot*>(variant) == nullptr);
   }
 
   SECTION("Returns zero if buffer is null") {
     ResourceManager resources(4096, FailingAllocator::instance());
 
-    REQUIRE(resources.allocVariant() == 0);
+    auto variant = resources.allocSlot();
+    REQUIRE(variant.id() == NULL_SLOT);
+    REQUIRE(static_cast<VariantSlot*>(variant) == nullptr);
   }
 
   SECTION("Returns zero if capacity is insufficient") {
     ResourceManager resources(sizeof(VariantSlot));
 
-    resources.allocVariant();
+    resources.allocSlot();
 
-    REQUIRE(resources.allocVariant() == 0);
+    auto variant = resources.allocSlot();
+    REQUIRE(variant.id() == NULL_SLOT);
+    REQUIRE(static_cast<VariantSlot*>(variant) == nullptr);
   }
 }

+ 1 - 1
extras/tests/ResourceManager/clear.cpp

@@ -15,7 +15,7 @@ TEST_CASE("ResourceManager::clear()") {
   ResourceManager resources(poolCapacity);
 
   SECTION("Discards allocated variants") {
-    resources.allocVariant();
+    resources.allocSlot();
 
     resources.clear();
     REQUIRE(resources.size() == 0);

+ 3 - 3
extras/tests/ResourceManager/size.cpp

@@ -9,7 +9,7 @@
 using namespace ArduinoJson::detail;
 
 TEST_CASE("ResourceManager::capacity()") {
-  const size_t capacity = 64;
+  const size_t capacity = 4 * sizeof(VariantSlot);
   ResourceManager resources(capacity);
   REQUIRE(capacity == resources.capacity());
 }
@@ -25,10 +25,10 @@ TEST_CASE("ResourceManager::size()") {
     const size_t variantCount = resources.capacity() / sizeof(VariantSlot);
 
     for (size_t i = 0; i < variantCount; i++)
-      resources.allocVariant();
+      resources.allocSlot();
     size_t size = resources.size();
 
-    resources.allocVariant();
+    resources.allocSlot();
 
     REQUIRE(size == resources.size());
   }

+ 5 - 4
src/ArduinoJson/Array/ArrayData.hpp

@@ -22,12 +22,13 @@ class ArrayData : public CollectionData {
 
   VariantData* getOrAddElement(size_t index, ResourceManager* resources);
 
-  VariantData* getElement(size_t index) const;
+  VariantData* getElement(size_t index, const ResourceManager* resources) const;
 
-  static VariantData* getElement(const ArrayData* array, size_t index) {
+  static VariantData* getElement(const ArrayData* array, size_t index,
+                                 const ResourceManager* resources) {
     if (!array)
       return nullptr;
-    return array->getElement(index);
+    return array->getElement(index, resources);
   }
 
   void removeElement(size_t index, ResourceManager* resources);
@@ -50,7 +51,7 @@ class ArrayData : public CollectionData {
   }
 
  private:
-  iterator at(size_t index) const;
+  iterator at(size_t index, const ResourceManager* resources) const;
 };
 
 ARDUINOJSON_END_PRIVATE_NAMESPACE

+ 10 - 8
src/ArduinoJson/Array/ArrayImpl.hpp

@@ -9,10 +9,11 @@
 
 ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
-inline ArrayData::iterator ArrayData::at(size_t index) const {
-  auto it = createIterator();
+inline ArrayData::iterator ArrayData::at(
+    size_t index, const ResourceManager* resources) const {
+  auto it = createIterator(resources);
   while (!it.done() && index) {
-    it.next();
+    it.next(resources);
     --index;
   }
   return it;
@@ -20,9 +21,9 @@ inline ArrayData::iterator ArrayData::at(size_t index) const {
 
 inline VariantData* ArrayData::getOrAddElement(size_t index,
                                                ResourceManager* resources) {
-  auto it = createIterator();
+  auto it = createIterator(resources);
   while (!it.done() && index > 0) {
-    it.next();
+    it.next(resources);
     index--;
   }
   if (it.done())
@@ -37,12 +38,13 @@ inline VariantData* ArrayData::getOrAddElement(size_t index,
   return element;
 }
 
-inline VariantData* ArrayData::getElement(size_t index) const {
-  return at(index).data();
+inline VariantData* ArrayData::getElement(
+    size_t index, const ResourceManager* resources) const {
+  return at(index, resources).data();
 }
 
 inline void ArrayData::removeElement(size_t index, ResourceManager* resources) {
-  remove(at(index), resources);
+  remove(at(index, resources), resources);
 }
 
 ARDUINOJSON_END_PRIVATE_NAMESPACE

+ 3 - 1
src/ArduinoJson/Array/ElementProxy.hpp

@@ -45,7 +45,9 @@ class ElementProxy : public VariantRefBase<ElementProxy<TUpstream>>,
   }
 
   FORCE_INLINE VariantData* getData() const {
-    return VariantData::getElement(VariantAttorney::getData(upstream_), index_);
+    return VariantData::getElement(
+        VariantAttorney::getData(upstream_), index_,
+        VariantAttorney::getResourceManager(upstream_));
   }
 
   FORCE_INLINE VariantData* getOrCreateData() const {

+ 4 - 4
src/ArduinoJson/Array/JsonArray.hpp

@@ -68,7 +68,7 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
   FORCE_INLINE iterator begin() const {
     if (!data_)
       return iterator();
-    return iterator(data_->createIterator(), resources_);
+    return iterator(data_->createIterator(resources_), resources_);
   }
 
   // Returns an iterator following the last element of the array.
@@ -148,19 +148,19 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
   // Returns the number of bytes occupied by the array.
   // https://arduinojson.org/v6/api/jsonarray/memoryusage/
   FORCE_INLINE size_t memoryUsage() const {
-    return data_ ? data_->memoryUsage() : 0;
+    return data_ ? data_->memoryUsage(resources_) : 0;
   }
 
   // Returns the depth (nesting level) of the array.
   // https://arduinojson.org/v6/api/jsonarray/nesting/
   FORCE_INLINE size_t nesting() const {
-    return detail::VariantData::nesting(collectionToVariant(data_));
+    return detail::VariantData::nesting(collectionToVariant(data_), resources_);
   }
 
   // Returns the number of elements in the array.
   // https://arduinojson.org/v6/api/jsonarray/size/
   FORCE_INLINE size_t size() const {
-    return data_ ? data_->size() : 0;
+    return data_ ? data_->size(resources_) : 0;
   }
 
  private:

+ 6 - 6
src/ArduinoJson/Array/JsonArrayConst.hpp

@@ -26,7 +26,7 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
   FORCE_INLINE iterator begin() const {
     if (!data_)
       return iterator();
-    return iterator(data_->createIterator(), resources_);
+    return iterator(data_->createIterator(resources_), resources_);
   }
 
   // Returns an iterator to the element following the last element of the array.
@@ -46,8 +46,8 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
   // Returns the element at the specified index.
   // https://arduinojson.org/v6/api/jsonarrayconst/subscript/
   FORCE_INLINE JsonVariantConst operator[](size_t index) const {
-    return JsonVariantConst(detail::ArrayData::getElement(data_, index),
-                            resources_);
+    return JsonVariantConst(
+        detail::ArrayData::getElement(data_, index, resources_), resources_);
   }
 
   operator JsonVariantConst() const {
@@ -69,19 +69,19 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
   // Returns the number of bytes occupied by the array.
   // https://arduinojson.org/v6/api/jsonarrayconst/memoryusage/
   FORCE_INLINE size_t memoryUsage() const {
-    return data_ ? data_->memoryUsage() : 0;
+    return data_ ? data_->memoryUsage(resources_) : 0;
   }
 
   // Returns the depth (nesting level) of the array.
   // https://arduinojson.org/v6/api/jsonarrayconst/nesting/
   FORCE_INLINE size_t nesting() const {
-    return detail::VariantData::nesting(collectionToVariant(data_));
+    return detail::VariantData::nesting(collectionToVariant(data_), resources_);
   }
 
   // Returns the number of elements in the array.
   // https://arduinojson.org/v6/api/jsonarrayconst/size/
   FORCE_INLINE size_t size() const {
-    return data_ ? data_->size() : 0;
+    return data_ ? data_->size(resources_) : 0;
   }
 
  private:

+ 2 - 2
src/ArduinoJson/Array/JsonArrayIterator.hpp

@@ -50,7 +50,7 @@ class JsonArrayIterator {
   }
 
   JsonArrayIterator& operator++() {
-    iterator_.next();
+    iterator_.next(resources_);
     return *this;
   }
 
@@ -84,7 +84,7 @@ class JsonArrayConstIterator {
   }
 
   JsonArrayConstIterator& operator++() {
-    iterator_.next();
+    iterator_.next(resources_);
     return *this;
   }
 

+ 9 - 11
src/ArduinoJson/Collection/CollectionData.hpp

@@ -20,7 +20,7 @@ class CollectionIterator {
  public:
   CollectionIterator() : slot_(nullptr) {}
 
-  void next();
+  void next(const ResourceManager* resources);
 
   bool done() const {
     return slot_ == nullptr;
@@ -70,8 +70,8 @@ class CollectionIterator {
 };
 
 class CollectionData {
-  VariantSlot* head_ = 0;
-  VariantSlot* tail_ = 0;
+  SlotId head_ = NULL_SLOT;
+  SlotId tail_ = NULL_SLOT;
 
  public:
   // Placement new
@@ -83,13 +83,13 @@ class CollectionData {
 
   using iterator = CollectionIterator;
 
-  iterator createIterator() const {
-    return iterator(head_);
+  iterator createIterator(const ResourceManager* resources) const {
+    return iterator(resources->getSlot(head_));
   }
 
-  size_t memoryUsage() const;
-  size_t size() const;
-  size_t nesting() const;
+  size_t memoryUsage(const ResourceManager*) const;
+  size_t size(const ResourceManager*) const;
+  size_t nesting(const ResourceManager*) const;
 
   void clear(ResourceManager* resources);
 
@@ -99,8 +99,6 @@ class CollectionData {
     collection->clear(resources);
   }
 
-  void movePointers(ptrdiff_t variantDistance);
-
   void remove(iterator it, ResourceManager* resources);
 
   static void remove(CollectionData* collection, iterator it,
@@ -113,7 +111,7 @@ class CollectionData {
   iterator addSlot(ResourceManager*);
 
  private:
-  VariantSlot* getPreviousSlot(VariantSlot*) const;
+  SlotWithId getPreviousSlot(VariantSlot*, const ResourceManager*) const;
   static void releaseSlot(VariantSlot*, ResourceManager*);
 };
 

+ 41 - 48
src/ArduinoJson/Collection/CollectionImpl.hpp

@@ -34,102 +34,95 @@ inline bool CollectionIterator::ownsKey() const {
   return slot_->ownsKey();
 }
 
-inline void CollectionIterator::next() {
+inline void CollectionIterator::next(const ResourceManager* resources) {
   ARDUINOJSON_ASSERT(slot_ != nullptr);
-  slot_ = slot_->next();
+  auto nextId = slot_->next();
+  if (nextId != NULL_SLOT)
+    slot_ = resources->getSlot(nextId);
+  else
+    slot_ = nullptr;
 }
 
 inline CollectionData::iterator CollectionData::addSlot(
     ResourceManager* resources) {
-  auto slot = resources->allocVariant();
+  auto slot = resources->allocSlot();
   if (!slot)
     return nullptr;
-  if (tail_) {
-    tail_->setNextNotNull(slot);
-    tail_ = slot;
+  if (tail_ != NULL_SLOT) {
+    auto tail = resources->getSlot(tail_);
+    tail->setNext(slot.id());
+    tail_ = slot.id();
   } else {
-    head_ = slot;
-    tail_ = slot;
+    head_ = slot.id();
+    tail_ = slot.id();
   }
-  return slot;
+  return iterator(slot);
 }
 
 inline void CollectionData::clear(ResourceManager* resources) {
-  for (auto slot = head_; slot; slot = slot->next())
-    releaseSlot(slot, resources);
-  head_ = 0;
-  tail_ = 0;
+  for (auto it = createIterator(resources); !it.done(); it.next(resources))
+    releaseSlot(it.slot_, resources);
+  head_ = NULL_SLOT;
+  tail_ = NULL_SLOT;
 }
 
-inline VariantSlot* CollectionData::getPreviousSlot(VariantSlot* target) const {
-  VariantSlot* current = head_;
-  while (current) {
-    VariantSlot* next = current->next();
-    if (next == target)
-      return current;
-    current = next;
+inline SlotWithId CollectionData::getPreviousSlot(
+    VariantSlot* target, const ResourceManager* resources) const {
+  auto prev = SlotWithId();
+  auto currentId = head_;
+  while (currentId != NULL_SLOT) {
+    auto currentSlot = resources->getSlot(currentId);
+    if (currentSlot == target)
+      return prev;
+    prev = SlotWithId(currentSlot, currentId);
+    currentId = currentSlot->next();
   }
-  return 0;
+  return SlotWithId();
 }
 
 inline void CollectionData::remove(iterator it, ResourceManager* resources) {
   if (it.done())
     return;
   auto curr = it.slot_;
-  auto prev = getPreviousSlot(curr);
+  auto prev = getPreviousSlot(curr, resources);
   auto next = curr->next();
   if (prev)
     prev->setNext(next);
   else
     head_ = next;
-  if (!next)
-    tail_ = prev;
+  if (next == NULL_SLOT)
+    tail_ = prev.id();
   releaseSlot(curr, resources);
 }
 
-inline size_t CollectionData::memoryUsage() const {
+inline size_t CollectionData::memoryUsage(
+    const ResourceManager* resources) const {
   size_t total = 0;
-  for (auto it = createIterator(); !it.done(); it.next()) {
-    total += sizeof(VariantSlot) + it->memoryUsage();
+  for (auto it = createIterator(resources); !it.done(); it.next(resources)) {
+    total += sizeof(VariantSlot) + it->memoryUsage(resources);
     if (it.ownsKey())
       total += sizeofString(strlen(it.key()));
   }
   return total;
 }
 
-inline size_t CollectionData::nesting() const {
+inline size_t CollectionData::nesting(const ResourceManager* resources) const {
   size_t maxChildNesting = 0;
-  for (auto it = createIterator(); !it.done(); it.next()) {
-    size_t childNesting = it->nesting();
+  for (auto it = createIterator(resources); !it.done(); it.next(resources)) {
+    size_t childNesting = it->nesting(resources);
     if (childNesting > maxChildNesting)
       maxChildNesting = childNesting;
   }
   return maxChildNesting + 1;
 }
 
-inline size_t CollectionData::size() const {
+inline size_t CollectionData::size(const ResourceManager* resources) const {
   size_t count = 0;
-  for (auto it = createIterator(); !it.done(); it.next())
+  for (auto it = createIterator(resources); !it.done(); it.next(resources))
     count++;
   return count;
 }
 
-template <typename T>
-inline void movePointer(T*& p, ptrdiff_t offset) {
-  if (!p)
-    return;
-  p = reinterpret_cast<T*>(
-      reinterpret_cast<void*>(reinterpret_cast<char*>(p) + offset));
-  ARDUINOJSON_ASSERT(isAligned(p));
-}
-
-inline void CollectionData::movePointers(ptrdiff_t variantDistance) {
-  movePointer(head_, variantDistance);
-  movePointer(tail_, variantDistance);
-  for (VariantSlot* slot = head_; slot; slot = slot->next())
-    slot->data()->movePointers(variantDistance);
-}
-
 inline void CollectionData::releaseSlot(VariantSlot* slot,
                                         ResourceManager* resources) {
   ARDUINOJSON_ASSERT(slot != nullptr);

+ 5 - 6
src/ArduinoJson/Configuration.hpp

@@ -75,19 +75,18 @@
 #  define ARDUINOJSON_DEFAULT_NESTING_LIMIT 10
 #endif
 
-// Number of bits to store the pointer to next node
-// (saves RAM but limits the number of values in a document)
-#ifndef ARDUINOJSON_SLOT_OFFSET_SIZE
+// Number of bits to store the variant identifier
+#ifndef ARDUINOJSON_SLOT_ID_SIZE
 #  if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ <= 2
 // Address space == 16-bit => max 127 values
-#    define ARDUINOJSON_SLOT_OFFSET_SIZE 1
+#    define ARDUINOJSON_SLOT_ID_SIZE 1
 #  elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ >= 8 || \
       defined(_WIN64) && _WIN64
 // Address space == 64-bit => max 2147483647 values
-#    define ARDUINOJSON_SLOT_OFFSET_SIZE 4
+#    define ARDUINOJSON_SLOT_ID_SIZE 4
 #  else
 // Address space == 32-bit => max 32767 values
-#    define ARDUINOJSON_SLOT_OFFSET_SIZE 2
+#    define ARDUINOJSON_SLOT_ID_SIZE 2
 #  endif
 #endif
 

+ 20 - 21
src/ArduinoJson/Document/JsonDocument.hpp

@@ -86,8 +86,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   // Reduces the capacity of the memory pool to match the current usage.
   // https://arduinojson.org/v6/api/JsonDocument/shrinktofit/
   void shrinkToFit() {
-    auto offset = resources_.shrinkToFit();
-    data_.movePointers(offset);
+    resources_.shrinkToFit();
   }
 
   // Reclaims the memory leaked when removing and replacing values.
@@ -105,14 +104,14 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   // https://arduinojson.org/v6/api/jsondocument/as/
   template <typename T>
   T as() {
-    return getVariant().template as<T>();
+    return getSlot().template as<T>();
   }
 
   // Casts the root to the specified type.
   // https://arduinojson.org/v6/api/jsondocument/as/
   template <typename T>
   T as() const {
-    return getVariant().template as<T>();
+    return getSlot().template as<T>();
   }
 
   // Empties the document and resets the memory pool
@@ -126,20 +125,20 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   // https://arduinojson.org/v6/api/jsondocument/is/
   template <typename T>
   bool is() {
-    return getVariant().template is<T>();
+    return getSlot().template is<T>();
   }
 
   // Returns true if the root is of the specified type.
   // https://arduinojson.org/v6/api/jsondocument/is/
   template <typename T>
   bool is() const {
-    return getVariant().template is<T>();
+    return getSlot().template is<T>();
   }
 
   // Returns true if the root is null.
   // https://arduinojson.org/v6/api/jsondocument/isnull/
   bool isNull() const {
-    return getVariant().isNull();
+    return getSlot().isNull();
   }
 
   // Returns the number of used bytes in the memory pool.
@@ -157,13 +156,13 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   // Returns the depth (nesting level) of the array.
   // https://arduinojson.org/v6/api/jsondocument/nesting/
   size_t nesting() const {
-    return data_.nesting();
+    return data_.nesting(&resources_);
   }
 
   // Returns the number of elements in the root array or object.
   // https://arduinojson.org/v6/api/jsondocument/size/
   size_t size() const {
-    return data_.size();
+    return data_.size(&resources_);
   }
 
   // Copies the specified document.
@@ -186,7 +185,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   template <typename T>
   typename detail::VariantTo<T>::type to() {
     clear();
-    return getVariant().template to<T>();
+    return getSlot().template to<T>();
   }
 
   // Creates an array and appends it to the root array.
@@ -233,14 +232,14 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   // https://arduinojson.org/v6/api/jsondocument/containskey/
   template <typename TChar>
   bool containsKey(TChar* key) const {
-    return data_.getMember(detail::adaptString(key)) != 0;
+    return data_.getMember(detail::adaptString(key), &resources_) != 0;
   }
 
   // Returns true if the root object contains the specified key.
   // https://arduinojson.org/v6/api/jsondocument/containskey/
   template <typename TString>
   bool containsKey(const TString& key) const {
-    return data_.getMember(detail::adaptString(key)) != 0;
+    return data_.getMember(detail::adaptString(key), &resources_) != 0;
   }
 
   // Gets or sets a root object's member.
@@ -269,8 +268,8 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   FORCE_INLINE typename detail::enable_if<detail::IsString<TString>::value,
                                           JsonVariantConst>::type
   operator[](const TString& key) const {
-    return JsonVariantConst(data_.getMember(detail::adaptString(key)),
-                            &resources_);
+    return JsonVariantConst(
+        data_.getMember(detail::adaptString(key), &resources_), &resources_);
   }
 
   // Gets a root object's member.
@@ -279,8 +278,8 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   FORCE_INLINE typename detail::enable_if<detail::IsString<TChar*>::value,
                                           JsonVariantConst>::type
   operator[](TChar* key) const {
-    return JsonVariantConst(data_.getMember(detail::adaptString(key)),
-                            &resources_);
+    return JsonVariantConst(
+        data_.getMember(detail::adaptString(key), &resources_), &resources_);
   }
 
   // Gets or sets a root array's element.
@@ -292,7 +291,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   // Gets a root array's member.
   // https://arduinojson.org/v6/api/jsondocument/subscript/
   FORCE_INLINE JsonVariantConst operator[](size_t index) const {
-    return JsonVariantConst(data_.getElement(index), &resources_);
+    return JsonVariantConst(data_.getElement(index, &resources_), &resources_);
   }
 
   // Appends a new (null) element to the root array.
@@ -345,19 +344,19 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   }
 
   FORCE_INLINE operator JsonVariant() {
-    return getVariant();
+    return getSlot();
   }
 
   FORCE_INLINE operator JsonVariantConst() const {
-    return getVariant();
+    return getSlot();
   }
 
  private:
-  JsonVariant getVariant() {
+  JsonVariant getSlot() {
     return JsonVariant(&data_, &resources_);
   }
 
-  JsonVariantConst getVariant() const {
+  JsonVariantConst getSlot() const {
     return JsonVariantConst(&data_, &resources_);
   }
 

+ 1 - 1
src/ArduinoJson/Json/JsonDeserializer.hpp

@@ -273,7 +273,7 @@ class JsonDeserializer {
       TFilter memberFilter = filter[key.c_str()];
 
       if (memberFilter.allow()) {
-        auto member = object.getMember(adaptString(key.c_str()));
+        auto member = object.getMember(adaptString(key.c_str()), resources_);
         if (!member) {
           // Save key in memory pool.
           auto savedKey = stringBuilder_.save();

+ 9 - 5
src/ArduinoJson/Json/JsonSerializer.hpp

@@ -16,17 +16,18 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
  public:
   static const bool producesText = true;
 
-  JsonSerializer(TWriter writer) : formatter_(writer) {}
+  JsonSerializer(TWriter writer, const ResourceManager* resources)
+      : formatter_(writer), resources_(resources) {}
 
   FORCE_INLINE size_t visit(const ArrayData& array) {
     write('[');
 
-    auto it = array.createIterator();
+    auto it = array.createIterator(resources_);
 
     while (!it.done()) {
       it->accept(*this);
 
-      it.next();
+      it.next(resources_);
       if (it.done())
         break;
 
@@ -40,14 +41,14 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
   size_t visit(const ObjectData& object) {
     write('{');
 
-    auto it = object.createIterator();
+    auto it = object.createIterator(resources_);
 
     while (!it.done()) {
       formatter_.writeString(it.key());
       write(':');
       it->accept(*this);
 
-      it.next();
+      it.next(resources_);
       if (it.done())
         break;
 
@@ -113,6 +114,9 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
 
  private:
   TextFormatter<TWriter> formatter_;
+
+ protected:
+  const ResourceManager* resources_;
 };
 
 ARDUINOJSON_END_PRIVATE_NAMESPACE

+ 6 - 5
src/ArduinoJson/Json/PrettyJsonSerializer.hpp

@@ -16,10 +16,11 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
   typedef JsonSerializer<TWriter> base;
 
  public:
-  PrettyJsonSerializer(TWriter writer) : base(writer), nesting_(0) {}
+  PrettyJsonSerializer(TWriter writer, const ResourceManager* resources)
+      : base(writer, resources), nesting_(0) {}
 
   size_t visit(const ArrayData& array) {
-    auto it = array.createIterator();
+    auto it = array.createIterator(base::resources_);
     if (!it.done()) {
       base::write("[\r\n");
       nesting_++;
@@ -27,7 +28,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
         indent();
         it->accept(*this);
 
-        it.next();
+        it.next(base::resources_);
         base::write(it.done() ? "\r\n" : ",\r\n");
       }
       nesting_--;
@@ -40,7 +41,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
   }
 
   size_t visit(const ObjectData& object) {
-    auto it = object.createIterator();
+    auto it = object.createIterator(base::resources_);
     if (!it.done()) {
       base::write("{\r\n");
       nesting_++;
@@ -50,7 +51,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
         base::write(": ");
         it->accept(*this);
 
-        it.next();
+        it.next(base::resources_);
         base::write(it.done() ? "\r\n" : ",\r\n");
       }
       nesting_--;

+ 8 - 4
src/ArduinoJson/Memory/ResourceManager.hpp

@@ -67,13 +67,17 @@ class ResourceManager {
     return overflowed_;
   }
 
-  VariantSlot* allocVariant() {
-    auto p = variantPool_.allocVariant();
+  SlotWithId allocSlot() {
+    auto p = variantPool_.allocSlot();
     if (!p)
       overflowed_ = true;
     return p;
   }
 
+  VariantSlot* getSlot(SlotId id) const {
+    return variantPool_.getSlot(id);
+  }
+
   template <typename TAdaptedString>
   StringNode* saveString(TAdaptedString str) {
     if (str.isNull())
@@ -123,8 +127,8 @@ class ResourceManager {
     stringPool_.clear(allocator_);
   }
 
-  ptrdiff_t shrinkToFit() {
-    return variantPool_.shrinkToFit(allocator_);
+  void shrinkToFit() {
+    variantPool_.shrinkToFit(allocator_);
   }
 
  private:

+ 38 - 8
src/ArduinoJson/Memory/VariantPool.hpp

@@ -6,10 +6,39 @@
 
 #include <ArduinoJson/Memory/ResourceManager.hpp>
 #include <ArduinoJson/Polyfills/assert.hpp>
+#include <ArduinoJson/Polyfills/integer.hpp>
 
 ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 class VariantSlot;
+using SlotId = uint_t<ARDUINOJSON_SLOT_ID_SIZE * 8>::type;
+using SlotCount = SlotId;
+const SlotId NULL_SLOT = SlotId(-1);
+
+class SlotWithId {
+ public:
+  SlotWithId() : slot_(nullptr), id_(NULL_SLOT) {}
+  SlotWithId(VariantSlot* slot, SlotId id) : slot_(slot), id_(id) {
+    ARDUINOJSON_ASSERT((slot == nullptr) == (id == NULL_SLOT));
+  }
+
+  SlotId id() const {
+    return id_;
+  }
+
+  operator VariantSlot*() {
+    return slot_;
+  }
+
+  VariantSlot* operator->() {
+    ARDUINOJSON_ASSERT(slot_ != nullptr);
+    return slot_;
+  }
+
+ private:
+  VariantSlot* slot_;
+  SlotId id_;
+};
 
 class VariantPool {
  public:
@@ -30,18 +59,19 @@ class VariantPool {
   void create(size_t cap, Allocator* allocator);
   void destroy(Allocator* allocator);
 
-  VariantSlot* allocVariant();
+  SlotWithId allocSlot();
+  VariantSlot* getSlot(SlotId id) const;
   void clear();
-  ptrdiff_t shrinkToFit(Allocator*);
-  size_t capacity() const;
-  size_t usage() const;
+  void shrinkToFit(Allocator*);
+  SlotCount capacity() const;
+  SlotCount usage() const;
 
-  static size_t bytesToSlots(size_t);
-  static size_t slotsToBytes(size_t);
+  static SlotCount bytesToSlots(size_t);
+  static size_t slotsToBytes(SlotCount);
 
  private:
-  size_t capacity_ = 0;
-  size_t usage_ = 0;
+  SlotCount capacity_ = 0;
+  SlotCount usage_ = 0;
   VariantSlot* slots_ = nullptr;
 };
 

+ 18 - 16
src/ArduinoJson/Memory/VariantPoolImpl.hpp

@@ -28,29 +28,31 @@ inline void VariantPool::destroy(Allocator* allocator) {
   usage_ = 0;
 }
 
-inline ptrdiff_t VariantPool::shrinkToFit(Allocator* allocator) {
-  auto originalPool = slots_;
+inline void VariantPool::shrinkToFit(Allocator* allocator) {
   slots_ = reinterpret_cast<VariantSlot*>(
       allocator->reallocate(slots_, slotsToBytes(usage_)));
-  if (slots_)
-    capacity_ = usage_;
-  return reinterpret_cast<char*>(slots_) -
-         reinterpret_cast<char*>(originalPool);
+  capacity_ = usage_;
 }
 
-inline VariantSlot* VariantPool::allocVariant() {
+inline SlotWithId VariantPool::allocSlot() {
   if (!slots_)
-    return nullptr;
-  if (usage_ + 1 > capacity_)
-    return nullptr;
-  return new (&slots_[usage_++]) VariantSlot;
+    return {};
+  if (usage_ >= capacity_)
+    return {};
+  auto index = usage_++;
+  auto slot = &slots_[index];
+  return {new (slot) VariantSlot, SlotId(index)};
+}
+
+inline VariantSlot* VariantPool::getSlot(SlotId id) const {
+  return id == NULL_SLOT ? nullptr : &slots_[id];
 }
 
-inline size_t VariantPool::usage() const {
+inline SlotCount VariantPool::usage() const {
   return usage_;
 }
 
-inline size_t VariantPool::capacity() const {
+inline SlotCount VariantPool::capacity() const {
   return capacity_;
 }
 
@@ -58,11 +60,11 @@ inline void VariantPool::clear() {
   usage_ = 0;
 }
 
-inline size_t VariantPool::bytesToSlots(size_t n) {
-  return n / sizeof(VariantSlot);
+inline SlotCount VariantPool::bytesToSlots(size_t n) {
+  return static_cast<SlotCount>(n / sizeof(VariantSlot));
 }
 
-inline size_t VariantPool::slotsToBytes(size_t n) {
+inline size_t VariantPool::slotsToBytes(SlotCount n) {
   return n * sizeof(VariantSlot);
 }
 

+ 9 - 5
src/ArduinoJson/MsgPack/MsgPackSerializer.hpp

@@ -19,7 +19,8 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
  public:
   static const bool producesText = false;
 
-  MsgPackSerializer(TWriter writer) : writer_(writer) {}
+  MsgPackSerializer(TWriter writer, const ResourceManager* resources)
+      : writer_(writer), resources_(resources) {}
 
   template <typename T>
   typename enable_if<is_floating_point<T>::value && sizeof(T) == 4,
@@ -48,7 +49,7 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
   }
 
   size_t visit(const ArrayData& array) {
-    size_t n = array.size();
+    size_t n = array.size(resources_);
     if (n < 0x10) {
       writeByte(uint8_t(0x90 + n));
     } else if (n < 0x10000) {
@@ -58,14 +59,15 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
       writeByte(0xDD);
       writeInteger(uint32_t(n));
     }
-    for (auto it = array.createIterator(); !it.done(); it.next()) {
+    for (auto it = array.createIterator(resources_); !it.done();
+         it.next(resources_)) {
       it->accept(*this);
     }
     return bytesWritten();
   }
 
   size_t visit(const ObjectData& object) {
-    size_t n = object.size();
+    size_t n = object.size(resources_);
     if (n < 0x10) {
       writeByte(uint8_t(0x80 + n));
     } else if (n < 0x10000) {
@@ -75,7 +77,8 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
       writeByte(0xDF);
       writeInteger(uint32_t(n));
     }
-    for (auto it = object.createIterator(); !it.done(); it.next()) {
+    for (auto it = object.createIterator(resources_); !it.done();
+         it.next(resources_)) {
       visit(it.key());
       it->accept(*this);
     }
@@ -200,6 +203,7 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
   }
 
   CountingDecorator<TWriter> writer_;
+  const ResourceManager* resources_;
 };
 
 ARDUINOJSON_END_PRIVATE_NAMESPACE

+ 1 - 1
src/ArduinoJson/Namespace.hpp

@@ -19,7 +19,7 @@
                                               ARDUINOJSON_ENABLE_INFINITY, \
                                               ARDUINOJSON_ENABLE_COMMENTS, \
                                               ARDUINOJSON_DECODE_UNICODE), \
-                        ARDUINOJSON_SLOT_OFFSET_SIZE)
+                        ARDUINOJSON_SLOT_ID_SIZE)
 
 #endif
 

+ 8 - 6
src/ArduinoJson/Object/JsonObject.hpp

@@ -56,19 +56,19 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
   // Returns the number of bytes occupied by the object.
   // https://arduinojson.org/v6/api/jsonobject/memoryusage/
   FORCE_INLINE size_t memoryUsage() const {
-    return data_ ? data_->memoryUsage() : 0;
+    return data_ ? data_->memoryUsage(resources_) : 0;
   }
 
   // Returns the depth (nesting level) of the object.
   // https://arduinojson.org/v6/api/jsonobject/nesting/
   FORCE_INLINE size_t nesting() const {
-    return detail::VariantData::nesting(collectionToVariant(data_));
+    return detail::VariantData::nesting(collectionToVariant(data_), resources_);
   }
 
   // Returns the number of members in the object.
   // https://arduinojson.org/v6/api/jsonobject/size/
   FORCE_INLINE size_t size() const {
-    return data_ ? data_->size() : 0;
+    return data_ ? data_->size(resources_) : 0;
   }
 
   // Returns an iterator to the first key-value pair of the object.
@@ -76,7 +76,7 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
   FORCE_INLINE iterator begin() const {
     if (!data_)
       return iterator();
-    return iterator(data_->createIterator(), resources_);
+    return iterator(data_->createIterator(resources_), resources_);
   }
 
   // Returns an iterator following the last key-value pair of the object.
@@ -158,7 +158,8 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
   FORCE_INLINE
       typename detail::enable_if<detail::IsString<TString>::value, bool>::type
       containsKey(const TString& key) const {
-    return detail::ObjectData::getMember(data_, detail::adaptString(key)) != 0;
+    return detail::ObjectData::getMember(data_, detail::adaptString(key),
+                                         resources_) != 0;
   }
 
   // Returns true if the object contains the specified key.
@@ -167,7 +168,8 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
   FORCE_INLINE
       typename detail::enable_if<detail::IsString<TChar*>::value, bool>::type
       containsKey(TChar* key) const {
-    return detail::ObjectData::getMember(data_, detail::adaptString(key)) != 0;
+    return detail::ObjectData::getMember(data_, detail::adaptString(key),
+                                         resources_) != 0;
   }
 
   // Creates an array and adds it to the object.

+ 11 - 9
src/ArduinoJson/Object/JsonObjectConst.hpp

@@ -45,19 +45,19 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
   // Returns the number of bytes occupied by the object.
   // https://arduinojson.org/v6/api/jsonobjectconst/memoryusage/
   FORCE_INLINE size_t memoryUsage() const {
-    return data_ ? data_->memoryUsage() : 0;
+    return data_ ? data_->memoryUsage(resources_) : 0;
   }
 
   // Returns the depth (nesting level) of the object.
   // https://arduinojson.org/v6/api/jsonobjectconst/nesting/
   FORCE_INLINE size_t nesting() const {
-    return detail::VariantData::nesting(collectionToVariant(data_));
+    return detail::VariantData::nesting(collectionToVariant(data_), resources_);
   }
 
   // Returns the number of members in the object.
   // https://arduinojson.org/v6/api/jsonobjectconst/size/
   FORCE_INLINE size_t size() const {
-    return data_ ? data_->size() : 0;
+    return data_ ? data_->size(resources_) : 0;
   }
 
   // Returns an iterator to the first key-value pair of the object.
@@ -65,7 +65,7 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
   FORCE_INLINE iterator begin() const {
     if (!data_)
       return iterator();
-    return iterator(data_->createIterator(), resources_);
+    return iterator(data_->createIterator(resources_), resources_);
   }
 
   // Returns an iterator following the last key-value pair of the object.
@@ -78,14 +78,16 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
   // https://arduinojson.org/v6/api/jsonobjectconst/containskey/
   template <typename TString>
   FORCE_INLINE bool containsKey(const TString& key) const {
-    return detail::ObjectData::getMember(data_, detail::adaptString(key)) != 0;
+    return detail::ObjectData::getMember(data_, detail::adaptString(key),
+                                         resources_) != 0;
   }
 
   // Returns true if the object contains the specified key.
   // https://arduinojson.org/v6/api/jsonobjectconst/containskey/
   template <typename TChar>
   FORCE_INLINE bool containsKey(TChar* key) const {
-    return detail::ObjectData::getMember(data_, detail::adaptString(key)) != 0;
+    return detail::ObjectData::getMember(data_, detail::adaptString(key),
+                                         resources_) != 0;
   }
 
   // Gets the member with specified key.
@@ -94,9 +96,9 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
   FORCE_INLINE typename detail::enable_if<detail::IsString<TString>::value,
                                           JsonVariantConst>::type
   operator[](const TString& key) const {
-    return JsonVariantConst(
-        detail::ObjectData::getMember(data_, detail::adaptString(key)),
-        resources_);
+    return JsonVariantConst(detail::ObjectData::getMember(
+                                data_, detail::adaptString(key), resources_),
+                            resources_);
   }
 
   // Gets the member with specified key.

+ 4 - 4
src/ArduinoJson/Object/JsonObjectImpl.hpp

@@ -54,16 +54,16 @@ template <typename TDerived>
 template <typename TString>
 inline typename enable_if<IsString<TString>::value, bool>::type
 VariantRefBase<TDerived>::containsKey(const TString& key) const {
-  return VariantData::getMember(VariantAttorney::getData(derived()),
-                                adaptString(key)) != 0;
+  return VariantData::getMember(getData(), adaptString(key),
+                                getResourceManager()) != 0;
 }
 
 template <typename TDerived>
 template <typename TChar>
 inline typename enable_if<IsString<TChar*>::value, bool>::type
 VariantRefBase<TDerived>::containsKey(TChar* key) const {
-  return VariantData::getMember(VariantAttorney::getData(derived()),
-                                adaptString(key)) != 0;
+  return VariantData::getMember(getData(), adaptString(key),
+                                getResourceManager()) != 0;
 }
 
 template <typename TDerived>

+ 2 - 2
src/ArduinoJson/Object/JsonObjectIterator.hpp

@@ -34,7 +34,7 @@ class JsonObjectIterator {
   }
 
   JsonObjectIterator& operator++() {
-    iterator_.next();
+    iterator_.next(resources_);
     return *this;
   }
 
@@ -69,7 +69,7 @@ class JsonObjectConstIterator {
   }
 
   JsonObjectConstIterator& operator++() {
-    iterator_.next();
+    iterator_.next(resources_);
     return *this;
   }
 

+ 3 - 2
src/ArduinoJson/Object/MemberProxy.hpp

@@ -46,8 +46,9 @@ class MemberProxy
   }
 
   FORCE_INLINE VariantData* getData() const {
-    return VariantData::getMember(VariantAttorney::getData(upstream_),
-                                  adaptString(key_));
+    return VariantData::getMember(
+        VariantAttorney::getData(upstream_), adaptString(key_),
+        VariantAttorney::getResourceManager(upstream_));
   }
 
   FORCE_INLINE VariantData* getOrCreateData() const {

+ 6 - 4
src/ArduinoJson/Object/ObjectData.hpp

@@ -43,13 +43,15 @@ class ObjectData : public CollectionData {
   VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources);
 
   template <typename TAdaptedString>
-  VariantData* getMember(TAdaptedString key) const;
+  VariantData* getMember(TAdaptedString key,
+                         const ResourceManager* resources) const;
 
   template <typename TAdaptedString>
-  static VariantData* getMember(const ObjectData* object, TAdaptedString key) {
+  static VariantData* getMember(const ObjectData* object, TAdaptedString key,
+                                const ResourceManager* resources) {
     if (!object)
       return nullptr;
-    return object->getMember(key);
+    return object->getMember(key, resources);
   }
 
   template <typename TAdaptedString>
@@ -65,7 +67,7 @@ class ObjectData : public CollectionData {
 
  private:
   template <typename TAdaptedString>
-  iterator findKey(TAdaptedString key) const;
+  iterator findKey(TAdaptedString key, const ResourceManager* resources) const;
 };
 
 ARDUINOJSON_END_PRIVATE_NAMESPACE

+ 8 - 6
src/ArduinoJson/Object/ObjectImpl.hpp

@@ -10,24 +10,26 @@
 ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TAdaptedString>
-inline VariantData* ObjectData::getMember(TAdaptedString key) const {
-  return findKey(key).data();
+inline VariantData* ObjectData::getMember(
+    TAdaptedString key, const ResourceManager* resources) const {
+  return findKey(key, resources).data();
 }
 
 template <typename TAdaptedString>
 VariantData* ObjectData::getOrAddMember(TAdaptedString key,
                                         ResourceManager* resources) {
-  auto it = findKey(key);
+  auto it = findKey(key, resources);
   if (!it.done())
     return it.data();
   return addMember(key, resources);
 }
 
 template <typename TAdaptedString>
-inline ObjectData::iterator ObjectData::findKey(TAdaptedString key) const {
+inline ObjectData::iterator ObjectData::findKey(
+    TAdaptedString key, const ResourceManager* resources) const {
   if (key.isNull())
     return iterator();
-  for (auto it = createIterator(); !it.done(); it.next()) {
+  for (auto it = createIterator(resources); !it.done(); it.next(resources)) {
     if (stringEquals(key, adaptString(it.key())))
       return it;
   }
@@ -37,7 +39,7 @@ inline ObjectData::iterator ObjectData::findKey(TAdaptedString key) const {
 template <typename TAdaptedString>
 inline void ObjectData::removeMember(TAdaptedString key,
                                      ResourceManager* resources) {
-  remove(findKey(key), resources);
+  remove(findKey(key, resources), resources);
 }
 
 ARDUINOJSON_END_PRIVATE_NAMESPACE

+ 7 - 7
src/ArduinoJson/Polyfills/integer.hpp

@@ -11,21 +11,21 @@
 ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <int Bits>
-struct int_t;
+struct uint_t;
 
 template <>
-struct int_t<8> {
-  typedef int8_t type;
+struct uint_t<8> {
+  typedef uint8_t type;
 };
 
 template <>
-struct int_t<16> {
-  typedef int16_t type;
+struct uint_t<16> {
+  typedef uint16_t type;
 };
 
 template <>
-struct int_t<32> {
-  typedef int32_t type;
+struct uint_t<32> {
+  typedef uint32_t type;
 };
 
 ARDUINOJSON_END_PRIVATE_NAMESPACE

+ 2 - 1
src/ArduinoJson/Serialization/measure.hpp

@@ -11,7 +11,8 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 template <template <typename> class TSerializer>
 size_t measure(ArduinoJson::JsonVariantConst source) {
   DummyWriter dp;
-  TSerializer<DummyWriter> serializer(dp);
+  TSerializer<DummyWriter> serializer(
+      dp, VariantAttorney::getResourceManager(source));
   return VariantData::accept(VariantAttorney::getData(source), serializer);
 }
 

+ 2 - 1
src/ArduinoJson/Serialization/serialize.hpp

@@ -10,7 +10,8 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <template <typename> class TSerializer, typename TWriter>
 size_t doSerialize(ArduinoJson::JsonVariantConst source, TWriter writer) {
-  TSerializer<TWriter> serializer(writer);
+  TSerializer<TWriter> serializer(writer,
+                                  VariantAttorney::getResourceManager(source));
   return VariantData::accept(VariantAttorney::getData(source), serializer);
 }
 

+ 1 - 1
src/ArduinoJson/Variant/JsonVariant.hpp

@@ -94,7 +94,7 @@ inline JsonVariant VariantRefBase<TDerived>::add() const {
 }
 
 template <typename TDerived>
-inline JsonVariant VariantRefBase<TDerived>::getVariant() const {
+inline JsonVariant VariantRefBase<TDerived>::getSlot() const {
   return JsonVariant(getData(), getResourceManager());
 }
 

+ 15 - 15
src/ArduinoJson/Variant/JsonVariantConst.hpp

@@ -50,19 +50,19 @@ class JsonVariantConst : public detail::VariantTag,
   // Returns the number of bytes occupied by the value.
   // https://arduinojson.org/v6/api/jsonvariantconst/memoryusage/
   FORCE_INLINE size_t memoryUsage() const {
-    return data_ ? data_->memoryUsage() : 0;
+    return data_ ? data_->memoryUsage(resources_) : 0;
   }
 
   // Returns the depth (nesting level) of the value.
   // https://arduinojson.org/v6/api/jsonvariantconst/nesting/
   FORCE_INLINE size_t nesting() const {
-    return detail::VariantData::nesting(data_);
+    return detail::VariantData::nesting(data_, resources_);
   }
 
   // Returns the size of the array or object.
   // https://arduinojson.org/v6/api/jsonvariantconst/size/
   size_t size() const {
-    return detail::VariantData::size(data_);
+    return detail::VariantData::size(data_, resources_);
   }
 
   // Casts the value to the specified type.
@@ -93,8 +93,8 @@ class JsonVariantConst : public detail::VariantTag,
   // Gets array's element at specified index.
   // https://arduinojson.org/v6/api/jsonvariantconst/subscript/
   FORCE_INLINE JsonVariantConst operator[](size_t index) const {
-    return JsonVariantConst(detail::VariantData::getElement(data_, index),
-                            resources_);
+    return JsonVariantConst(
+        detail::VariantData::getElement(data_, index, resources_), resources_);
   }
 
   // Gets object's member with specified key.
@@ -103,9 +103,9 @@ class JsonVariantConst : public detail::VariantTag,
   FORCE_INLINE typename detail::enable_if<detail::IsString<TString>::value,
                                           JsonVariantConst>::type
   operator[](const TString& key) const {
-    return JsonVariantConst(
-        detail::VariantData::getMember(data_, detail::adaptString(key)),
-        resources_);
+    return JsonVariantConst(detail::VariantData::getMember(
+                                data_, detail::adaptString(key), resources_),
+                            resources_);
   }
 
   // Gets object's member with specified key.
@@ -114,9 +114,9 @@ class JsonVariantConst : public detail::VariantTag,
   FORCE_INLINE typename detail::enable_if<detail::IsString<TChar*>::value,
                                           JsonVariantConst>::type
   operator[](TChar* key) const {
-    return JsonVariantConst(
-        detail::VariantData::getMember(data_, detail::adaptString(key)),
-        resources_);
+    return JsonVariantConst(detail::VariantData::getMember(
+                                data_, detail::adaptString(key), resources_),
+                            resources_);
   }
 
   // Returns true if tge object contains the specified key.
@@ -125,8 +125,8 @@ class JsonVariantConst : public detail::VariantTag,
   FORCE_INLINE
       typename detail::enable_if<detail::IsString<TString>::value, bool>::type
       containsKey(const TString& key) const {
-    return detail::VariantData::getMember(getData(),
-                                          detail::adaptString(key)) != 0;
+    return detail::VariantData::getMember(getData(), detail::adaptString(key),
+                                          resources_) != 0;
   }
 
   // Returns true if tge object contains the specified key.
@@ -135,8 +135,8 @@ class JsonVariantConst : public detail::VariantTag,
   FORCE_INLINE
       typename detail::enable_if<detail::IsString<TChar*>::value, bool>::type
       containsKey(TChar* key) const {
-    return detail::VariantData::getMember(getData(),
-                                          detail::adaptString(key)) != 0;
+    return detail::VariantData::getMember(getData(), detail::adaptString(key),
+                                          resources_) != 0;
   }
 
  protected:

+ 23 - 29
src/ArduinoJson/Variant/VariantData.hpp

@@ -185,30 +185,28 @@ class VariantData {
     }
   }
 
-  VariantData* getElement(size_t index) const {
-    auto array = asArray();
-    if (!array)
-      return nullptr;
-    return array->getElement(index);
+  VariantData* getElement(size_t index,
+                          const ResourceManager* resources) const {
+    return ArrayData::getElement(asArray(), index, resources);
   }
 
-  static VariantData* getElement(const VariantData* var, size_t index) {
-    return var != 0 ? var->getElement(index) : 0;
+  static VariantData* getElement(const VariantData* var, size_t index,
+                                 const ResourceManager* resources) {
+    return var != 0 ? var->getElement(index, resources) : 0;
   }
 
   template <typename TAdaptedString>
-  VariantData* getMember(TAdaptedString key) const {
-    auto object = asObject();
-    if (!object)
-      return nullptr;
-    return object->getMember(key);
+  VariantData* getMember(TAdaptedString key,
+                         const ResourceManager* resources) const {
+    return ObjectData::getMember(asObject(), key, resources);
   }
 
   template <typename TAdaptedString>
-  static VariantData* getMember(const VariantData* var, TAdaptedString key) {
+  static VariantData* getMember(const VariantData* var, TAdaptedString key,
+                                const ResourceManager* resources) {
     if (!var)
       return 0;
-    return var->getMember(key);
+    return var->getMember(key, resources);
   }
 
   VariantData* getOrAddElement(size_t index, ResourceManager* resources) {
@@ -276,36 +274,32 @@ class VariantData {
     return type() == VALUE_IS_LINKED_STRING || type() == VALUE_IS_OWNED_STRING;
   }
 
-  size_t memoryUsage() const {
+  size_t memoryUsage(const ResourceManager* resources) const {
     switch (type()) {
       case VALUE_IS_OWNED_STRING:
       case VALUE_IS_RAW_STRING:
         return sizeofString(content_.asOwnedString->length);
       case VALUE_IS_OBJECT:
       case VALUE_IS_ARRAY:
-        return content_.asCollection.memoryUsage();
+        return content_.asCollection.memoryUsage(resources);
       default:
         return 0;
     }
   }
 
-  void movePointers(ptrdiff_t variantDistance) {
-    if (flags_ & COLLECTION_MASK)
-      content_.asCollection.movePointers(variantDistance);
-  }
-
-  size_t nesting() const {
+  size_t nesting(const ResourceManager* resources) const {
     auto collection = asCollection();
     if (collection)
-      return collection->nesting();
+      return collection->nesting(resources);
     else
       return 0;
   }
 
-  static size_t nesting(const VariantData* var) {
+  static size_t nesting(const VariantData* var,
+                        const ResourceManager* resources) {
     if (!var)
       return 0;
-    return var->nesting();
+    return var->nesting(resources);
   }
 
   void operator=(const VariantData& src) {
@@ -455,12 +449,12 @@ class VariantData {
     content_.asOwnedString = s;
   }
 
-  size_t size() const {
-    return isCollection() ? content_.asCollection.size() : 0;
+  size_t size(const ResourceManager* resources) const {
+    return isCollection() ? content_.asCollection.size(resources) : 0;
   }
 
-  static size_t size(const VariantData* var) {
-    return var != 0 ? var->size() : 0;
+  static size_t size(const VariantData* var, const ResourceManager* resources) {
+    return var != 0 ? var->size(resources) : 0;
   }
 
   ArrayData& toArray() {

+ 6 - 6
src/ArduinoJson/Variant/VariantRefBase.hpp

@@ -58,7 +58,7 @@ class VariantRefBase : public VariantTag {
   template <typename T>
   FORCE_INLINE typename enable_if<ConverterNeedsWriteableRef<T>::value, T>::type
   as() const {
-    return Converter<T>::fromJson(getVariant());
+    return Converter<T>::fromJson(getSlot());
   }
 
   template <typename T>
@@ -92,7 +92,7 @@ class VariantRefBase : public VariantTag {
   FORCE_INLINE
       typename enable_if<ConverterNeedsWriteableRef<T>::value, bool>::type
       is() const {
-    return Converter<T>::checkJson(getVariant());
+    return Converter<T>::checkJson(getSlot());
   }
 
   // Returns true if the value is of the specified type.
@@ -127,20 +127,20 @@ class VariantRefBase : public VariantTag {
   // Returns the size of the array or object.
   // https://arduinojson.org/v6/api/jsonvariant/size/
   FORCE_INLINE size_t size() const {
-    return VariantData::size(getData());
+    return VariantData::size(getData(), getResourceManager());
   }
 
   // Returns the number of bytes occupied by the value.
   // https://arduinojson.org/v6/api/jsonvariant/memoryusage/
   FORCE_INLINE size_t memoryUsage() const {
     VariantData* data = getData();
-    return data ? data->memoryUsage() : 0;
+    return data ? data->memoryUsage(getResourceManager()) : 0;
   }
 
   // Returns the depth (nesting level) of the value.
   // https://arduinojson.org/v6/api/jsonvariant/nesting/
   FORCE_INLINE size_t nesting() const {
-    return VariantData::nesting(getData());
+    return VariantData::nesting(getData(), getResourceManager());
   }
 
   // Appends a new (null) element to the array.
@@ -269,7 +269,7 @@ class VariantRefBase : public VariantTag {
   }
 
  private:
-  FORCE_INLINE ArduinoJson::JsonVariant getVariant() const;
+  FORCE_INLINE ArduinoJson::JsonVariant getSlot() const;
 
   FORCE_INLINE ArduinoJson::JsonVariantConst getVariantConst() const {
     return ArduinoJson::JsonVariantConst(getData(), getResourceManager());

+ 6 - 26
src/ArduinoJson/Variant/VariantSlot.hpp

@@ -5,7 +5,6 @@
 #pragma once
 
 #include <ArduinoJson/Memory/ResourceManager.hpp>
-#include <ArduinoJson/Polyfills/integer.hpp>
 #include <ArduinoJson/Polyfills/limits.hpp>
 #include <ArduinoJson/Polyfills/type_traits.hpp>
 #include <ArduinoJson/Variant/VariantContent.hpp>
@@ -14,15 +13,13 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 struct StringNode;
 
-typedef int_t<ARDUINOJSON_SLOT_OFFSET_SIZE * 8>::type VariantSlotDiff;
-
 class VariantSlot {
   // CAUTION: same layout as VariantData
   // we cannot use composition because it adds padding
   // (+20% on ESP8266 for example)
   VariantContent content_;
   uint8_t flags_;
-  VariantSlotDiff next_;
+  SlotId next_;
   const char* key_;
 
  public:
@@ -33,7 +30,7 @@ class VariantSlot {
 
   static void operator delete(void*, void*) noexcept {}
 
-  VariantSlot() : flags_(0), next_(0), key_(0) {}
+  VariantSlot() : flags_(0), next_(NULL_SLOT), key_(0) {}
 
   VariantData* data() {
     return reinterpret_cast<VariantData*>(&content_);
@@ -43,29 +40,12 @@ class VariantSlot {
     return reinterpret_cast<const VariantData*>(&content_);
   }
 
-  VariantSlot* next() {
-    return next_ ? this + next_ : 0;
-  }
-
-  const VariantSlot* next() const {
-    return const_cast<VariantSlot*>(this)->next();
-  }
-
-  void setNext(VariantSlot* slot) {
-    ARDUINOJSON_ASSERT(!slot || slot - this >=
-                                    numeric_limits<VariantSlotDiff>::lowest());
-    ARDUINOJSON_ASSERT(!slot || slot - this <=
-                                    numeric_limits<VariantSlotDiff>::highest());
-    next_ = VariantSlotDiff(slot ? slot - this : 0);
+  SlotId next() const {
+    return next_;
   }
 
-  void setNextNotNull(VariantSlot* slot) {
-    ARDUINOJSON_ASSERT(slot != 0);
-    ARDUINOJSON_ASSERT(slot - this >=
-                       numeric_limits<VariantSlotDiff>::lowest());
-    ARDUINOJSON_ASSERT(slot - this <=
-                       numeric_limits<VariantSlotDiff>::highest());
-    next_ = VariantSlotDiff(slot - this);
+  void setNext(SlotId slot) {
+    next_ = slot;
   }
 
   void setKey(const char* k) {