Make VariantSlot a union. Include next slot id in VariantData

This commit is contained in:
Benoit Blanchon
2024-08-24 18:18:28 +02:00
parent ab72bb8601
commit d3721cb122
19 changed files with 170 additions and 150 deletions

View File

@ -9,28 +9,28 @@
using namespace ArduinoJson::detail; using namespace ArduinoJson::detail;
TEST_CASE("ResourceManager::allocSlot()") { TEST_CASE("ResourceManager::allocVariant()") {
SECTION("Returns different pointer") { SECTION("Returns different pointer") {
ResourceManager resources; ResourceManager resources;
VariantSlot* s1 = resources.allocSlot(); auto s1 = resources.allocVariant();
REQUIRE(s1 != 0); REQUIRE(s1.data() != nullptr);
VariantSlot* s2 = resources.allocSlot(); auto s2 = resources.allocVariant();
REQUIRE(s2 != 0); REQUIRE(s2.data() != nullptr);
REQUIRE(s1 != s2); REQUIRE(s1.data() != s2.data());
} }
SECTION("Returns the same slot after calling freeSlot()") { SECTION("Returns the same slot after calling freeVariant()") {
ResourceManager resources; ResourceManager resources;
auto s1 = resources.allocSlot(); auto s1 = resources.allocVariant();
auto s2 = resources.allocSlot(); auto s2 = resources.allocVariant();
resources.freeSlot(s1); resources.freeVariant(s1);
resources.freeSlot(s2); resources.freeVariant(s2);
auto s3 = resources.allocSlot(); auto s3 = resources.allocVariant();
auto s4 = resources.allocSlot(); auto s4 = resources.allocVariant();
auto s5 = resources.allocSlot(); auto s5 = resources.allocVariant();
REQUIRE(s2.id() != s1.id()); REQUIRE(s2.id() != s1.id());
REQUIRE(s3.id() == s2.id()); REQUIRE(s3.id() == s2.id());
@ -42,26 +42,26 @@ TEST_CASE("ResourceManager::allocSlot()") {
SECTION("Returns aligned pointers") { SECTION("Returns aligned pointers") {
ResourceManager resources; ResourceManager resources;
REQUIRE(isAligned(resources.allocSlot().operator VariantSlot*())); REQUIRE(isAligned(resources.allocVariant().data()));
REQUIRE(isAligned(resources.allocSlot().operator VariantSlot*())); REQUIRE(isAligned(resources.allocVariant().data()));
} }
SECTION("Returns null if pool list allocation fails") { SECTION("Returns null if pool list allocation fails") {
ResourceManager resources(FailingAllocator::instance()); ResourceManager resources(FailingAllocator::instance());
auto variant = resources.allocSlot(); auto variant = resources.allocVariant();
REQUIRE(variant.id() == NULL_SLOT); REQUIRE(variant.id() == NULL_SLOT);
REQUIRE(static_cast<VariantSlot*>(variant) == nullptr); REQUIRE(variant.data() == nullptr);
} }
SECTION("Returns null if pool allocation fails") { SECTION("Returns null if pool allocation fails") {
ResourceManager resources(FailingAllocator::instance()); ResourceManager resources(FailingAllocator::instance());
resources.allocSlot(); resources.allocVariant();
auto variant = resources.allocSlot(); auto variant = resources.allocVariant();
REQUIRE(variant.id() == NULL_SLOT); REQUIRE(variant.id() == NULL_SLOT);
REQUIRE(static_cast<VariantSlot*>(variant) == nullptr); REQUIRE(variant.data() == nullptr);
} }
SECTION("Try overflow pool counter") { SECTION("Try overflow pool counter") {
@ -73,18 +73,18 @@ TEST_CASE("ResourceManager::allocSlot()") {
// fill all the pools // fill all the pools
for (SlotId i = 0; i < NULL_SLOT; i++) { for (SlotId i = 0; i < NULL_SLOT; i++) {
auto slot = resources.allocSlot(); auto slot = resources.allocVariant();
REQUIRE(slot.id() == i); // or != NULL_SLOT REQUIRE(slot.id() == i); // or != NULL_SLOT
REQUIRE(static_cast<VariantSlot*>(slot) != nullptr); REQUIRE(slot.data() != nullptr);
} }
REQUIRE(resources.overflowed() == false); REQUIRE(resources.overflowed() == false);
// now all allocations should fail // now all allocations should fail
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
auto slot = resources.allocSlot(); auto slot = resources.allocVariant();
REQUIRE(slot.id() == NULL_SLOT); REQUIRE(slot.id() == NULL_SLOT);
REQUIRE(static_cast<VariantSlot*>(slot) == nullptr); REQUIRE(slot.data() == nullptr);
} }
REQUIRE(resources.overflowed() == true); REQUIRE(resources.overflowed() == true);

View File

@ -3,6 +3,7 @@
// MIT License // MIT License
#include <ArduinoJson/Memory/ResourceManager.hpp> #include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
#include <ArduinoJson/Memory/VariantPoolImpl.hpp> #include <ArduinoJson/Memory/VariantPoolImpl.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp> #include <ArduinoJson/Strings/StringAdapters.hpp>
#include <catch.hpp> #include <catch.hpp>
@ -13,7 +14,7 @@ TEST_CASE("ResourceManager::clear()") {
ResourceManager resources; ResourceManager resources;
SECTION("Discards allocated variants") { SECTION("Discards allocated variants") {
resources.allocSlot(); resources.allocVariant();
resources.clear(); resources.clear();
REQUIRE(resources.size() == 0); REQUIRE(resources.size() == 0);

View File

@ -3,6 +3,7 @@
// MIT License // MIT License
#include <ArduinoJson/Memory/ResourceManager.hpp> #include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
#include <ArduinoJson/Memory/VariantPoolImpl.hpp> #include <ArduinoJson/Memory/VariantPoolImpl.hpp>
#include <catch.hpp> #include <catch.hpp>
@ -21,7 +22,7 @@ TEST_CASE("ResourceManager::shrinkToFit()") {
} }
SECTION("only one pool") { SECTION("only one pool") {
resources.allocSlot(); resources.allocVariant();
resources.shrinkToFit(); resources.shrinkToFit();
@ -36,7 +37,7 @@ TEST_CASE("ResourceManager::shrinkToFit()") {
for (size_t i = 0; for (size_t i = 0;
i < ARDUINOJSON_POOL_CAPACITY * ARDUINOJSON_INITIAL_POOL_COUNT + 1; i < ARDUINOJSON_POOL_CAPACITY * ARDUINOJSON_INITIAL_POOL_COUNT + 1;
i++) i++)
resources.allocSlot(); resources.allocVariant();
REQUIRE(spyingAllocator.log() == REQUIRE(spyingAllocator.log() ==
AllocatorLog{ AllocatorLog{
Allocate(sizeofPool()) * ARDUINOJSON_INITIAL_POOL_COUNT, Allocate(sizeofPool()) * ARDUINOJSON_INITIAL_POOL_COUNT,

View File

@ -3,6 +3,7 @@
// MIT License // MIT License
#include <ArduinoJson/Memory/ResourceManager.hpp> #include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
#include <ArduinoJson/Memory/VariantPoolImpl.hpp> #include <ArduinoJson/Memory/VariantPoolImpl.hpp>
#include <catch.hpp> #include <catch.hpp>
@ -21,10 +22,10 @@ TEST_CASE("ResourceManager::size()") {
SECTION("Doesn't grow when allocation of second pool fails") { SECTION("Doesn't grow when allocation of second pool fails") {
timebomb.setCountdown(1); timebomb.setCountdown(1);
for (size_t i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++) for (size_t i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
resources.allocSlot(); resources.allocVariant();
size_t size = resources.size(); size_t size = resources.size();
resources.allocSlot(); resources.allocVariant();
REQUIRE(size == resources.size()); REQUIRE(size == resources.size());
} }

View File

@ -4,6 +4,7 @@
#include <ArduinoJson/Memory/Alignment.hpp> #include <ArduinoJson/Memory/Alignment.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp> #include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
#include <ArduinoJson/Memory/VariantPoolImpl.hpp> #include <ArduinoJson/Memory/VariantPoolImpl.hpp>
#include <catch.hpp> #include <catch.hpp>
@ -14,7 +15,7 @@ using namespace ArduinoJson::detail;
static void fullPreallocatedPools(ResourceManager& resources) { static void fullPreallocatedPools(ResourceManager& resources) {
for (int i = 0; for (int i = 0;
i < ARDUINOJSON_INITIAL_POOL_COUNT * ARDUINOJSON_POOL_CAPACITY; i++) i < ARDUINOJSON_INITIAL_POOL_COUNT * ARDUINOJSON_POOL_CAPACITY; i++)
resources.allocSlot(); resources.allocVariant();
} }
TEST_CASE("ResourceManager::swap()") { TEST_CASE("ResourceManager::swap()") {
@ -23,13 +24,13 @@ TEST_CASE("ResourceManager::swap()") {
ResourceManager a(&spy); ResourceManager a(&spy);
ResourceManager b(&spy); ResourceManager b(&spy);
auto a1 = a.allocSlot(); auto a1 = a.allocVariant();
auto b1 = b.allocSlot(); auto b1 = b.allocVariant();
swap(a, b); swap(a, b);
REQUIRE(a1->data() == b.getSlot(a1.id())->data()); REQUIRE(a1.data() == b.getVariant(a1.id()));
REQUIRE(b1->data() == a.getSlot(b1.id())->data()); REQUIRE(b1.data() == a.getVariant(b1.id()));
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()) * 2, Allocate(sizeofPool()) * 2,
@ -42,12 +43,12 @@ TEST_CASE("ResourceManager::swap()") {
ResourceManager b(&spy); ResourceManager b(&spy);
fullPreallocatedPools(b); fullPreallocatedPools(b);
auto a1 = a.allocSlot(); auto a1 = a.allocVariant();
auto b1 = b.allocSlot(); auto b1 = b.allocVariant();
swap(a, b); swap(a, b);
REQUIRE(a1->data() == b.getSlot(a1.id())->data()); REQUIRE(a1.data() == b.getVariant(a1.id()));
REQUIRE(b1->data() == a.getSlot(b1.id())->data()); REQUIRE(b1.data() == a.getVariant(b1.id()));
REQUIRE(spy.log() == REQUIRE(spy.log() ==
AllocatorLog{ AllocatorLog{
@ -63,12 +64,12 @@ TEST_CASE("ResourceManager::swap()") {
fullPreallocatedPools(a); fullPreallocatedPools(a);
ResourceManager b(&spy); ResourceManager b(&spy);
auto a1 = a.allocSlot(); auto a1 = a.allocVariant();
auto b1 = b.allocSlot(); auto b1 = b.allocVariant();
swap(a, b); swap(a, b);
REQUIRE(a1->data() == b.getSlot(a1.id())->data()); REQUIRE(a1.data() == b.getVariant(a1.id()));
REQUIRE(b1->data() == a.getSlot(b1.id())->data()); REQUIRE(b1.data() == a.getVariant(b1.id()));
REQUIRE(spy.log() == REQUIRE(spy.log() ==
AllocatorLog{ AllocatorLog{
@ -85,12 +86,12 @@ TEST_CASE("ResourceManager::swap()") {
ResourceManager b(&spy); ResourceManager b(&spy);
fullPreallocatedPools(b); fullPreallocatedPools(b);
auto a1 = a.allocSlot(); auto a1 = a.allocVariant();
auto b1 = b.allocSlot(); auto b1 = b.allocVariant();
swap(a, b); swap(a, b);
REQUIRE(a1->data() == b.getSlot(a1.id())->data()); REQUIRE(a1.data() == b.getVariant(a1.id()));
REQUIRE(b1->data() == a.getSlot(b1.id())->data()); REQUIRE(b1.data() == a.getVariant(b1.id()));
} }
} }

View File

@ -20,11 +20,11 @@ inline ArrayData::iterator ArrayData::at(
} }
inline VariantData* ArrayData::addElement(ResourceManager* resources) { inline VariantData* ArrayData::addElement(ResourceManager* resources) {
auto slot = resources->allocSlot(); auto slot = resources->allocVariant();
if (!slot) if (!slot)
return nullptr; return nullptr;
CollectionData::appendOne(slot, resources); CollectionData::appendOne(slot, resources);
return slot->data(); return slot.data();
} }
inline VariantData* ArrayData::getOrAddElement(size_t index, inline VariantData* ArrayData::getOrAddElement(size_t index,
@ -58,12 +58,12 @@ inline void ArrayData::removeElement(size_t index, ResourceManager* resources) {
template <typename T> template <typename T>
inline bool ArrayData::addValue(T&& value, ResourceManager* resources) { inline bool ArrayData::addValue(T&& value, ResourceManager* resources) {
ARDUINOJSON_ASSERT(resources != nullptr); ARDUINOJSON_ASSERT(resources != nullptr);
auto slot = resources->allocSlot(); auto slot = resources->allocVariant();
if (!slot) if (!slot)
return false; return false;
JsonVariant variant(slot->data(), resources); JsonVariant variant(slot.data(), resources);
if (!variant.set(detail::forward<T>(value))) { if (!variant.set(detail::forward<T>(value))) {
resources->freeSlot(slot); resources->freeVariant(slot);
return false; return false;
} }
CollectionData::appendOne(slot, resources); CollectionData::appendOne(slot, resources);

View File

@ -12,7 +12,7 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantData; class VariantData;
class VariantSlot; union VariantSlot;
class CollectionIterator { class CollectionIterator {
friend class CollectionData; friend class CollectionData;
@ -58,9 +58,9 @@ class CollectionIterator {
} }
private: private:
CollectionIterator(VariantSlot* slot, SlotId slotId); CollectionIterator(VariantData* slot, SlotId slotId);
VariantSlot* slot_; VariantData* slot_;
SlotId currentId_, nextId_; SlotId currentId_, nextId_;
}; };
@ -79,7 +79,7 @@ class CollectionData {
using iterator = CollectionIterator; using iterator = CollectionIterator;
iterator createIterator(const ResourceManager* resources) const { iterator createIterator(const ResourceManager* resources) const {
return iterator(resources->getSlot(head_), head_); return iterator(resources->getVariant(head_), head_);
} }
size_t size(const ResourceManager*) const; size_t size(const ResourceManager*) const;
@ -98,15 +98,15 @@ class CollectionData {
} }
protected: protected:
void appendOne(SlotWithId slot, const ResourceManager* resources); void appendOne(VariantWithId slot, const ResourceManager* resources);
void appendPair(SlotWithId key, SlotWithId value, void appendPair(VariantWithId key, VariantWithId value,
const ResourceManager* resources); const ResourceManager* resources);
void removeOne(iterator it, ResourceManager* resources); void removeOne(iterator it, ResourceManager* resources);
void removePair(iterator it, ResourceManager* resources); void removePair(iterator it, ResourceManager* resources);
private: private:
SlotWithId getPreviousSlot(VariantSlot*, const ResourceManager*) const; VariantWithId getPreviousSlot(VariantData*, const ResourceManager*) const;
}; };
inline const VariantData* collectionToVariant( inline const VariantData* collectionToVariant(

View File

@ -12,23 +12,23 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline CollectionIterator::CollectionIterator(VariantSlot* slot, SlotId slotId) inline CollectionIterator::CollectionIterator(VariantData* slot, SlotId slotId)
: slot_(slot), currentId_(slotId) { : slot_(slot), currentId_(slotId) {
nextId_ = slot_ ? slot_->next() : NULL_SLOT; nextId_ = slot_ ? slot_->next() : NULL_SLOT;
} }
inline void CollectionIterator::next(const ResourceManager* resources) { inline void CollectionIterator::next(const ResourceManager* resources) {
ARDUINOJSON_ASSERT(currentId_ != NULL_SLOT); ARDUINOJSON_ASSERT(currentId_ != NULL_SLOT);
slot_ = resources->getSlot(nextId_); slot_ = resources->getVariant(nextId_);
currentId_ = nextId_; currentId_ = nextId_;
if (slot_) if (slot_)
nextId_ = slot_->next(); nextId_ = slot_->next();
} }
inline void CollectionData::appendOne(SlotWithId slot, inline void CollectionData::appendOne(VariantWithId slot,
const ResourceManager* resources) { const ResourceManager* resources) {
if (tail_ != NULL_SLOT) { if (tail_ != NULL_SLOT) {
auto tail = resources->getSlot(tail_); auto tail = resources->getVariant(tail_);
tail->setNext(slot.id()); tail->setNext(slot.id());
tail_ = slot.id(); tail_ = slot.id();
} else { } else {
@ -37,12 +37,12 @@ inline void CollectionData::appendOne(SlotWithId slot,
} }
} }
inline void CollectionData::appendPair(SlotWithId key, SlotWithId value, inline void CollectionData::appendPair(VariantWithId key, VariantWithId value,
const ResourceManager* resources) { const ResourceManager* resources) {
key->setNext(value.id()); key->setNext(value.id());
if (tail_ != NULL_SLOT) { if (tail_ != NULL_SLOT) {
auto tail = resources->getSlot(tail_); auto tail = resources->getVariant(tail_);
tail->setNext(key.id()); tail->setNext(key.id());
tail_ = value.id(); tail_ = value.id();
} else { } else {
@ -55,24 +55,24 @@ inline void CollectionData::clear(ResourceManager* resources) {
auto next = head_; auto next = head_;
while (next != NULL_SLOT) { while (next != NULL_SLOT) {
auto currId = next; auto currId = next;
auto slot = resources->getSlot(next); auto slot = resources->getVariant(next);
next = slot->next(); next = slot->next();
resources->freeSlot(SlotWithId(slot, currId)); resources->freeVariant(VariantWithId(slot, currId));
} }
head_ = NULL_SLOT; head_ = NULL_SLOT;
tail_ = NULL_SLOT; tail_ = NULL_SLOT;
} }
inline SlotWithId CollectionData::getPreviousSlot( inline VariantWithId CollectionData::getPreviousSlot(
VariantSlot* target, const ResourceManager* resources) const { VariantData* target, const ResourceManager* resources) const {
auto prev = SlotWithId(); auto prev = VariantWithId();
auto currentId = head_; auto currentId = head_;
while (currentId != NULL_SLOT) { while (currentId != NULL_SLOT) {
auto currentSlot = resources->getSlot(currentId); auto currentSlot = resources->getVariant(currentId);
if (currentSlot == target) if (currentSlot == target)
break; break;
prev = SlotWithId(currentSlot, currentId); prev = VariantWithId(currentSlot, currentId);
currentId = currentSlot->next(); currentId = currentSlot->next();
} }
return prev; return prev;
@ -90,7 +90,7 @@ inline void CollectionData::removeOne(iterator it, ResourceManager* resources) {
head_ = next; head_ = next;
if (next == NULL_SLOT) if (next == NULL_SLOT)
tail_ = prev.id(); tail_ = prev.id();
resources->freeSlot({it.slot_, it.currentId_}); resources->freeVariant({it.slot_, it.currentId_});
} }
inline void CollectionData::removePair(ObjectData::iterator it, inline void CollectionData::removePair(ObjectData::iterator it,
@ -101,11 +101,11 @@ inline void CollectionData::removePair(ObjectData::iterator it,
auto keySlot = it.slot_; auto keySlot = it.slot_;
auto valueId = it.nextId_; auto valueId = it.nextId_;
auto valueSlot = resources->getSlot(valueId); auto valueSlot = resources->getVariant(valueId);
// remove value slot // remove value slot
keySlot->setNext(valueSlot->next()); keySlot->setNext(valueSlot->next());
resources->freeSlot({valueSlot, valueId}); resources->freeVariant({valueSlot, valueId});
// remove key slot // remove key slot
removeOne(it, resources); removeOne(it, resources);

View File

@ -25,9 +25,9 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
auto slotId = array.head(); auto slotId = array.head();
while (slotId != NULL_SLOT) { while (slotId != NULL_SLOT) {
auto slot = resources_->getSlot(slotId); auto slot = resources_->getVariant(slotId);
slot->data()->accept(*this); slot->accept(*this);
slotId = slot->next(); slotId = slot->next();
@ -47,8 +47,8 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
bool isKey = true; bool isKey = true;
while (slotId != NULL_SLOT) { while (slotId != NULL_SLOT) {
auto slot = resources_->getSlot(slotId); auto slot = resources_->getVariant(slotId);
slot->data()->accept(*this); slot->accept(*this);
slotId = slot->next(); slotId = slot->next();

View File

@ -13,8 +13,10 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantSlot; union VariantSlot;
class VariantPool; class VariantPool;
class VariantData;
class VariantWithId;
class ResourceManager { class ResourceManager {
public: public:
@ -49,18 +51,11 @@ class ResourceManager {
return overflowed_; return overflowed_;
} }
SlotWithId allocSlot() { VariantWithId allocVariant();
auto p = variantPools_.allocSlot(allocator_);
if (!p)
overflowed_ = true;
return p;
}
void freeSlot(SlotWithId slot); void freeVariant(VariantWithId slot);
VariantSlot* getSlot(SlotId id) const { VariantData* getVariant(SlotId id) const;
return variantPools_.getSlot(id);
}
template <typename TAdaptedString> template <typename TAdaptedString>
StringNode* saveString(TAdaptedString str) { StringNode* saveString(TAdaptedString str) {

View File

@ -7,12 +7,26 @@
#include <ArduinoJson/Collection/CollectionData.hpp> #include <ArduinoJson/Collection/CollectionData.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp> #include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Variant/VariantData.hpp> #include <ArduinoJson/Variant/VariantData.hpp>
#include <ArduinoJson/Variant/VariantSlot.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline void ResourceManager::freeSlot(SlotWithId slot) { inline VariantWithId ResourceManager::allocVariant() {
slot->data()->setNull(this); auto p = variantPools_.allocSlot(allocator_);
variantPools_.freeSlot(slot); if (!p) {
overflowed_ = true;
return {};
}
return {new (&p->variant) VariantData, p.id()};
}
inline void ResourceManager::freeVariant(VariantWithId variant) {
variant->setNull(this);
variantPools_.freeSlot(variant);
}
inline VariantData* ResourceManager::getVariant(SlotId id) const {
return reinterpret_cast<VariantData*>(variantPools_.getSlot(id));
} }
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -12,7 +12,7 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantSlot; union VariantSlot;
class VariantPool; class VariantPool;
class StringPool { class StringPool {

View File

@ -10,7 +10,7 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantSlot; union VariantSlot;
using SlotId = uint_t<ARDUINOJSON_SLOT_ID_SIZE * 8>; using SlotId = uint_t<ARDUINOJSON_SLOT_ID_SIZE * 8>;
using SlotCount = SlotId; using SlotCount = SlotId;
const SlotId NULL_SLOT = SlotId(-1); const SlotId NULL_SLOT = SlotId(-1);
@ -22,11 +22,15 @@ class SlotWithId {
ARDUINOJSON_ASSERT((slot == nullptr) == (id == NULL_SLOT)); ARDUINOJSON_ASSERT((slot == nullptr) == (id == NULL_SLOT));
} }
explicit operator bool() const {
return slot_ != nullptr;
}
SlotId id() const { SlotId id() const {
return id_; return id_;
} }
operator VariantSlot*() { VariantSlot* slot() const {
return slot_; return slot_;
} }

View File

@ -41,7 +41,7 @@ inline SlotWithId VariantPool::allocSlot() {
return {}; return {};
auto index = usage_++; auto index = usage_++;
auto slot = &slots_[index]; auto slot = &slots_[index];
return {new (slot) VariantSlot, SlotId(index)}; return {slot, SlotId(index)};
} }
inline VariantSlot* VariantPool::getSlot(SlotId id) const { inline VariantSlot* VariantPool::getSlot(SlotId id) const {
@ -69,12 +69,12 @@ inline SlotWithId VariantPoolList::allocFromFreeList() {
ARDUINOJSON_ASSERT(freeList_ != NULL_SLOT); ARDUINOJSON_ASSERT(freeList_ != NULL_SLOT);
auto id = freeList_; auto id = freeList_;
auto slot = getSlot(freeList_); auto slot = getSlot(freeList_);
freeList_ = slot->next(); freeList_ = slot->free.next;
return {new (slot) VariantSlot, id}; return {slot, id};
} }
inline void VariantPoolList::freeSlot(SlotWithId slot) { inline void VariantPoolList::freeSlot(SlotWithId slot) {
slot->setNext(freeList_); slot->free.next = freeList_;
freeList_ = slot.id(); freeList_ = slot.id();
} }

View File

@ -138,7 +138,8 @@ class VariantPoolList {
auto slot = pools_[poolIndex].allocSlot(); auto slot = pools_[poolIndex].allocSlot();
if (!slot) if (!slot)
return {}; return {};
return {slot, SlotId(poolIndex * ARDUINOJSON_POOL_CAPACITY + slot.id())}; return {slot.slot(),
SlotId(poolIndex * ARDUINOJSON_POOL_CAPACITY + slot.id())};
} }
VariantPool* addPool(Allocator* allocator) { VariantPool* addPool(Allocator* allocator) {

View File

@ -61,8 +61,8 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
auto slotId = array.head(); auto slotId = array.head();
while (slotId != NULL_SLOT) { while (slotId != NULL_SLOT) {
auto slot = resources_->getSlot(slotId); auto slot = resources_->getVariant(slotId);
slot->data()->accept(*this); slot->accept(*this);
slotId = slot->next(); slotId = slot->next();
} }
@ -83,8 +83,8 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
auto slotId = object.head(); auto slotId = object.head();
while (slotId != NULL_SLOT) { while (slotId != NULL_SLOT) {
auto slot = resources_->getSlot(slotId); auto slot = resources_->getVariant(slotId);
slot->data()->accept(*this); slot->accept(*this);
slotId = slot->next(); slotId = slot->next();
} }

View File

@ -51,20 +51,20 @@ inline void ObjectData::removeMember(TAdaptedString key,
template <typename TAdaptedString> template <typename TAdaptedString>
inline VariantData* ObjectData::addMember(TAdaptedString key, inline VariantData* ObjectData::addMember(TAdaptedString key,
ResourceManager* resources) { ResourceManager* resources) {
auto keySlot = resources->allocSlot(); auto keySlot = resources->allocVariant();
if (!keySlot) if (!keySlot)
return nullptr; return nullptr;
auto valueSlot = resources->allocSlot(); auto valueSlot = resources->allocVariant();
if (!valueSlot) if (!valueSlot)
return nullptr; return nullptr;
if (!keySlot->data()->setString(key, resources)) if (!keySlot->setString(key, resources))
return nullptr; return nullptr;
CollectionData::appendPair(keySlot, valueSlot, resources); CollectionData::appendPair(keySlot, valueSlot, resources);
return valueSlot->data(); return valueSlot.data();
} }
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -5,12 +5,12 @@
#pragma once #pragma once
#include <ArduinoJson/Memory/StringNode.hpp> #include <ArduinoJson/Memory/StringNode.hpp>
#include <ArduinoJson/Memory/VariantPool.hpp>
#include <ArduinoJson/Misc/SerializedValue.hpp> #include <ArduinoJson/Misc/SerializedValue.hpp>
#include <ArduinoJson/Numbers/convertNumber.hpp> #include <ArduinoJson/Numbers/convertNumber.hpp>
#include <ArduinoJson/Strings/JsonString.hpp> #include <ArduinoJson/Strings/JsonString.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp> #include <ArduinoJson/Strings/StringAdapters.hpp>
#include <ArduinoJson/Variant/VariantContent.hpp> #include <ArduinoJson/Variant/VariantContent.hpp>
#include <ArduinoJson/Variant/VariantSlot.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
@ -20,9 +20,25 @@ T parseNumber(const char* s);
class VariantData { class VariantData {
VariantContent content_; // must be first to allow cast from array to variant VariantContent content_; // must be first to allow cast from array to variant
uint8_t type_; uint8_t type_;
SlotId next_;
public: public:
VariantData() : type_(VALUE_IS_NULL) {} // Placement new
static void* operator new(size_t, void* p) noexcept {
return p;
}
static void operator delete(void*, void*) noexcept {}
VariantData() : type_(VALUE_IS_NULL), next_(NULL_SLOT) {}
SlotId next() const {
return next_;
}
void setNext(SlotId slot) {
next_ = slot;
}
template <typename TVisitor> template <typename TVisitor>
typename TVisitor::result_type accept(TVisitor& visit) const { typename TVisitor::result_type accept(TVisitor& visit) const {
@ -518,4 +534,20 @@ class VariantData {
} }
}; };
class VariantWithId : public SlotWithId {
public:
VariantWithId() {}
VariantWithId(VariantData* data, SlotId id)
: SlotWithId(reinterpret_cast<VariantSlot*>(data), id) {}
VariantData* data() {
return reinterpret_cast<VariantData*>(slot());
}
VariantData* operator->() {
ARDUINOJSON_ASSERT(data() != nullptr);
return data();
}
};
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -7,52 +7,22 @@
#include <ArduinoJson/Memory/ResourceManager.hpp> #include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Polyfills/limits.hpp> #include <ArduinoJson/Polyfills/limits.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp> #include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Variant/VariantContent.hpp> #include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
struct StringNode; struct StringNode;
class VariantSlot { struct FreeSlot {
// CAUTION: same layout as VariantData SlotId next;
// we cannot use composition because it adds padding
// (+20% on ESP8266 for example)
VariantContent content_;
uint8_t type_;
SlotId next_;
public:
// Placement new
static void* operator new(size_t, void* p) noexcept {
return p;
}
static void operator delete(void*, void*) noexcept {}
VariantSlot() : type_(0), next_(NULL_SLOT) {
(void)type_; // HACK: suppress Clang warning "private field is not used"
}
VariantData* data() {
return reinterpret_cast<VariantData*>(&content_);
}
const VariantData* data() const {
return reinterpret_cast<const VariantData*>(&content_);
}
SlotId next() const {
return next_;
}
void setNext(SlotId slot) {
next_ = slot;
}
}; };
inline VariantData* slotData(VariantSlot* slot) { union VariantSlot {
return reinterpret_cast<VariantData*>(slot); VariantSlot() {}
}
VariantData variant;
FreeSlot free;
};
// Returns the size (in bytes) of an array with n elements. // Returns the size (in bytes) of an array with n elements.
constexpr size_t sizeofArray(size_t n) { constexpr size_t sizeofArray(size_t n) {