Benoit Blanchon 7 месяцев назад
Родитель
Сommit
84fcd90658
35 измененных файлов с 501 добавлено и 603 удалено
  1. 6 6
      extras/tests/ResourceManager/StringBuffer.cpp
  2. 17 15
      extras/tests/ResourceManager/StringBuilder.cpp
  3. 3 8
      src/ArduinoJson/Array/ElementProxy.hpp
  4. 1 1
      src/ArduinoJson/Array/JsonArray.hpp
  5. 1 1
      src/ArduinoJson/Array/JsonArrayConst.hpp
  6. 1 1
      src/ArduinoJson/Collection/CollectionData.hpp
  7. 12 11
      src/ArduinoJson/Collection/CollectionImpl.hpp
  8. 1 1
      src/ArduinoJson/Deserialization/deserialize.hpp
  9. 18 17
      src/ArduinoJson/Document/JsonDocument.hpp
  10. 21 18
      src/ArduinoJson/Json/JsonDeserializer.hpp
  11. 4 4
      src/ArduinoJson/Json/JsonSerializer.hpp
  12. 2 2
      src/ArduinoJson/Json/PrettyJsonSerializer.hpp
  13. 1 1
      src/ArduinoJson/Memory/ResourceManager.hpp
  14. 1 1
      src/ArduinoJson/Memory/ResourceManagerImpl.hpp
  15. 3 5
      src/ArduinoJson/MsgPack/MsgPackBinary.hpp
  16. 10 10
      src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp
  17. 4 6
      src/ArduinoJson/MsgPack/MsgPackExtension.hpp
  18. 4 4
      src/ArduinoJson/MsgPack/MsgPackSerializer.hpp
  19. 1 1
      src/ArduinoJson/Object/JsonObject.hpp
  20. 1 1
      src/ArduinoJson/Object/JsonObjectConst.hpp
  21. 4 2
      src/ArduinoJson/Object/JsonPair.hpp
  22. 3 8
      src/ArduinoJson/Object/MemberProxy.hpp
  23. 2 0
      src/ArduinoJson/Object/ObjectData.hpp
  24. 4 2
      src/ArduinoJson/Object/ObjectImpl.hpp
  25. 1 1
      src/ArduinoJson/Serialization/measure.hpp
  26. 1 1
      src/ArduinoJson/Serialization/serialize.hpp
  27. 37 60
      src/ArduinoJson/Variant/ConverterImpl.hpp
  28. 9 7
      src/ArduinoJson/Variant/JsonVariant.hpp
  29. 17 23
      src/ArduinoJson/Variant/JsonVariantConst.hpp
  30. 1 3
      src/ArduinoJson/Variant/JsonVariantVisitor.hpp
  31. 10 0
      src/ArduinoJson/Variant/VariantAttorney.hpp
  32. 203 292
      src/ArduinoJson/Variant/VariantData.hpp
  33. 67 62
      src/ArduinoJson/Variant/VariantImpl.hpp
  34. 17 13
      src/ArduinoJson/Variant/VariantRefBase.hpp
  35. 13 15
      src/ArduinoJson/Variant/VariantRefBaseImpl.hpp

+ 6 - 6
extras/tests/ResourceManager/StringBuffer.cpp

@@ -21,8 +21,8 @@ TEST_CASE("StringBuffer") {
     strcpy(ptr, "hi!");
     sb.save(&variant);
 
-    REQUIRE(variant.type() == VariantType::TinyString);
-    REQUIRE(variant.asString(&resources) == "hi!");
+    REQUIRE(variant.type == VariantType::TinyString);
+    REQUIRE(VariantImpl(&variant, &resources).asString() == "hi!");
   }
 
   SECTION("Tiny string can't contain NUL") {
@@ -30,9 +30,9 @@ TEST_CASE("StringBuffer") {
     memcpy(ptr, "a\0b", 3);
     sb.save(&variant);
 
-    REQUIRE(variant.type() == VariantType::OwnedString);
+    REQUIRE(variant.type == VariantType::OwnedString);
 
-    auto str = variant.asString(&resources);
+    auto str = VariantImpl(&variant, &resources).asString();
     REQUIRE(str.size() == 3);
     REQUIRE(str.c_str()[0] == 'a');
     REQUIRE(str.c_str()[1] == 0);
@@ -44,7 +44,7 @@ TEST_CASE("StringBuffer") {
     strcpy(ptr, "alfa");
     sb.save(&variant);
 
-    REQUIRE(variant.type() == VariantType::OwnedString);
-    REQUIRE(variant.asString(&resources) == "alfa");
+    REQUIRE(variant.type == VariantType::OwnedString);
+    REQUIRE(VariantImpl(&variant, &resources).asString() == "alfa");
   }
 }

+ 17 - 15
extras/tests/ResourceManager/StringBuilder.cpp

@@ -26,7 +26,7 @@ TEST_CASE("StringBuilder") {
     REQUIRE(spyingAllocator.log() == AllocatorLog{
                                          Allocate(sizeofStringBuffer()),
                                      });
-    REQUIRE(data.type() == VariantType::TinyString);
+    REQUIRE(data.type == VariantType::TinyString);
   }
 
   SECTION("Tiny string") {
@@ -45,8 +45,8 @@ TEST_CASE("StringBuilder") {
     str.save(&data);
 
     REQUIRE(resources.overflowed() == false);
-    REQUIRE(data.type() == VariantType::TinyString);
-    REQUIRE(data.asString(&resources) == "url");
+    REQUIRE(data.type == VariantType::TinyString);
+    REQUIRE(VariantImpl(&data, &resources).asString() == "url");
   }
 
   SECTION("Short string fits in first allocation") {
@@ -134,10 +134,10 @@ TEST_CASE("StringBuilder::save() deduplicates strings") {
     auto s2 = saveString(builder, "world");
     auto s3 = saveString(builder, "hello");
 
-    REQUIRE(s1.asString(&resources) == "hello");
-    REQUIRE(s2.asString(&resources) == "world");
-    REQUIRE(+s1.asString(&resources).c_str() ==
-            +s3.asString(&resources).c_str());  // same address
+    REQUIRE(VariantImpl(&s1, &resources).asString() == "hello");
+    REQUIRE(VariantImpl(&s2, &resources).asString() == "world");
+    REQUIRE(+VariantImpl(&s1, &resources).asString().c_str() ==
+            +VariantImpl(&s3, &resources).asString().c_str());  // same address
 
     REQUIRE(spy.log() ==
             AllocatorLog{
@@ -153,10 +153,11 @@ TEST_CASE("StringBuilder::save() deduplicates strings") {
     auto s1 = saveString(builder, "hello world");
     auto s2 = saveString(builder, "hello");
 
-    REQUIRE(s1.asString(&resources) == "hello world");
-    REQUIRE(s2.asString(&resources) == "hello");
-    REQUIRE(+s2.asString(&resources).c_str() !=
-            +s1.asString(&resources).c_str());  // different address
+    REQUIRE(VariantImpl(&s1, &resources).asString() == "hello world");
+    REQUIRE(VariantImpl(&s2, &resources).asString() == "hello");
+    REQUIRE(
+        +VariantImpl(&s1, &resources).asString().c_str() !=
+        +VariantImpl(&s2, &resources).asString().c_str());  // different address
 
     REQUIRE(spy.log() ==
             AllocatorLog{
@@ -171,10 +172,11 @@ TEST_CASE("StringBuilder::save() deduplicates strings") {
     auto s1 = saveString(builder, "hello world");
     auto s2 = saveString(builder, "worl");
 
-    REQUIRE(s1.asString(&resources) == "hello world");
-    REQUIRE(s2.asString(&resources) == "worl");
-    REQUIRE(s2.asString(&resources).c_str() !=
-            s1.asString(&resources).c_str());  // different address
+    REQUIRE(VariantImpl(&s1, &resources).asString() == "hello world");
+    REQUIRE(VariantImpl(&s2, &resources).asString() == "worl");
+    REQUIRE(
+        VariantImpl(&s1, &resources).asString().c_str() !=
+        VariantImpl(&s2, &resources).asString().c_str());  // different address
 
     REQUIRE(spy.log() ==
             AllocatorLog{

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

@@ -55,17 +55,12 @@ class ElementProxy : public VariantRefBase<ElementProxy<TUpstream>>,
   }
 
   FORCE_INLINE VariantData* getData() const {
-    auto data = VariantAttorney::getData(upstream_);
-    auto resources = VariantAttorney::getResourceManager(upstream_);
-    return VariantData::asArray(data, resources).getElement(index_);
+    return VariantAttorney::getVariantImpl(upstream_).getElement(index_);
   }
 
   VariantData* getOrCreateData() const {
-    auto data = VariantAttorney::getOrCreateData(upstream_);
-    if (!data)
-      return nullptr;
-    return data->getOrAddElement(
-        index_, VariantAttorney::getResourceManager(upstream_));
+    return VariantAttorney::getOrCreateVariantImpl(upstream_).getOrAddElement(
+        index_);
   }
 
   TUpstream upstream_;

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

@@ -24,7 +24,7 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
 
   // INTERNAL USE ONLY
   JsonArray(detail::VariantData* data, detail::ResourceManager* resources)
-      : impl_(detail::VariantData::asArray(data, resources)) {}
+      : impl_(detail::VariantImpl(data, resources).asArray()) {}
 
   // INTERNAL USE ONLY
   JsonArray(const detail::ArrayImpl& impl) : impl_(impl) {}

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

@@ -38,7 +38,7 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
 
   // INTERNAL USE ONLY
   JsonArrayConst(detail::VariantData* data, detail::ResourceManager* resources)
-      : impl_(detail::VariantData::asArray(data, resources)) {}
+      : impl_(detail::VariantImpl(data, resources).asArray()) {}
 
   // INTERNAL USE ONLY
   JsonArrayConst(const detail::ArrayImpl& impl) : impl_(impl) {}

+ 1 - 1
src/ArduinoJson/Collection/CollectionData.hpp

@@ -12,7 +12,7 @@
 
 ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
-class VariantData;
+struct VariantData;
 class ResourceManager;
 
 class CollectionIterator {

+ 12 - 11
src/ArduinoJson/Collection/CollectionImpl.hpp

@@ -14,7 +14,7 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 inline void CollectionIterator::next(const ResourceManager* resources) {
   ARDUINOJSON_ASSERT(slot_);
-  auto nextId = slot_->next();
+  auto nextId = slot_->next;
   slot_ = resources->getVariant(nextId);
   currentId_ = nextId;
 }
@@ -31,7 +31,7 @@ inline void CollectionImpl::appendOne(Slot<VariantData> slot) {
 
   if (data_->tail != NULL_SLOT) {
     auto tail = resources_->getVariant(data_->tail);
-    tail->setNext(slot.id());
+    tail->next = slot.id();
     data_->tail = slot.id();
   } else {
     data_->head = slot.id();
@@ -44,11 +44,11 @@ inline void CollectionImpl::appendPair(Slot<VariantData> key,
   ARDUINOJSON_ASSERT(data_ != nullptr);
   ARDUINOJSON_ASSERT(resources_ != nullptr);
 
-  key->setNext(value.id());
+  key->next = value.id();
 
   if (data_->tail != NULL_SLOT) {
     auto tail = resources_->getVariant(data_->tail);
-    tail->setNext(key.id());
+    tail->next = key.id();
     data_->tail = value.id();
   } else {
     data_->head = key.id();
@@ -63,7 +63,7 @@ inline void CollectionImpl::clear() {
   while (next != NULL_SLOT) {
     auto currId = next;
     auto slot = resources_->getVariant(next);
-    next = slot->next();
+    next = slot->next;
     resources_->freeVariant({slot, currId});
   }
 
@@ -80,7 +80,7 @@ inline Slot<VariantData> CollectionImpl::getPreviousSlot(
     if (currentSlot == target)
       break;
     prev = Slot<VariantData>(currentSlot, currentId);
-    currentId = currentSlot->next();
+    currentId = currentSlot->next;
   }
   return prev;
 }
@@ -90,9 +90,9 @@ inline void CollectionImpl::removeOne(iterator it) {
     return;
   auto curr = it.slot_;
   auto prev = getPreviousSlot(curr);
-  auto next = curr->next();
+  auto next = curr->next;
   if (prev)
-    prev->setNext(next);
+    prev->next = next;
   else
     data_->head = next;
   if (next == NULL_SLOT)
@@ -106,11 +106,11 @@ inline void CollectionImpl::removePair(ObjectImpl::iterator it) {
 
   auto keySlot = it.slot_;
 
-  auto valueId = keySlot->next();
+  auto valueId = keySlot->next;
   auto valueSlot = resources_->getVariant(valueId);
 
   // remove value slot
-  keySlot->setNext(valueSlot->next());
+  keySlot->next = valueSlot->next;
   resources_->freeVariant({valueSlot, valueId});
 
   // remove key slot
@@ -122,7 +122,8 @@ inline size_t CollectionImpl::nesting() const {
     return 0;
   size_t maxChildNesting = 0;
   for (auto it = createIterator(); !it.done(); it.next(resources_)) {
-    size_t childNesting = it->nesting(resources_);
+    VariantImpl variant(it.data(), resources_);
+    size_t childNesting = variant.nesting();
     if (childNesting > maxChildNesting)
       maxChildNesting = childNesting;
   }

+ 1 - 1
src/ArduinoJson/Deserialization/deserialize.hpp

@@ -50,7 +50,7 @@ DeserializationError doDeserialize(TDestination&& dst, TReader reader,
   auto resources = VariantAttorney::getResourceManager(dst);
   dst.clear();
   auto err = TDeserializer<TReader>(resources, reader)
-                 .parse(*data, options.filter, options.nestingLimit);
+                 .parse(data, options.filter, options.nestingLimit);
   shrinkJsonDocument(dst);
   return err;
 }

+ 18 - 17
src/ArduinoJson/Document/JsonDocument.hpp

@@ -88,7 +88,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   // https://arduinojson.org/v7/api/jsondocument/clear/
   void clear() {
     resources_.clear();
-    data_.reset();
+    data_.type = detail::VariantType::Null;
   }
 
   // Returns true if the root is of the specified type.
@@ -120,13 +120,13 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   // Returns the depth (nesting level) of the array.
   // https://arduinojson.org/v7/api/jsondocument/nesting/
   size_t nesting() const {
-    return data_.nesting(&resources_);
+    return getVariantImpl().nesting();
   }
 
   // Returns the number of elements in the root array or object.
   // https://arduinojson.org/v7/api/jsondocument/size/
   size_t size() const {
-    return data_.size(&resources_);
+    return getVariantImpl().size();
   }
 
   // Copies the specified document.
@@ -165,7 +165,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   template <typename TChar>
   ARDUINOJSON_DEPRECATED("use doc[\"key\"].is<T>() instead")
   bool containsKey(TChar* key) const {
-    return data_.getMember(detail::adaptString(key), &resources_) != 0;
+    return getVariantImpl().getMember(detail::adaptString(key)) != 0;
   }
 
   // DEPRECATED: use obj[key].is<T>() instead
@@ -174,7 +174,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
             detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
   ARDUINOJSON_DEPRECATED("use doc[key].is<T>() instead")
   bool containsKey(const TString& key) const {
-    return data_.getMember(detail::adaptString(key), &resources_) != 0;
+    return getVariantImpl().getMember(detail::adaptString(key)) != 0;
   }
 
   // DEPRECATED: use obj[key].is<T>() instead
@@ -212,7 +212,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
             detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
   JsonVariantConst operator[](const TString& key) const {
     return JsonVariantConst(
-        data_.getMember(detail::adaptString(key), &resources_), &resources_);
+        getVariantImpl().getMember(detail::adaptString(key)), &resources_);
   }
 
   // Gets a root object's member.
@@ -223,7 +223,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
                                 int> = 0>
   JsonVariantConst operator[](TChar* key) const {
     return JsonVariantConst(
-        data_.getMember(detail::adaptString(key), &resources_), &resources_);
+        getVariantImpl().getMember(detail::adaptString(key)), &resources_);
   }
 
   // Gets or sets a root array's element.
@@ -237,7 +237,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   // Gets a root array's member.
   // https://arduinojson.org/v7/api/jsondocument/subscript/
   JsonVariantConst operator[](size_t index) const {
-    return JsonVariantConst(data_.getElement(index, &resources_), &resources_);
+    return JsonVariantConst(getVariantImpl().getElement(index), &resources_);
   }
 
   // Gets or sets a root object's member.
@@ -267,14 +267,14 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   template <typename T, detail::enable_if_t<
                             detail::is_same<T, JsonVariant>::value, int> = 0>
   JsonVariant add() {
-    return JsonVariant(data_.addElement(&resources_), &resources_);
+    return JsonVariant(getVariantImpl().addElement(), &resources_);
   }
 
   // Appends a value to the root array.
   // https://arduinojson.org/v7/api/jsondocument/add/
   template <typename TValue>
   bool add(const TValue& value) {
-    return data_.addValue(value, &resources_);
+    return getVariantImpl().addValue(value);
   }
 
   // Appends a value to the root array.
@@ -282,7 +282,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   template <typename TChar,
             detail::enable_if_t<!detail::is_const<TChar>::value, int> = 0>
   bool add(TChar* value) {
-    return data_.addValue(value, &resources_);
+    return getVariantImpl().addValue(value);
   }
 
   // Removes an element of the root array.
@@ -290,8 +290,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   template <typename T,
             detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
   void remove(T index) {
-    detail::VariantData::removeElement(getData(), size_t(index),
-                                       getResourceManager());
+    getVariantImpl().removeElement(size_t(index));
   }
 
   // Removes a member of the root object.
@@ -301,8 +300,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
                                     !detail::is_const<TChar>::value,
                                 int> = 0>
   void remove(TChar* key) {
-    detail::VariantData::removeMember(getData(), detail::adaptString(key),
-                                      getResourceManager());
+    getVariantImpl().removeMember(detail::adaptString(key));
   }
 
   // Removes a member of the root object.
@@ -310,8 +308,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   template <typename TString,
             detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
   void remove(const TString& key) {
-    detail::VariantData::removeMember(getData(), detail::adaptString(key),
-                                      getResourceManager());
+    getVariantImpl().removeMember(detail::adaptString(key));
   }
 
   // Removes a member of the root object or an element of the root array.
@@ -391,6 +388,10 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
   }
 
  private:
+  detail::VariantImpl getVariantImpl() const {
+    return detail::VariantImpl(&data_, &resources_);
+  }
+
   JsonVariant getVariant() {
     return JsonVariant(&data_, &resources_);
   }

+ 21 - 18
src/ArduinoJson/Json/JsonDeserializer.hpp

@@ -28,13 +28,13 @@ class JsonDeserializer {
         resources_(resources) {}
 
   template <typename TFilter>
-  DeserializationError parse(VariantData& variant, TFilter filter,
+  DeserializationError parse(VariantData* variant, TFilter filter,
                              DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
 
     err = parseVariant(variant, filter, nestingLimit);
 
-    if (!err && latch_.last() != 0 && variant.isFloat()) {
+    if (!err && latch_.last() != 0 && variant->isFloat()) {
       // We don't detect trailing characters earlier, so we need to check now
       return DeserializationError::InvalidInput;
     }
@@ -60,7 +60,7 @@ class JsonDeserializer {
 
   template <typename TFilter>
   DeserializationError::Code parseVariant(
-      VariantData& variant, TFilter filter,
+      VariantData* variant, TFilter filter,
       DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
 
@@ -71,14 +71,15 @@ class JsonDeserializer {
     switch (current()) {
       case '[':
         if (filter.allowArray())
-          return parseArray(variant.toArray(resources_), filter, nestingLimit);
+          return parseArray(VariantImpl(variant, resources_).toArray(), filter,
+                            nestingLimit);
         else
           return skipArray(nestingLimit);
 
       case '{':
         if (filter.allowObject())
-          return parseObject(variant.toObject(resources_), filter,
-                             nestingLimit);
+          return parseObject(VariantImpl(variant, resources_).toObject(),
+                             filter, nestingLimit);
         else
           return skipObject(nestingLimit);
 
@@ -91,12 +92,12 @@ class JsonDeserializer {
 
       case 't':
         if (filter.allowValue())
-          variant.setBoolean(true);
+          variant->setBoolean(true);
         return skipKeyword("true");
 
       case 'f':
         if (filter.allowValue())
-          variant.setBoolean(false);
+          variant->setBoolean(false);
         return skipKeyword("false");
 
       case 'n':
@@ -178,7 +179,7 @@ class JsonDeserializer {
           return DeserializationError::NoMemory;
 
         // 1 - Parse value
-        err = parseVariant(*value, elementFilter, nestingLimit.decrement());
+        err = parseVariant(value, elementFilter, nestingLimit.decrement());
         if (err)
           return err;
       } else {
@@ -282,11 +283,11 @@ class JsonDeserializer {
 
           stringBuilder_.save(keyVariant);
         } else {
-          member->clear(resources_);
+          VariantImpl(member, resources_).clear();
         }
 
         // Parse value
-        err = parseVariant(*member, memberFilter, nestingLimit.decrement());
+        err = parseVariant(member, memberFilter, nestingLimit.decrement());
         if (err)
           return err;
       } else {
@@ -380,7 +381,7 @@ class JsonDeserializer {
     }
   }
 
-  DeserializationError::Code parseStringValue(VariantData& variant) {
+  DeserializationError::Code parseStringValue(VariantData* variant) {
     DeserializationError::Code err;
 
     stringBuilder_.startString();
@@ -389,7 +390,7 @@ class JsonDeserializer {
     if (err)
       return err;
 
-    stringBuilder_.save(&variant);
+    stringBuilder_.save(variant);
 
     return DeserializationError::Ok;
   }
@@ -505,7 +506,7 @@ class JsonDeserializer {
     return DeserializationError::Ok;
   }
 
-  DeserializationError::Code parseNumericValue(VariantData& result) {
+  DeserializationError::Code parseNumericValue(VariantData* result) {
     uint8_t n = 0;
 
     char c = current();
@@ -519,26 +520,28 @@ class JsonDeserializer {
     auto number = parseNumber(buffer_);
     switch (number.type()) {
       case NumberType::UnsignedInteger:
-        if (result.setInteger(number.asUnsignedInteger(), resources_))
+        if (VariantImpl(result, resources_)
+                .setInteger(number.asUnsignedInteger()))
           return DeserializationError::Ok;
         else
           return DeserializationError::NoMemory;
 
       case NumberType::SignedInteger:
-        if (result.setInteger(number.asSignedInteger(), resources_))
+        if (VariantImpl(result, resources_)
+                .setInteger(number.asSignedInteger()))
           return DeserializationError::Ok;
         else
           return DeserializationError::NoMemory;
 
       case NumberType::Float:
-        if (result.setFloat(number.asFloat(), resources_))
+        if (VariantImpl(result, resources_).setFloat(number.asFloat()))
           return DeserializationError::Ok;
         else
           return DeserializationError::NoMemory;
 
 #if ARDUINOJSON_USE_DOUBLE
       case NumberType::Double:
-        if (result.setFloat(number.asDouble(), resources_))
+        if (VariantImpl(result, resources_).setFloat(number.asDouble()))
           return DeserializationError::Ok;
         else
           return DeserializationError::NoMemory;

+ 4 - 4
src/ArduinoJson/Json/JsonSerializer.hpp

@@ -27,9 +27,9 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
     while (slotId != NULL_SLOT) {
       auto slot = resources_->getVariant(slotId);
 
-      slot->accept(*this, resources_);
+      VariantImpl(slot, resources_).accept(*this);
 
-      slotId = slot->next();
+      slotId = slot->next;
 
       if (slotId != NULL_SLOT)
         write(',');
@@ -48,9 +48,9 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
 
     while (slotId != NULL_SLOT) {
       auto slot = resources_->getVariant(slotId);
-      slot->accept(*this, resources_);
+      VariantImpl(slot, resources_).accept(*this);
 
-      slotId = slot->next();
+      slotId = slot->next;
 
       if (slotId != NULL_SLOT)
         write(isKey ? ':' : ',');

+ 2 - 2
src/ArduinoJson/Json/PrettyJsonSerializer.hpp

@@ -26,7 +26,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
       nesting_++;
       while (!it.done()) {
         indent();
-        it->accept(*this, base::resources_);
+        VariantImpl(it.data(), base::resources_).accept(*this);
 
         it.next(base::resources_);
         base::write(it.done() ? "\r\n" : ",\r\n");
@@ -49,7 +49,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
       while (!it.done()) {
         if (isKey)
           indent();
-        it->accept(*this, base::resources_);
+        VariantImpl(it.data(), base::resources_).accept(*this);
         it.next(base::resources_);
         if (isKey)
           base::write(": ");

+ 1 - 1
src/ArduinoJson/Memory/ResourceManager.hpp

@@ -14,7 +14,7 @@
 
 ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
-class VariantData;
+struct VariantData;
 class VariantWithId;
 
 class ResourceManager {

+ 1 - 1
src/ArduinoJson/Memory/ResourceManagerImpl.hpp

@@ -22,7 +22,7 @@ inline Slot<VariantData> ResourceManager::allocVariant() {
 }
 
 inline void ResourceManager::freeVariant(Slot<VariantData> slot) {
-  slot->clear(this);
+  VariantImpl(slot.ptr(), this).clear();
   variantPools_.freeSlot(slot);
 }
 

+ 3 - 5
src/ArduinoJson/MsgPack/MsgPackBinary.hpp

@@ -29,7 +29,7 @@ struct Converter<MsgPackBinary> : private detail::VariantAttorney {
     if (!data)
       return;
     auto resources = getResourceManager(dst);
-    data->clear(resources);
+    detail::VariantImpl(data, resources).clear();
     if (src.data()) {
       size_t headerSize = src.size() >= 0x10000 ? 5
                           : src.size() >= 0x100 ? 3
@@ -66,10 +66,8 @@ struct Converter<MsgPackBinary> : private detail::VariantAttorney {
   }
 
   static MsgPackBinary fromJson(JsonVariantConst src) {
-    auto data = getData(src);
-    if (!data)
-      return {};
-    auto rawstr = data->asRawString();
+    auto variant = VariantAttorney::getVariantImpl(src);
+    auto rawstr = variant.asRawString();
     auto p = reinterpret_cast<const uint8_t*>(rawstr.c_str());
     auto n = rawstr.size();
     if (n >= 2 && p[0] == 0xc4) {  // bin 8

+ 10 - 10
src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp

@@ -24,10 +24,10 @@ class MsgPackDeserializer {
         foundSomething_(false) {}
 
   template <typename TFilter>
-  DeserializationError parse(VariantData& variant, TFilter filter,
+  DeserializationError parse(VariantData* variant, TFilter filter,
                              DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
-    err = parseVariant(&variant, filter, nestingLimit);
+    err = parseVariant(variant, filter, nestingLimit);
     return foundSomething_ ? err : DeserializationError::EmptyInput;
   }
 
@@ -91,7 +91,7 @@ class MsgPackDeserializer {
 
     if (code <= 0x7f || code >= 0xe0) {  // fixint
       if (allowValue)
-        variant->setInteger(static_cast<int8_t>(code), resources_);
+        VariantImpl(variant, resources_).setInteger(static_cast<int8_t>(code));
       return DeserializationError::Ok;
     }
 
@@ -231,14 +231,14 @@ class MsgPackDeserializer {
     if (isSigned) {
       auto truncatedValue = static_cast<JsonInteger>(signedValue);
       if (truncatedValue == signedValue) {
-        if (!variant->setInteger(truncatedValue, resources_))
+        if (!VariantImpl(variant, resources_).setInteger(truncatedValue))
           return DeserializationError::NoMemory;
       }
       // else set null on overflow
     } else {
       auto truncatedValue = static_cast<JsonUInt>(unsignedValue);
       if (truncatedValue == unsignedValue)
-        if (!variant->setInteger(truncatedValue, resources_))
+        if (!VariantImpl(variant, resources_).setInteger(truncatedValue))
           return DeserializationError::NoMemory;
       // else set null on overflow
     }
@@ -257,7 +257,7 @@ class MsgPackDeserializer {
       return err;
 
     fixEndianness(value);
-    variant->setFloat(value, resources_);
+    VariantImpl(variant, resources_).setFloat(value);
 
     return DeserializationError::Ok;
   }
@@ -273,7 +273,7 @@ class MsgPackDeserializer {
       return err;
 
     fixEndianness(value);
-    if (variant->setFloat(value, resources_))
+    if (VariantImpl(variant, resources_).setFloat(value))
       return DeserializationError::Ok;
     else
       return DeserializationError::NoMemory;
@@ -293,7 +293,7 @@ class MsgPackDeserializer {
 
     doubleToFloat(i, o);
     fixEndianness(value);
-    variant->setFloat(value, resources_);
+    VariantImpl(variant, resources_).setFloat(value);
 
     return DeserializationError::Ok;
   }
@@ -352,7 +352,7 @@ class MsgPackDeserializer {
     ArrayImpl array;
     if (allowArray) {
       ARDUINOJSON_ASSERT(variant != 0);
-      array = variant->toArray(resources_);
+      array = VariantImpl(variant, resources_).toArray();
     }
 
     TFilter elementFilter = filter[0U];
@@ -388,7 +388,7 @@ class MsgPackDeserializer {
     ObjectImpl object;
     if (filter.allowObject()) {
       ARDUINOJSON_ASSERT(variant != 0);
-      object = variant->toObject(resources_);
+      object = VariantImpl(variant, resources_).toObject();
     }
 
     for (; n; --n) {

+ 4 - 6
src/ArduinoJson/MsgPack/MsgPackExtension.hpp

@@ -31,11 +31,11 @@ class MsgPackExtension {
 template <>
 struct Converter<MsgPackExtension> : private detail::VariantAttorney {
   static void toJson(MsgPackExtension src, JsonVariant dst) {
-    auto data = VariantAttorney::getData(dst);
+    auto data = getData(dst);
     if (!data)
       return;
     auto resources = getResourceManager(dst);
-    data->clear(resources);
+    detail::VariantImpl(data, resources).clear();
     if (src.data()) {
       uint8_t format, sizeBytes;
       if (src.size() >= 0x10000) {
@@ -80,10 +80,8 @@ struct Converter<MsgPackExtension> : private detail::VariantAttorney {
   }
 
   static MsgPackExtension fromJson(JsonVariantConst src) {
-    auto data = getData(src);
-    if (!data)
-      return {};
-    auto rawstr = data->asRawString();
+    auto variant = VariantAttorney::getVariantImpl(src);
+    auto rawstr = variant.asRawString();
     if (rawstr.size() == 0)
       return {};
     auto p = reinterpret_cast<const uint8_t*>(rawstr.c_str());

+ 4 - 4
src/ArduinoJson/MsgPack/MsgPackSerializer.hpp

@@ -62,8 +62,8 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
     auto slotId = array.head();
     while (slotId != NULL_SLOT) {
       auto slot = resources_->getVariant(slotId);
-      slot->accept(*this, resources_);
-      slotId = slot->next();
+      VariantImpl(slot, resources_).accept(*this);
+      slotId = slot->next;
     }
 
     return bytesWritten();
@@ -84,8 +84,8 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
     auto slotId = object.head();
     while (slotId != NULL_SLOT) {
       auto slot = resources_->getVariant(slotId);
-      slot->accept(*this, resources_);
-      slotId = slot->next();
+      VariantImpl(slot, resources_).accept(*this);
+      slotId = slot->next;
     }
 
     return bytesWritten();

+ 1 - 1
src/ArduinoJson/Object/JsonObject.hpp

@@ -27,7 +27,7 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
 
   // INTERNAL USE ONLY
   JsonObject(detail::VariantData* data, detail::ResourceManager* resource)
-      : impl_(detail::VariantData::asObject(data, resource)) {}
+      : impl_(detail::VariantImpl(data, resource).asObject()) {}
 
   operator JsonVariant() const {
     return JsonVariant(getData(), getResourceManager());

+ 1 - 1
src/ArduinoJson/Object/JsonObjectConst.hpp

@@ -23,7 +23,7 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
 
   // INTERNAL USE ONLY
   JsonObjectConst(detail::VariantData* data, detail::ResourceManager* resources)
-      : impl_(detail::VariantData::asObject(data, resources)) {}
+      : impl_(detail::VariantImpl(data, resources).asObject()) {}
 
   // INTERNAL USE ONLY
   JsonObjectConst(const detail::ObjectImpl& impl) : impl_(impl) {}

+ 4 - 2
src/ArduinoJson/Object/JsonPair.hpp

@@ -18,7 +18,8 @@ class JsonPair {
   JsonPair(detail::ObjectImpl::iterator iterator,
            detail::ResourceManager* resources) {
     if (!iterator.done()) {
-      key_ = iterator->asString(resources);
+      detail::VariantImpl variant(iterator.data(), resources);
+      key_ = variant.asString();
       iterator.next(resources);
       value_ = JsonVariant(iterator.data(), resources);
     }
@@ -46,7 +47,8 @@ class JsonPairConst {
   JsonPairConst(detail::ObjectImpl::iterator iterator,
                 detail::ResourceManager* resources) {
     if (!iterator.done()) {
-      key_ = iterator->asString(resources);
+      detail::VariantImpl variant(iterator.data(), resources);
+      key_ = variant.asString();
       iterator.next(resources);
       value_ = JsonVariantConst(iterator.data(), resources);
     }

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

@@ -56,17 +56,12 @@ class MemberProxy
   }
 
   VariantData* getData() const {
-    return VariantData::getMember(
-        VariantAttorney::getData(upstream_), key_,
-        VariantAttorney::getResourceManager(upstream_));
+    return VariantAttorney::getVariantImpl(upstream_).getMember(key_);
   }
 
   VariantData* getOrCreateData() const {
-    auto data = VariantAttorney::getOrCreateData(upstream_);
-    if (!data)
-      return nullptr;
-    return data->getOrAddMember(key_,
-                                VariantAttorney::getResourceManager(upstream_));
+    return VariantAttorney::getOrCreateVariantImpl(upstream_).getOrAddMember(
+        key_);
   }
 
  private:

+ 2 - 0
src/ArduinoJson/Object/ObjectData.hpp

@@ -8,6 +8,8 @@
 
 ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
+class VariantImpl;
+
 class ObjectImpl : public CollectionImpl {
  public:
   ObjectImpl() {}

+ 4 - 2
src/ArduinoJson/Object/ObjectImpl.hpp

@@ -33,7 +33,8 @@ inline ObjectImpl::iterator ObjectImpl::findKey(TAdaptedString key) const {
     return iterator();
   bool isKey = true;
   for (auto it = createIterator(); !it.done(); it.next(resources_)) {
-    if (isKey && stringEquals(key, adaptString(it->asString(resources_))))
+    VariantImpl variant(it.data(), resources_);
+    if (isKey && stringEquals(key, adaptString(variant.asString())))
       return it;
     isKey = !isKey;
   }
@@ -59,7 +60,8 @@ inline VariantData* ObjectImpl::addMember(TAdaptedString key) {
   if (!valueSlot)
     return nullptr;
 
-  if (!keySlot->setString(key, resources_))
+  VariantImpl keyImpl(keySlot.ptr(), resources_);
+  if (!keyImpl.setString(key))
     return nullptr;
 
   CollectionImpl::appendPair(keySlot, valueSlot);

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

@@ -14,7 +14,7 @@ size_t measure(ArduinoJson::JsonVariantConst source) {
   auto data = VariantAttorney::getData(source);
   auto resources = VariantAttorney::getResourceManager(source);
   TSerializer<DummyWriter> serializer(dp, resources);
-  return VariantData::accept(data, resources, serializer);
+  return VariantImpl(data, resources).accept(serializer);
 }
 
 ARDUINOJSON_END_PRIVATE_NAMESPACE

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

@@ -13,7 +13,7 @@ size_t doSerialize(ArduinoJson::JsonVariantConst source, TWriter writer) {
   auto data = VariantAttorney::getData(source);
   auto resources = VariantAttorney::getResourceManager(source);
   TSerializer<TWriter> serializer(writer, resources);
-  return VariantData::accept(data, resources, serializer);
+  return VariantImpl(data, resources).accept(serializer);
 }
 
 template <template <typename> class TSerializer, typename TDestination>

+ 37 - 60
src/ArduinoJson/Variant/ConverterImpl.hpp

@@ -60,25 +60,18 @@ struct Converter<T, detail::enable_if_t<detail::is_integral<T>::value &&
     : private detail::VariantAttorney {
   static bool toJson(T src, JsonVariant dst) {
     ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
-    auto data = getData(dst);
-    if (!data)
-      return false;
-    auto resources = getResourceManager(dst);
-    data->clear(resources);
-    return data->setInteger(src, resources);
+    auto variant = getVariantImpl(dst);
+    variant.clear();
+    return variant.setInteger(src);
   }
 
   static T fromJson(JsonVariantConst src) {
     ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
-    auto data = getData(src);
-    auto resources = getResourceManager(src);
-    return data ? data->template asIntegral<T>(resources) : T();
+    return getVariantImpl(src).template asIntegral<T>();
   }
 
   static bool checkJson(JsonVariantConst src) {
-    auto data = getData(src);
-    auto resources = getResourceManager(src);
-    return data && data->template isInteger<T>(resources);
+    return getVariantImpl(src).template isInteger<T>();
   }
 };
 
@@ -90,40 +83,29 @@ struct Converter<T, detail::enable_if_t<detail::is_enum<T>::value>>
   }
 
   static T fromJson(JsonVariantConst src) {
-    auto data = getData(src);
-    auto resources = getResourceManager(src);
-    return data ? static_cast<T>(data->template asIntegral<int>(resources))
-                : T();
+    return static_cast<T>(getVariantImpl(src).template asIntegral<int>());
   }
 
   static bool checkJson(JsonVariantConst src) {
-    auto data = getData(src);
-    auto resources = getResourceManager(src);
-    return data && data->template isInteger<int>(resources);
+    return getVariantImpl(src).template isInteger<int>();
   }
 };
 
 template <>
 struct Converter<bool> : private detail::VariantAttorney {
   static bool toJson(bool src, JsonVariant dst) {
-    auto data = getData(dst);
-    if (!data)
-      return false;
-    auto resources = getResourceManager(dst);
-    data->clear(resources);
-    data->setBoolean(src);
-    return true;
+    auto variant = getVariantImpl(dst);
+    variant.clear();
+    return variant.setBoolean(src);
   }
 
   static bool fromJson(JsonVariantConst src) {
-    auto data = getData(src);
-    auto resources = getResourceManager(src);
-    return data ? data->asBoolean(resources) : false;
+    return getVariantImpl(src).asBoolean();
   }
 
   static bool checkJson(JsonVariantConst src) {
     auto data = getData(src);
-    return data && data->isBoolean();
+    return data && data->type == detail::VariantType::Boolean;
   }
 };
 
@@ -131,18 +113,13 @@ template <typename T>
 struct Converter<T, detail::enable_if_t<detail::is_floating_point<T>::value>>
     : private detail::VariantAttorney {
   static bool toJson(T src, JsonVariant dst) {
-    auto data = getData(dst);
-    if (!data)
-      return false;
-    auto resources = getResourceManager(dst);
-    data->clear(resources);
-    return data->setFloat(src, resources);
+    auto variant = getVariantImpl(dst);
+    variant.clear();
+    return variant.setFloat(src);
   }
 
   static T fromJson(JsonVariantConst src) {
-    auto data = getData(src);
-    auto resources = getResourceManager(src);
-    return data ? data->template asFloat<T>(resources) : 0;
+    return getVariantImpl(src).template asFloat<T>();
   }
 
   static bool checkJson(JsonVariantConst src) {
@@ -154,13 +131,13 @@ struct Converter<T, detail::enable_if_t<detail::is_floating_point<T>::value>>
 template <>
 struct Converter<const char*> : private detail::VariantAttorney {
   static void toJson(const char* src, JsonVariant dst) {
-    detail::VariantData::setString(getData(dst), detail::adaptString(src),
-                                   getResourceManager(dst));
+    auto variant = getVariantImpl(dst);
+    variant.clear();
+    variant.setString(detail::adaptString(src));
   }
 
   static const char* fromJson(JsonVariantConst src) {
-    auto data = getData(src);
-    return data ? data->asString(getResourceManager(src)).c_str() : 0;
+    return getVariantImpl(src).asString().c_str();
   }
 
   static bool checkJson(JsonVariantConst src) {
@@ -172,13 +149,13 @@ struct Converter<const char*> : private detail::VariantAttorney {
 template <>
 struct Converter<JsonString> : private detail::VariantAttorney {
   static void toJson(JsonString src, JsonVariant dst) {
-    detail::VariantData::setString(getData(dst), detail::adaptString(src),
-                                   getResourceManager(dst));
+    auto variant = getVariantImpl(dst);
+    variant.clear();
+    variant.setString(detail::adaptString(src));
   }
 
   static JsonString fromJson(JsonVariantConst src) {
-    auto data = getData(src);
-    return data ? data->asString(getResourceManager(src)) : JsonString();
+    return getVariantImpl(src).asString();
   }
 
   static bool checkJson(JsonVariantConst src) {
@@ -190,10 +167,9 @@ struct Converter<JsonString> : private detail::VariantAttorney {
 template <typename T>
 inline detail::enable_if_t<detail::IsString<T>::value> convertToJson(
     const T& src, JsonVariant dst) {
-  using namespace detail;
-  auto data = VariantAttorney::getData(dst);
-  auto resources = VariantAttorney::getResourceManager(dst);
-  detail::VariantData::setString(data, adaptString(src), resources);
+  auto variant = detail::VariantAttorney::getVariantImpl(dst);
+  variant.clear();
+  variant.setString(detail::adaptString(src));
 }
 
 // SerializedValue<std::string>
@@ -202,22 +178,23 @@ inline detail::enable_if_t<detail::IsString<T>::value> convertToJson(
 template <typename T>
 struct Converter<SerializedValue<T>> : private detail::VariantAttorney {
   static void toJson(SerializedValue<T> src, JsonVariant dst) {
-    detail::VariantData::setRawString(getData(dst), src,
-                                      getResourceManager(dst));
+    auto variant = getVariantImpl(dst);
+    variant.clear();
+    variant.setRawString(src);
   }
 };
 
 template <>
 struct Converter<detail::nullptr_t> : private detail::VariantAttorney {
   static void toJson(detail::nullptr_t, JsonVariant dst) {
-    detail::VariantData::clear(getData(dst), getResourceManager(dst));
+    getVariantImpl(dst).clear();
   }
   static detail::nullptr_t fromJson(JsonVariantConst) {
     return nullptr;
   }
   static bool checkJson(JsonVariantConst src) {
     auto data = getData(src);
-    return data == 0 || data->isNull();
+    return data == 0 || data->type == detail::VariantType::Null;
   }
 };
 
@@ -263,7 +240,7 @@ inline void convertToJson(const ::Printable& src, JsonVariant dst) {
   auto data = detail::VariantAttorney::getData(dst);
   if (!resources || !data)
     return;
-  data->clear(resources);
+  detail::VariantImpl(data, resources).clear();
   detail::StringBuilderPrint print(resources);
   src.printTo(print);
   if (print.overflowed())
@@ -334,7 +311,7 @@ struct Converter<JsonArrayConst> : private detail::VariantAttorney {
 
   static bool checkJson(JsonVariantConst src) {
     auto data = getData(src);
-    return data && data->isArray();
+    return data && data->type == detail::VariantType::Array;
   }
 };
 
@@ -353,7 +330,7 @@ struct Converter<JsonArray> : private detail::VariantAttorney {
 
   static bool checkJson(JsonVariant src) {
     auto data = getData(src);
-    return data && data->isArray();
+    return data && data->type == detail::VariantType::Array;
   }
 };
 
@@ -372,7 +349,7 @@ struct Converter<JsonObjectConst> : private detail::VariantAttorney {
 
   static bool checkJson(JsonVariantConst src) {
     auto data = getData(src);
-    return data && data->isObject();
+    return data && data->type == detail::VariantType::Object;
   }
 };
 
@@ -391,7 +368,7 @@ struct Converter<JsonObject> : private detail::VariantAttorney {
 
   static bool checkJson(JsonVariant src) {
     auto data = getData(src);
-    return data && data->isObject();
+    return data && data->type == detail::VariantType::Object;
   }
 };
 

+ 9 - 7
src/ArduinoJson/Variant/JsonVariant.hpp

@@ -16,27 +16,29 @@ class JsonVariant : public detail::VariantRefBase<JsonVariant>,
 
  public:
   // Creates an unbound reference.
-  JsonVariant() : data_(0), resources_(0) {}
+  JsonVariant() {}
 
   // INTERNAL USE ONLY
   JsonVariant(detail::VariantData* data, detail::ResourceManager* resources)
-      : data_(data), resources_(resources) {}
+      : impl_(data, resources) {}
+
+  // INTERNAL USE ONLY
+  JsonVariant(detail::VariantImpl impl) : impl_(impl) {}
 
  private:
   detail::ResourceManager* getResourceManager() const {
-    return resources_;
+    return impl_.getResourceManager();
   }
 
   detail::VariantData* getData() const {
-    return data_;
+    return impl_.getData();
   }
 
   detail::VariantData* getOrCreateData() const {
-    return data_;
+    return impl_.getData();
   }
 
-  detail::VariantData* data_;
-  detail::ResourceManager* resources_;
+  mutable detail::VariantImpl impl_;
 };
 
 namespace detail {

+ 17 - 23
src/ArduinoJson/Variant/JsonVariantConst.hpp

@@ -35,34 +35,34 @@ class JsonVariantConst : public detail::VariantTag,
 
  public:
   // Creates an unbound reference.
-  JsonVariantConst() : data_(nullptr), resources_(nullptr) {}
+  JsonVariantConst() {}
 
   // INTERNAL USE ONLY
   explicit JsonVariantConst(detail::VariantData* data,
                             detail::ResourceManager* resources)
-      : data_(data), resources_(resources) {}
+      : impl_(data, resources) {}
 
   // Returns true if the value is null or the reference is unbound.
   // https://arduinojson.org/v7/api/jsonvariantconst/isnull/
   bool isNull() const {
-    return detail::VariantData::isNull(data_);
+    return impl_.isNull();
   }
 
   // Returns true if the reference is unbound.
   bool isUnbound() const {
-    return !data_;
+    return impl_.getData() == nullptr;
   }
 
   // Returns the depth (nesting level) of the value.
   // https://arduinojson.org/v7/api/jsonvariantconst/nesting/
   size_t nesting() const {
-    return detail::VariantData::nesting(data_, resources_);
+    return impl_.nesting();
   }
 
   // Returns the size of the array or object.
   // https://arduinojson.org/v7/api/jsonvariantconst/size/
   size_t size() const {
-    return detail::VariantData::size(data_, resources_);
+    return impl_.size();
   }
 
   // Casts the value to the specified type.
@@ -104,9 +104,8 @@ class JsonVariantConst : public detail::VariantTag,
   template <typename T,
             detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
   JsonVariantConst operator[](T index) const {
-    return JsonVariantConst(
-        detail::VariantData::getElement(data_, size_t(index), resources_),
-        resources_);
+    return JsonVariantConst(impl_.getElement(size_t(index)),
+                            impl_.getResourceManager());
   }
 
   // Gets object's member with specified key.
@@ -114,9 +113,8 @@ class JsonVariantConst : public detail::VariantTag,
   template <typename TString,
             detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
   JsonVariantConst operator[](const TString& key) const {
-    return JsonVariantConst(detail::VariantData::getMember(
-                                data_, detail::adaptString(key), resources_),
-                            resources_);
+    return JsonVariantConst(impl_.getMember(detail::adaptString(key)),
+                            impl_.getResourceManager());
   }
 
   // Gets object's member with specified key.
@@ -126,9 +124,8 @@ class JsonVariantConst : public detail::VariantTag,
                                     !detail::is_const<TChar>::value,
                                 int> = 0>
   JsonVariantConst operator[](TChar* key) const {
-    return JsonVariantConst(detail::VariantData::getMember(
-                                data_, detail::adaptString(key), resources_),
-                            resources_);
+    return JsonVariantConst(impl_.getMember(detail::adaptString(key)),
+                            impl_.getResourceManager());
   }
 
   // Gets object's member with specified key or the array's element at the
@@ -149,8 +146,7 @@ class JsonVariantConst : public detail::VariantTag,
             detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
   ARDUINOJSON_DEPRECATED("use var[key].is<T>() instead")
   bool containsKey(const TString& key) const {
-    return detail::VariantData::getMember(getData(), detail::adaptString(key),
-                                          resources_) != 0;
+    return impl_.getMember(detail::adaptString(key)) != 0;
   }
 
   // DEPRECATED: use obj["key"].is<T>() instead
@@ -161,8 +157,7 @@ class JsonVariantConst : public detail::VariantTag,
                                 int> = 0>
   ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
   bool containsKey(TChar* key) const {
-    return detail::VariantData::getMember(getData(), detail::adaptString(key),
-                                          resources_) != 0;
+    return impl_.getMember(detail::adaptString(key)) != 0;
   }
 
   // DEPRECATED: use obj[key].is<T>() instead
@@ -182,16 +177,15 @@ class JsonVariantConst : public detail::VariantTag,
 
  protected:
   detail::VariantData* getData() const {
-    return data_;
+    return impl_.getData();
   }
 
   detail::ResourceManager* getResourceManager() const {
-    return resources_;
+    return impl_.getResourceManager();
   }
 
  private:
-  mutable detail::VariantData* data_;
-  mutable detail::ResourceManager* resources_;
+  mutable detail::VariantImpl impl_;
 };
 
 ARDUINOJSON_END_PUBLIC_NAMESPACE

+ 1 - 3
src/ArduinoJson/Variant/JsonVariantVisitor.hpp

@@ -49,11 +49,9 @@ template <typename TVisitor>
 typename TVisitor::result_type accept(JsonVariantConst variant,
                                       TVisitor& visit) {
   auto data = VariantAttorney::getData(variant);
-  if (!data)
-    return visit.visit(nullptr);
   auto resources = VariantAttorney::getResourceManager(variant);
   VisitorAdapter<TVisitor> adapter(visit);
-  return data->accept(adapter, resources);
+  return VariantImpl(data, resources).accept(adapter);
 }
 
 ARDUINOJSON_END_PRIVATE_NAMESPACE

+ 10 - 0
src/ArduinoJson/Variant/VariantAttorney.hpp

@@ -26,6 +26,16 @@ class VariantAttorney {
     return client.getData();
   }
 
+  template <typename TClient>
+  static VariantImpl getVariantImpl(TClient& client) {
+    return VariantImpl(client.getData(), client.getResourceManager());
+  }
+
+  template <typename TClient>
+  static VariantImpl getOrCreateVariantImpl(TClient& client) {
+    return VariantImpl(client.getOrCreateData(), client.getResourceManager());
+  }
+
   template <typename TClient>
   static VariantData* getOrCreateData(TClient& client) {
     return client.getOrCreateData();

+ 203 - 292
src/ArduinoJson/Variant/VariantData.hpp

@@ -27,12 +27,11 @@ static bool isTinyString(const T& s, size_t n) {
   return !containsNul;
 }
 
-class VariantData {
-  VariantContent content_;  // must be first to allow cast from array to variant
-  VariantType type_;
-  SlotId next_;
+struct VariantData {
+  VariantContent content;  // must be first to allow cast from array to variant
+  VariantType type = VariantType::Null;
+  SlotId next = NULL_SLOT;
 
- public:
   // Placement new
   static void* operator new(size_t, void* p) noexcept {
     return p;
@@ -40,27 +39,79 @@ class VariantData {
 
   static void operator delete(void*, void*) noexcept {}
 
-  VariantData() : type_(VariantType::Null), next_(NULL_SLOT) {}
+  void setBoolean(bool value) {
+    ARDUINOJSON_ASSERT(type == VariantType::Null);
+    type = VariantType::Boolean;
+    content.asBoolean = value;
+  }
+
+  template <typename TAdaptedString>
+  void setTinyString(const TAdaptedString& s) {
+    ARDUINOJSON_ASSERT(type == VariantType::Null);
+    ARDUINOJSON_ASSERT(s.size() <= tinyStringMaxLength);
+
+    type = VariantType::TinyString;
+
+    auto n = uint8_t(s.size());
+    for (uint8_t i = 0; i < n; i++) {
+      char c = s[i];
+      ARDUINOJSON_ASSERT(c != 0);  // no NUL in tiny string
+      content.asTinyString[i] = c;
+    }
+
+    content.asTinyString[n] = 0;
+  }
+
+  void setOwnedString(StringNode* s) {
+    ARDUINOJSON_ASSERT(type == VariantType::Null);
+    ARDUINOJSON_ASSERT(s);
+    type = VariantType::OwnedString;
+    content.asOwnedString = s;
+  }
+
+  void setRawString(StringNode* s) {
+    ARDUINOJSON_ASSERT(type == VariantType::Null);
+    ARDUINOJSON_ASSERT(s);
+    type = VariantType::RawString;
+    content.asOwnedString = s;
+  }
+
+  bool isFloat() const {
+    return type & VariantTypeBits::NumberBit;
+  }
+
+  bool isString() const {
+    return type == VariantType::LinkedString ||
+           type == VariantType::OwnedString || type == VariantType::TinyString;
+  }
+};
 
-  SlotId next() const {
-    return next_;
+class VariantImpl {
+ public:
+  VariantImpl() : data_(nullptr), resources_(nullptr) {}
+
+  VariantImpl(VariantData* data, ResourceManager* resources)
+      : data_(data), resources_(resources) {}
+
+  VariantData* getData() const {
+    return data_;
   }
 
-  void setNext(SlotId slot) {
-    next_ = slot;
+  ResourceManager* getResourceManager() const {
+    return resources_;
   }
 
   template <typename TVisitor>
-  typename TVisitor::result_type accept(TVisitor& visit,
-                                        ResourceManager* resources) {
+  typename TVisitor::result_type accept(TVisitor& visit) {
+    if (!data_)
+      return visit.visit(nullptr);
+
 #if ARDUINOJSON_USE_8_BYTE_POOL
-    auto eightByteValue = getEightByte(resources);
-#else
-    (void)resources;  // silence warning
+    auto eightByteValue = getEightByte();
 #endif
-    switch (type_) {
+    switch (data_->type) {
       case VariantType::Float:
-        return visit.visit(content_.asFloat);
+        return visit.visit(data_->content.asFloat);
 
 #if ARDUINOJSON_USE_DOUBLE
       case VariantType::Double:
@@ -68,30 +119,30 @@ class VariantData {
 #endif
 
       case VariantType::Array:
-        return visit.visit(asArray(resources));
+        return visit.visit(asArray());
 
       case VariantType::Object:
-        return visit.visit(asObject(resources));
+        return visit.visit(asObject());
 
       case VariantType::TinyString:
-        return visit.visit(JsonString(content_.asTinyString));
+        return visit.visit(JsonString(data_->content.asTinyString));
 
       case VariantType::LinkedString:
-        return visit.visit(JsonString(asLinkedString(resources), true));
+        return visit.visit(JsonString(asLinkedString(), true));
 
       case VariantType::OwnedString:
-        return visit.visit(JsonString(content_.asOwnedString->data,
-                                      content_.asOwnedString->length));
+        return visit.visit(JsonString(data_->content.asOwnedString->data,
+                                      data_->content.asOwnedString->length));
 
       case VariantType::RawString:
-        return visit.visit(RawString(content_.asOwnedString->data,
-                                     content_.asOwnedString->length));
+        return visit.visit(RawString(data_->content.asOwnedString->data,
+                                     data_->content.asOwnedString->length));
 
       case VariantType::Int32:
-        return visit.visit(static_cast<JsonInteger>(content_.asInt32));
+        return visit.visit(static_cast<JsonInteger>(data_->content.asInt32));
 
       case VariantType::Uint32:
-        return visit.visit(static_cast<JsonUInt>(content_.asUint32));
+        return visit.visit(static_cast<JsonUInt>(data_->content.asUint32));
 
 #if ARDUINOJSON_USE_LONG_LONG
       case VariantType::Int64:
@@ -102,62 +153,39 @@ class VariantData {
 #endif
 
       case VariantType::Boolean:
-        return visit.visit(content_.asBoolean != 0);
+        return visit.visit(data_->content.asBoolean != 0);
 
       default:
         return visit.visit(nullptr);
     }
   }
 
-  template <typename TVisitor>
-  static typename TVisitor::result_type accept(VariantData* var,
-                                               ResourceManager* resources,
-                                               TVisitor& visit) {
-    if (var != 0)
-      return var->accept(visit, resources);
-    else
-      return visit.visit(nullptr);
-  }
-
-  VariantData* addElement(ResourceManager* resources) {
-    auto array = isNull() ? toArray(resources) : asArray(resources);
+  VariantData* addElement() {
+    auto array = isNull() ? toArray() : asArray();
     return array.addElement();
   }
 
-  static VariantData* addElement(VariantData* var, ResourceManager* resources) {
-    if (!var)
-      return nullptr;
-    return var->addElement(resources);
-  }
-
   template <typename T>
-  bool addValue(const T& value, ResourceManager* resources) {
-    auto array = isNull() ? toArray(resources) : asArray(resources);
+  bool addValue(const T& value) {
+    auto array = isNull() ? toArray() : asArray();
     return array.addValue(value);
   }
 
-  template <typename T>
-  static bool addValue(VariantData* var, const T& value,
-                       ResourceManager* resources) {
-    if (!var)
+  bool asBoolean() const {
+    if (!data_)
       return false;
-    return var->addValue(value, resources);
-  }
 
-  bool asBoolean(const ResourceManager* resources) const {
 #if ARDUINOJSON_USE_8_BYTE_POOL
-    auto eightByteValue = getEightByte(resources);
-#else
-    (void)resources;  // silence warning
+    auto eightByteValue = getEightByte();
 #endif
-    switch (type_) {
+    switch (data_->type) {
       case VariantType::Boolean:
-        return content_.asBoolean;
+        return data_->content.asBoolean;
       case VariantType::Uint32:
       case VariantType::Int32:
-        return content_.asUint32 != 0;
+        return data_->content.asUint32 != 0;
       case VariantType::Float:
-        return content_.asFloat != 0;
+        return data_->content.asFloat != 0;
 #if ARDUINOJSON_USE_DOUBLE
       case VariantType::Double:
         return eightByteValue->asDouble != 0;
@@ -174,37 +202,33 @@ class VariantData {
     }
   }
 
-  ArrayImpl asArray(ResourceManager* resources) {
-    return ArrayImpl(isArray() ? &content_.asCollection : nullptr, resources);
+  ArrayImpl asArray() {
+    return ArrayImpl(isArray() ? &data_->content.asCollection : nullptr,
+                     resources_);
   }
 
-  static ArrayImpl asArray(VariantData* var, ResourceManager* resources) {
-    return ArrayImpl(
-        var && var->isArray() ? &var->content_.asCollection : nullptr,
-        resources);
-  }
-
-  CollectionImpl asCollection(ResourceManager* resources) {
-    return CollectionImpl(isCollection() ? &content_.asCollection : nullptr,
-                          resources);
+  CollectionImpl asCollection() {
+    return CollectionImpl(
+        isCollection() ? &data_->content.asCollection : nullptr, resources_);
   }
 
   template <typename T>
-  T asFloat(const ResourceManager* resources) const {
+  T asFloat() const {
+    if (!data_)
+      return 0.0;
+
     static_assert(is_floating_point<T>::value, "T must be a floating point");
 #if ARDUINOJSON_USE_8_BYTE_POOL
-    auto eightByteValue = getEightByte(resources);
-#else
-    (void)resources;  // silence warning
+    auto eightByteValue = getEightByte();
 #endif
     const char* str = nullptr;
-    switch (type_) {
+    switch (data_->type) {
       case VariantType::Boolean:
-        return static_cast<T>(content_.asBoolean);
+        return static_cast<T>(data_->content.asBoolean);
       case VariantType::Uint32:
-        return static_cast<T>(content_.asUint32);
+        return static_cast<T>(data_->content.asUint32);
       case VariantType::Int32:
-        return static_cast<T>(content_.asInt32);
+        return static_cast<T>(data_->content.asInt32);
 #if ARDUINOJSON_USE_LONG_LONG
       case VariantType::Uint64:
         return static_cast<T>(eightByteValue->asUint64);
@@ -212,16 +236,16 @@ class VariantData {
         return static_cast<T>(eightByteValue->asInt64);
 #endif
       case VariantType::TinyString:
-        str = content_.asTinyString;
+        str = data_->content.asTinyString;
         break;
       case VariantType::LinkedString:
-        str = asLinkedString(resources);
+        str = asLinkedString();
         break;
       case VariantType::OwnedString:
-        str = content_.asOwnedString->data;
+        str = data_->content.asOwnedString->data;
         break;
       case VariantType::Float:
-        return static_cast<T>(content_.asFloat);
+        return static_cast<T>(data_->content.asFloat);
 #if ARDUINOJSON_USE_DOUBLE
       case VariantType::Double:
         return static_cast<T>(eightByteValue->asDouble);
@@ -235,21 +259,22 @@ class VariantData {
   }
 
   template <typename T>
-  T asIntegral(const ResourceManager* resources) const {
+  T asIntegral() const {
+    if (!data_)
+      return 0;
+
     static_assert(is_integral<T>::value, "T must be an integral type");
 #if ARDUINOJSON_USE_8_BYTE_POOL
-    auto eightByteValue = getEightByte(resources);
-#else
-    (void)resources;  // silence warning
+    auto eightByteValue = getEightByte();
 #endif
     const char* str = nullptr;
-    switch (type_) {
+    switch (data_->type) {
       case VariantType::Boolean:
-        return content_.asBoolean;
+        return data_->content.asBoolean;
       case VariantType::Uint32:
-        return convertNumber<T>(content_.asUint32);
+        return convertNumber<T>(data_->content.asUint32);
       case VariantType::Int32:
-        return convertNumber<T>(content_.asInt32);
+        return convertNumber<T>(data_->content.asInt32);
 #if ARDUINOJSON_USE_LONG_LONG
       case VariantType::Uint64:
         return convertNumber<T>(eightByteValue->asUint64);
@@ -257,16 +282,16 @@ class VariantData {
         return convertNumber<T>(eightByteValue->asInt64);
 #endif
       case VariantType::TinyString:
-        str = content_.asTinyString;
+        str = data_->content.asTinyString;
         break;
       case VariantType::LinkedString:
-        str = asLinkedString(resources);
+        str = asLinkedString();
         break;
       case VariantType::OwnedString:
-        str = content_.asOwnedString->data;
+        str = data_->content.asOwnedString->data;
         break;
       case VariantType::Float:
-        return convertNumber<T>(content_.asFloat);
+        return convertNumber<T>(data_->content.asFloat);
 #if ARDUINOJSON_USE_DOUBLE
       case VariantType::Double:
         return convertNumber<T>(eightByteValue->asDouble);
@@ -279,112 +304,93 @@ class VariantData {
     return parseNumber<T>(str);
   }
 
-  ObjectImpl asObject(ResourceManager* resources) {
-    return ObjectImpl(isObject() ? &content_.asCollection : nullptr, resources);
-  }
-
-  static ObjectImpl asObject(VariantData* var, ResourceManager* resources) {
-    return ObjectImpl(
-        var && var->isObject() ? &var->content_.asCollection : nullptr,
-        resources);
+  ObjectImpl asObject() {
+    return ObjectImpl(isObject() ? &data_->content.asCollection : nullptr,
+                      resources_);
   }
 
   JsonString asRawString() const {
-    switch (type_) {
+    switch (type()) {
       case VariantType::RawString:
-        return JsonString(content_.asOwnedString->data,
-                          content_.asOwnedString->length);
+        return JsonString(data_->content.asOwnedString->data,
+                          data_->content.asOwnedString->length);
       default:
         return JsonString();
     }
   }
 
-  const char* asLinkedString(const ResourceManager* resources) const;
+  const char* asLinkedString() const;
 
-  JsonString asString(const ResourceManager* resources) const {
-    switch (type_) {
+  JsonString asString() const {
+    switch (type()) {
       case VariantType::TinyString:
-        return JsonString(content_.asTinyString);
+        return JsonString(data_->content.asTinyString);
       case VariantType::LinkedString:
-        return JsonString(asLinkedString(resources), true);
+        return JsonString(asLinkedString(), true);
       case VariantType::OwnedString:
-        return JsonString(content_.asOwnedString->data,
-                          content_.asOwnedString->length);
+        return JsonString(data_->content.asOwnedString->data,
+                          data_->content.asOwnedString->length);
       default:
         return JsonString();
     }
   }
 
 #if ARDUINOJSON_USE_8_BYTE_POOL
-  const EightByteValue* getEightByte(const ResourceManager* resources) const;
+  const EightByteValue* getEightByte() const;
 #endif
 
-  VariantData* getElement(size_t index, ResourceManager* resources) {
-    return asArray(resources).getElement(index);
-  }
-
-  static VariantData* getElement(VariantData* var, size_t index,
-                                 ResourceManager* resources) {
-    if (!var)
-      return nullptr;
-    return var->asArray(resources).getElement(index);
+  VariantData* getElement(size_t index) {
+    return asArray().getElement(index);
   }
 
   template <typename TAdaptedString>
-  VariantData* getMember(TAdaptedString key, ResourceManager* resources) {
-    return asObject(resources).getMember(key);
+  VariantData* getMember(TAdaptedString key) {
+    return asObject().getMember(key);
   }
 
-  template <typename TAdaptedString>
-  static VariantData* getMember(VariantData* var, TAdaptedString key,
-                                ResourceManager* resources) {
-    if (!var)
-      return 0;
-    return var->getMember(key, resources);
-  }
-
-  VariantData* getOrAddElement(size_t index, ResourceManager* resources) {
-    auto array = isNull() ? toArray(resources) : asArray(resources);
+  VariantData* getOrAddElement(size_t index) {
+    auto array = isNull() ? toArray() : asArray();
     return array.getOrAddElement(index);
   }
 
   template <typename TAdaptedString>
-  VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources) {
+  VariantData* getOrAddMember(TAdaptedString key) {
     if (key.isNull())
       return nullptr;
-    auto obj = isNull() ? toObject(resources) : asObject(resources);
+    auto obj = isNull() ? toObject() : asObject();
     return obj.getOrAddMember(key);
   }
 
   bool isArray() const {
-    return type_ == VariantType::Array;
+    return type() == VariantType::Array;
   }
 
   bool isBoolean() const {
-    return type_ == VariantType::Boolean;
+    return type() == VariantType::Boolean;
   }
 
   bool isCollection() const {
-    return type_ & VariantTypeBits::CollectionMask;
+    return type() & VariantTypeBits::CollectionMask;
   }
 
   bool isFloat() const {
-    return type_ & VariantTypeBits::NumberBit;
+    return data_ && data_->isFloat();
   }
 
   template <typename T>
-  bool isInteger(const ResourceManager* resources) const {
+  bool isInteger() const {
+    if (!data_)
+      return false;
+
 #if ARDUINOJSON_USE_LONG_LONG
-    auto eightByteValue = getEightByte(resources);
-#else
-    (void)resources;  // silence warning
+    auto eightByteValue = getEightByte();
 #endif
-    switch (type_) {
+    switch (data_->type) {
       case VariantType::Uint32:
-        return canConvertNumber<T>(content_.asUint32);
+        return canConvertNumber<T>(data_->content.asUint32);
 
       case VariantType::Int32:
-        return canConvertNumber<T>(content_.asInt32);
+        return canConvertNumber<T>(data_->content.asInt32);
 
 #if ARDUINOJSON_USE_LONG_LONG
       case VariantType::Uint64:
@@ -400,197 +406,102 @@ class VariantData {
   }
 
   bool isNull() const {
-    return type_ == VariantType::Null;
-  }
-
-  static bool isNull(const VariantData* var) {
-    if (!var)
-      return true;
-    return var->isNull();
+    return type() == VariantType::Null;
   }
 
   bool isObject() const {
-    return type_ == VariantType::Object;
+    return type() == VariantType::Object;
   }
 
   bool isString() const {
-    return type_ == VariantType::LinkedString ||
-           type_ == VariantType::OwnedString ||
-           type_ == VariantType::TinyString;
-  }
-
-  size_t nesting(ResourceManager* resources) {
-    return asCollection(resources).nesting();
-  }
-
-  static size_t nesting(VariantData* var, ResourceManager* resources) {
-    if (!var)
-      return 0;
-    return var->nesting(resources);
-  }
-
-  void removeElement(size_t index, ResourceManager* resources) {
-    asArray(resources).removeElement(index);
+    return data_ && data_->isString();
   }
 
-  static void removeElement(VariantData* var, size_t index,
-                            ResourceManager* resources) {
-    if (!var)
-      return;
-    var->removeElement(index, resources);
+  size_t nesting() {
+    return asCollection().nesting();
   }
 
-  template <typename TAdaptedString>
-  void removeMember(TAdaptedString key, ResourceManager* resources) {
-    asObject(resources).removeMember(key);
+  void removeElement(size_t index) {
+    asArray().removeElement(index);
   }
 
   template <typename TAdaptedString>
-  static void removeMember(VariantData* var, TAdaptedString key,
-                           ResourceManager* resources) {
-    if (!var)
-      return;
-    var->removeMember(key, resources);
-  }
-
-  void reset() {  // TODO: remove
-    type_ = VariantType::Null;
+  void removeMember(TAdaptedString key) {
+    asObject().removeMember(key);
   }
 
-  void setBoolean(bool value) {
-    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
-    type_ = VariantType::Boolean;
-    content_.asBoolean = value;
+  bool setBoolean(bool value) {
+    if (!data_)
+      return false;
+    data_->setBoolean(value);
+    return true;
   }
 
   template <typename T>
-  enable_if_t<sizeof(T) == 4, bool> setFloat(T value, ResourceManager*) {
-    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
-    type_ = VariantType::Float;
-    content_.asFloat = value;
+  enable_if_t<sizeof(T) == 4, bool> setFloat(T value) {
+    ARDUINOJSON_ASSERT(type() == VariantType::Null);  // must call clear() first
+    if (!data_)
+      return false;
+    data_->type = VariantType::Float;
+    data_->content.asFloat = value;
     return true;
   }
 
   template <typename T>
-  enable_if_t<sizeof(T) == 8, bool> setFloat(T value, ResourceManager*);
+  enable_if_t<sizeof(T) == 8, bool> setFloat(T value);
 
   template <typename T>
-  enable_if_t<is_signed<T>::value, bool> setInteger(T value,
-                                                    ResourceManager* resources);
+  enable_if_t<is_signed<T>::value, bool> setInteger(T value);
 
   template <typename T>
-  enable_if_t<is_unsigned<T>::value, bool> setInteger(
-      T value, ResourceManager* resources);
-
-  void setRawString(StringNode* s) {
-    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
-    ARDUINOJSON_ASSERT(s);
-    type_ = VariantType::RawString;
-    content_.asOwnedString = s;
-  }
+  enable_if_t<is_unsigned<T>::value, bool> setInteger(T value);
 
   template <typename T>
-  void setRawString(SerializedValue<T> value, ResourceManager* resources);
-
-  template <typename T>
-  static void setRawString(VariantData* var, SerializedValue<T> value,
-                           ResourceManager* resources) {
-    if (!var)
-      return;
-    var->clear(resources);
-    var->setRawString(value, resources);
-  }
+  void setRawString(SerializedValue<T> value);
 
   template <typename TAdaptedString>
-  bool setString(TAdaptedString value, ResourceManager* resources);
+  bool setString(TAdaptedString value);
 
-  template <typename TAdaptedString>
-  static void setString(VariantData* var, TAdaptedString value,
-                        ResourceManager* resources) {
-    if (!var)
-      return;
-    var->clear(resources);
-    var->setString(value, resources);
-  }
-
-  bool setLinkedString(const char* s, ResourceManager* resources);
-
-  template <typename TAdaptedString>
-  void setTinyString(const TAdaptedString& s) {
-    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
-    ARDUINOJSON_ASSERT(s.size() <= tinyStringMaxLength);
-
-    type_ = VariantType::TinyString;
-
-    auto n = uint8_t(s.size());
-    for (uint8_t i = 0; i < n; i++) {
-      char c = s[i];
-      ARDUINOJSON_ASSERT(c != 0);  // no NUL in tiny string
-      content_.asTinyString[i] = c;
-    }
+  bool setLinkedString(const char* s);
 
-    content_.asTinyString[n] = 0;
-  }
-
-  void setOwnedString(StringNode* s) {
-    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
-    ARDUINOJSON_ASSERT(s);
-    type_ = VariantType::OwnedString;
-    content_.asOwnedString = s;
-  }
-
-  size_t size(ResourceManager* resources) {
+  size_t size() {
     if (isObject())
-      return asObject(resources).size();
+      return asObject().size();
 
     if (isArray())
-      return asArray(resources).size();
+      return asArray().size();
 
     return 0;
   }
 
-  static size_t size(VariantData* var, ResourceManager* resources) {
-    return var != 0 ? var->size(resources) : 0;
+  ArrayImpl toArray() {
+    ARDUINOJSON_ASSERT(type() == VariantType::Null);  // must call clear() first
+    if (!data_)
+      return ArrayImpl();
+    data_->type = VariantType::Array;
+    return ArrayImpl(new (&data_->content.asCollection) CollectionData(),
+                     resources_);
   }
 
-  ArrayImpl toArray(ResourceManager* resources) {
-    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
-    type_ = VariantType::Array;
-    return ArrayImpl(new (&content_.asCollection) CollectionData(), resources);
-  }
-
-  static ArrayImpl toArray(VariantData* var, ResourceManager* resources) {
-    if (!var)
-      return ArrayImpl(nullptr, resources);
-    var->clear(resources);
-    return var->toArray(resources);
-  }
-
-  ObjectImpl toObject(ResourceManager* resources) {
-    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
-    type_ = VariantType::Object;
-    return ObjectImpl(new (&content_.asCollection) CollectionData(), resources);
-  }
-
-  static ObjectImpl toObject(VariantData* var, ResourceManager* resources) {
-    if (!var)
+  ObjectImpl toObject() {
+    ARDUINOJSON_ASSERT(type() == VariantType::Null);  // must call clear() first
+    if (!data_)
       return ObjectImpl();
-    var->clear(resources);
-    return var->toObject(resources);
+    data_->type = VariantType::Object;
+    return ObjectImpl(new (&data_->content.asCollection) CollectionData(),
+                      resources_);
   }
 
   VariantType type() const {
-    return type_;
+    return data_ ? data_->type : VariantType::Null;
   }
 
   // Release the resources used by this variant and set it to null.
-  void clear(ResourceManager* resources);
+  void clear();
 
-  static void clear(VariantData* var, ResourceManager* resources) {
-    if (!var)
-      return;
-    var->clear(resources);
-  }
+ private:
+  VariantData* data_;
+  ResourceManager* resources_;
 };
 
 ARDUINOJSON_END_PRIVATE_NAMESPACE

+ 67 - 62
src/ArduinoJson/Variant/VariantImpl.hpp

@@ -10,126 +10,130 @@
 ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T>
-inline void VariantData::setRawString(SerializedValue<T> value,
-                                      ResourceManager* resources) {
-  ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
-  auto dup = resources->saveString(adaptString(value.data(), value.size()));
+inline void VariantImpl::setRawString(SerializedValue<T> value) {
+  if (!data_)
+    return;
+  auto dup = resources_->saveString(adaptString(value.data(), value.size()));
   if (dup)
-    setRawString(dup);
+    data_->setRawString(dup);
 }
 
-inline bool VariantData::setLinkedString(const char* s,
-                                         ResourceManager* resources) {
-  ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
+inline bool VariantImpl::setLinkedString(const char* s) {
+  ARDUINOJSON_ASSERT(isNull());  // must call clear() first
   ARDUINOJSON_ASSERT(s);
 
-  auto slotId = resources->saveStaticString(s);
+  auto slotId = resources_->saveStaticString(s);
   if (slotId == NULL_SLOT)
     return false;
 
-  type_ = VariantType::LinkedString;
-  content_.asSlotId = slotId;
+  data_->type = VariantType::LinkedString;
+  data_->content.asSlotId = slotId;
   return true;
 }
 
 template <typename TAdaptedString>
-inline bool VariantData::setString(TAdaptedString value,
-                                   ResourceManager* resources) {
-  ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
+inline bool VariantImpl::setString(TAdaptedString value) {
+  ARDUINOJSON_ASSERT(isNull());  // must call clear() first
+
+  if (!data_)
+    return false;
 
   if (value.isNull())
     return false;
 
   if (value.isStatic())
-    return setLinkedString(value.data(), resources);
+    return setLinkedString(value.data());
 
   if (isTinyString(value, value.size())) {
-    setTinyString(value);
+    data_->setTinyString(value);
     return true;
   }
 
-  auto dup = resources->saveString(value);
+  auto dup = resources_->saveString(value);
   if (dup) {
-    setOwnedString(dup);
+    data_->setOwnedString(dup);
     return true;
   }
 
   return false;
 }
 
-inline void VariantData::clear(ResourceManager* resources) {
-  if (type_ & VariantTypeBits::OwnedStringBit)
-    resources->dereferenceString(content_.asOwnedString->data);
+inline void VariantImpl::clear() {
+  if (!data_)
+    return;
+
+  if (data_->type & VariantTypeBits::OwnedStringBit)
+    resources_->dereferenceString(data_->content.asOwnedString->data);
 
 #if ARDUINOJSON_USE_8_BYTE_POOL
-  if (type_ & VariantTypeBits::EightByteBit)
-    resources->freeEightByte(content_.asSlotId);
+  if (data_->type & VariantTypeBits::EightByteBit)
+    resources_->freeEightByte(data_->content.asSlotId);
 #endif
 
-  asCollection(resources).clear();
+  asCollection().clear();
 
-  type_ = VariantType::Null;
+  data_->type = VariantType::Null;
 }
 
 #if ARDUINOJSON_USE_8_BYTE_POOL
-inline const EightByteValue* VariantData::getEightByte(
-    const ResourceManager* resources) const {
-  return type_ & VariantTypeBits::EightByteBit
-             ? resources->getEightByte(content_.asSlotId)
+inline const EightByteValue* VariantImpl::getEightByte() const {
+  return type() & VariantTypeBits::EightByteBit
+             ? resources_->getEightByte(data_->content.asSlotId)
              : 0;
 }
 #endif
 
-inline const char* VariantData::asLinkedString(
-    const ResourceManager* resources) const {
-  ARDUINOJSON_ASSERT(type_ == VariantType::LinkedString);
-  return resources->getStaticString(content_.asSlotId);
+inline const char* VariantImpl::asLinkedString() const {
+  ARDUINOJSON_ASSERT(type() == VariantType::LinkedString);
+  return resources_->getStaticString(data_->content.asSlotId);
 }
 
 template <typename T>
-enable_if_t<sizeof(T) == 8, bool> VariantData::setFloat(
-    T value, ResourceManager* resources) {
-  ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
-  (void)resources;                                 // silence warning
+enable_if_t<sizeof(T) == 8, bool> VariantImpl::setFloat(T value) {
+  ARDUINOJSON_ASSERT(isNull());  // must call clear() first
+
+  if (!data_)
+    return false;
 
   float valueAsFloat = static_cast<float>(value);
 
 #if ARDUINOJSON_USE_DOUBLE
   if (value == valueAsFloat) {
-    type_ = VariantType::Float;
-    content_.asFloat = valueAsFloat;
+    data_->type = VariantType::Float;
+    data_->content.asFloat = valueAsFloat;
   } else {
-    auto slot = resources->allocEightByte();
+    auto slot = resources_->allocEightByte();
     if (!slot)
       return false;
-    type_ = VariantType::Double;
-    content_.asSlotId = slot.id();
+    data_->type = VariantType::Double;
+    data_->content.asSlotId = slot.id();
     slot->asDouble = value;
   }
 #else
-  type_ = VariantType::Float;
-  content_.asFloat = valueAsFloat;
+  data_->type = VariantType::Float;
+  data_->content.asFloat = valueAsFloat;
 #endif
   return true;
 }
 
 template <typename T>
-enable_if_t<is_signed<T>::value, bool> VariantData::setInteger(
-    T value, ResourceManager* resources) {
-  ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
-  (void)resources;                                 // silence warning
+enable_if_t<is_signed<T>::value, bool> VariantImpl::setInteger(T value) {
+  ARDUINOJSON_ASSERT(isNull());  // must call clear() first
+
+  if (!data_)
+    return false;
 
   if (canConvertNumber<int32_t>(value)) {
-    type_ = VariantType::Int32;
-    content_.asInt32 = static_cast<int32_t>(value);
+    data_->type = VariantType::Int32;
+    data_->content.asInt32 = static_cast<int32_t>(value);
   }
 #if ARDUINOJSON_USE_LONG_LONG
   else {
-    auto slot = resources->allocEightByte();
+    auto slot = resources_->allocEightByte();
     if (!slot)
       return false;
-    type_ = VariantType::Int64;
-    content_.asSlotId = slot.id();
+    data_->type = VariantType::Int64;
+    data_->content.asSlotId = slot.id();
     slot->asInt64 = value;
   }
 #endif
@@ -137,22 +141,23 @@ enable_if_t<is_signed<T>::value, bool> VariantData::setInteger(
 }
 
 template <typename T>
-enable_if_t<is_unsigned<T>::value, bool> VariantData::setInteger(
-    T value, ResourceManager* resources) {
-  ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
-  (void)resources;                                 // silence warning
+enable_if_t<is_unsigned<T>::value, bool> VariantImpl::setInteger(T value) {
+  ARDUINOJSON_ASSERT(isNull());  // must call clear() first
+
+  if (!data_)
+    return false;
 
   if (canConvertNumber<uint32_t>(value)) {
-    type_ = VariantType::Uint32;
-    content_.asUint32 = static_cast<uint32_t>(value);
+    data_->type = VariantType::Uint32;
+    data_->content.asUint32 = static_cast<uint32_t>(value);
   }
 #if ARDUINOJSON_USE_LONG_LONG
   else {
-    auto slot = resources->allocEightByte();
+    auto slot = resources_->allocEightByte();
     if (!slot)
       return false;
-    type_ = VariantType::Uint64;
-    content_.asSlotId = slot.id();
+    data_->type = VariantType::Uint64;
+    data_->content.asSlotId = slot.id();
     slot->asUint64 = value;
   }
 #endif

+ 17 - 13
src/ArduinoJson/Variant/VariantRefBase.hpp

@@ -29,13 +29,13 @@ class VariantRefBase : public VariantTag {
   // Sets the value to null.
   // https://arduinojson.org/v7/api/jsonvariant/clear/
   void clear() const {
-    VariantData::clear(getOrCreateData(), getResourceManager());
+    getOrCreateVariantImpl().clear();
   }
 
   // Returns true if the value is null or the reference is unbound.
   // https://arduinojson.org/v7/api/jsonvariant/isnull/
   bool isNull() const {
-    return VariantData::isNull(getData());
+    return getVariantImpl().isNull();
   }
 
   // Returns true if the reference is unbound.
@@ -93,13 +93,13 @@ class VariantRefBase : public VariantTag {
   // Returns the size of the array or object.
   // https://arduinojson.org/v7/api/jsonvariant/size/
   size_t size() const {
-    return VariantData::size(getData(), getResourceManager());
+    return getVariantImpl().size();
   }
 
   // Returns the depth (nesting level) of the value.
   // https://arduinojson.org/v7/api/jsonvariant/nesting/
   size_t nesting() const {
-    return VariantData::nesting(getData(), getResourceManager());
+    return getVariantImpl().nesting();
   }
 
   // Appends a new (empty) element to the array.
@@ -120,38 +120,34 @@ class VariantRefBase : public VariantTag {
   // https://arduinojson.org/v7/api/jsonvariant/add/
   template <typename T>
   bool add(const T& value) const {
-    return detail::VariantData::addValue(getOrCreateData(), value,
-                                         getResourceManager());
+    return getOrCreateVariantImpl().addValue(value);
   }
 
   // Appends a value to the array.
   // https://arduinojson.org/v7/api/jsonvariant/add/
   template <typename T, enable_if_t<!is_const<T>::value, int> = 0>
   bool add(T* value) const {
-    return detail::VariantData::addValue(getOrCreateData(), value,
-                                         getResourceManager());
+    return getOrCreateVariantImpl().addValue(value);
   }
 
   // Removes an element of the array.
   // https://arduinojson.org/v7/api/jsonvariant/remove/
   void remove(size_t index) const {
-    VariantData::removeElement(getData(), index, getResourceManager());
+    getVariantImpl().removeElement(index);
   }
 
   // Removes a member of the object.
   // https://arduinojson.org/v7/api/jsonvariant/remove/
   template <typename TChar, enable_if_t<IsString<TChar*>::value, int> = 0>
   void remove(TChar* key) const {
-    VariantData::removeMember(getData(), adaptString(key),
-                              getResourceManager());
+    getVariantImpl().removeMember(adaptString(key));
   }
 
   // Removes a member of the object.
   // https://arduinojson.org/v7/api/jsonvariant/remove/
   template <typename TString, enable_if_t<IsString<TString>::value, int> = 0>
   void remove(const TString& key) const {
-    VariantData::removeMember(getData(), adaptString(key),
-                              getResourceManager());
+    getVariantImpl().removeMember(adaptString(key));
   }
 
   // Removes a member of the object or an element of the array.
@@ -275,6 +271,14 @@ class VariantRefBase : public VariantTag {
     return VariantAttorney::getOrCreateData(derived());
   }
 
+  VariantImpl getVariantImpl() const {
+    return VariantImpl(getData(), getResourceManager());
+  }
+
+  VariantImpl getOrCreateVariantImpl() const {
+    return VariantImpl(getOrCreateData(), getResourceManager());
+  }
+
   FORCE_INLINE ArduinoJson::JsonVariant getVariant() const;
 
   FORCE_INLINE ArduinoJson::JsonVariantConst getVariantConst() const {

+ 13 - 15
src/ArduinoJson/Variant/VariantRefBaseImpl.hpp

@@ -69,23 +69,20 @@ inline void convertToJson(const VariantRefBase<TDerived>& src,
 template <typename TDerived>
 template <typename T, enable_if_t<is_same<T, JsonVariant>::value, int>>
 inline T VariantRefBase<TDerived>::add() const {
-  return JsonVariant(
-      detail::VariantData::addElement(getOrCreateData(), getResourceManager()),
-      getResourceManager());
+  return JsonVariant(getOrCreateVariantImpl().addElement(),
+                     getResourceManager());
 }
 
 template <typename TDerived>
 template <typename TString, enable_if_t<IsString<TString>::value, int>>
 inline bool VariantRefBase<TDerived>::containsKey(const TString& key) const {
-  return VariantData::getMember(getData(), adaptString(key),
-                                getResourceManager()) != 0;
+  return getVariantImpl().getMember(adaptString(key)) != 0;
 }
 
 template <typename TDerived>
 template <typename TChar, enable_if_t<IsString<TChar*>::value, int>>
 inline bool VariantRefBase<TDerived>::containsKey(TChar* key) const {
-  return VariantData::getMember(getData(), adaptString(key),
-                                getResourceManager()) != 0;
+  return getVariantImpl().getMember(adaptString(key)) != 0;
 }
 
 template <typename TDerived>
@@ -150,24 +147,25 @@ inline bool VariantRefBase<TDerived>::doSet(const T& value, true_type) const {
 template <typename TDerived>
 template <typename T, enable_if_t<is_same<T, JsonArray>::value, int>>
 inline JsonArray VariantRefBase<TDerived>::to() const {
-  return JsonArray(
-      VariantData::toArray(getOrCreateData(), getResourceManager()));
+  auto variant = getOrCreateVariantImpl();
+  variant.clear();
+  return JsonArray(variant.toArray());
 }
 
 template <typename TDerived>
 template <typename T, enable_if_t<is_same<T, JsonObject>::value, int>>
 JsonObject VariantRefBase<TDerived>::to() const {
-  return JsonObject(
-      VariantData::toObject(getOrCreateData(), getResourceManager()));
+  auto variant = getOrCreateVariantImpl();
+  variant.clear();
+  return JsonObject(variant.toObject());
 }
 
 template <typename TDerived>
 template <typename T, enable_if_t<is_same<T, JsonVariant>::value, int>>
 JsonVariant VariantRefBase<TDerived>::to() const {
-  auto data = getOrCreateData();
-  auto resources = getResourceManager();
-  detail::VariantData::clear(data, resources);
-  return JsonVariant(data, resources);
+  detail::VariantImpl impl(getOrCreateData(), getResourceManager());
+  impl.clear();
+  return JsonVariant(impl);
 }
 
 ARDUINOJSON_END_PRIVATE_NAMESPACE