Make MemoryPool generic

This commit is contained in:
Benoit Blanchon
2024-08-25 14:57:35 +02:00
parent 2be24eded8
commit 362201241f
18 changed files with 178 additions and 192 deletions

View File

@ -265,12 +265,14 @@ class TimebombAllocator : public ArduinoJson::Allocator {
} // namespace
inline size_t sizeofPoolList(size_t n = ARDUINOJSON_INITIAL_POOL_COUNT) {
return sizeof(ArduinoJson::detail::MemoryPool) * n;
using namespace ArduinoJson::detail;
return sizeof(MemoryPool<VariantData>) * n;
}
inline size_t sizeofPool(
ArduinoJson::detail::SlotCount n = ARDUINOJSON_POOL_CAPACITY) {
return ArduinoJson::detail::MemoryPool::slotsToBytes(n);
using namespace ArduinoJson::detail;
return MemoryPool<VariantData>::slotsToBytes(n);
}
inline size_t sizeofStringBuffer(size_t iteration = 1) {

View File

@ -2,7 +2,6 @@
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson/Memory/MemoryPoolImpl.hpp>
#include <ArduinoJson/Memory/StringBuilder.hpp>
#include <catch.hpp>

View File

@ -2,7 +2,6 @@
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson/Memory/MemoryPoolImpl.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>

View File

@ -2,7 +2,6 @@
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson/Memory/MemoryPoolImpl.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <catch.hpp>

View File

@ -2,7 +2,6 @@
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson/Memory/MemoryPoolImpl.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
#include <catch.hpp>

View File

@ -2,7 +2,6 @@
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson/Memory/MemoryPoolImpl.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
#include <catch.hpp>

View File

@ -3,7 +3,6 @@
// MIT License
#include <ArduinoJson/Memory/Alignment.hpp>
#include <ArduinoJson/Memory/MemoryPoolImpl.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
#include <catch.hpp>

View File

@ -36,13 +36,13 @@
#include "ArduinoJson/Array/ElementProxy.hpp"
#include "ArduinoJson/Array/Utilities.hpp"
#include "ArduinoJson/Collection/CollectionImpl.hpp"
#include "ArduinoJson/Memory/MemoryPoolImpl.hpp"
#include "ArduinoJson/Memory/ResourceManagerImpl.hpp"
#include "ArduinoJson/Object/MemberProxy.hpp"
#include "ArduinoJson/Object/ObjectImpl.hpp"
#include "ArduinoJson/Variant/ConverterImpl.hpp"
#include "ArduinoJson/Variant/JsonVariantCopier.hpp"
#include "ArduinoJson/Variant/VariantCompare.hpp"
#include "ArduinoJson/Variant/VariantImpl.hpp"
#include "ArduinoJson/Variant/VariantRefBaseImpl.hpp"
#include "ArduinoJson/Json/JsonDeserializer.hpp"

View File

@ -4,7 +4,7 @@
#pragma once
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
@ -13,6 +13,7 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantData;
class ResourceManager;
class CollectionIterator {
friend class CollectionData;
@ -78,9 +79,7 @@ class CollectionData {
using iterator = CollectionIterator;
iterator createIterator(const ResourceManager* resources) const {
return iterator(resources->getVariant(head_), head_);
}
iterator createIterator(const ResourceManager* resources) const;
size_t size(const ResourceManager*) const;
size_t nesting(const ResourceManager*) const;
@ -98,15 +97,17 @@ class CollectionData {
}
protected:
void appendOne(VariantWithId slot, const ResourceManager* resources);
void appendPair(VariantWithId key, VariantWithId value,
void appendOne(SlotWithId<VariantData> slot,
const ResourceManager* resources);
void appendPair(SlotWithId<VariantData> key, SlotWithId<VariantData> value,
const ResourceManager* resources);
void removeOne(iterator it, ResourceManager* resources);
void removePair(iterator it, ResourceManager* resources);
private:
VariantWithId getPreviousSlot(VariantData*, const ResourceManager*) const;
SlotWithId<VariantData> getPreviousSlot(VariantData*,
const ResourceManager*) const;
};
inline const VariantData* collectionToVariant(

View File

@ -25,7 +25,12 @@ inline void CollectionIterator::next(const ResourceManager* resources) {
nextId_ = slot_->next();
}
inline void CollectionData::appendOne(VariantWithId slot,
inline CollectionData::iterator CollectionData::createIterator(
const ResourceManager* resources) const {
return iterator(resources->getVariant(head_), head_);
}
inline void CollectionData::appendOne(SlotWithId<VariantData> slot,
const ResourceManager* resources) {
if (tail_ != NULL_SLOT) {
auto tail = resources->getVariant(tail_);
@ -37,7 +42,8 @@ inline void CollectionData::appendOne(VariantWithId slot,
}
}
inline void CollectionData::appendPair(VariantWithId key, VariantWithId value,
inline void CollectionData::appendPair(SlotWithId<VariantData> key,
SlotWithId<VariantData> value,
const ResourceManager* resources) {
key->setNext(value.id());
@ -57,22 +63,22 @@ inline void CollectionData::clear(ResourceManager* resources) {
auto currId = next;
auto slot = resources->getVariant(next);
next = slot->next();
resources->freeVariant(VariantWithId(slot, currId));
resources->freeVariant(SlotWithId<VariantData>(slot, currId));
}
head_ = NULL_SLOT;
tail_ = NULL_SLOT;
}
inline VariantWithId CollectionData::getPreviousSlot(
inline SlotWithId<VariantData> CollectionData::getPreviousSlot(
VariantData* target, const ResourceManager* resources) const {
auto prev = VariantWithId();
auto prev = SlotWithId<VariantData>();
auto currentId = head_;
while (currentId != NULL_SLOT) {
auto currentSlot = resources->getVariant(currentId);
if (currentSlot == target)
break;
prev = VariantWithId(currentSlot, currentId);
prev = SlotWithId<VariantData>(currentSlot, currentId);
currentId = currentSlot->next();
}
return prev;

View File

@ -10,15 +10,15 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantData;
using SlotId = uint_t<ARDUINOJSON_SLOT_ID_SIZE * 8>;
using SlotCount = SlotId;
const SlotId NULL_SLOT = SlotId(-1);
template <typename T>
class SlotWithId {
public:
SlotWithId() : slot_(nullptr), id_(NULL_SLOT) {}
SlotWithId(VariantData* slot, SlotId id) : slot_(slot), id_(id) {
SlotWithId(T* slot, SlotId id) : slot_(slot), id_(id) {
ARDUINOJSON_ASSERT((slot == nullptr) == (id == NULL_SLOT));
}
@ -30,38 +30,81 @@ class SlotWithId {
return id_;
}
VariantData* ptr() const {
T* ptr() const {
return slot_;
}
VariantData* operator->() {
T* operator->() const {
ARDUINOJSON_ASSERT(slot_ != nullptr);
return slot_;
}
private:
VariantData* slot_;
T* slot_;
SlotId id_;
};
template <typename T>
class MemoryPool {
public:
void create(SlotCount cap, Allocator* allocator);
void destroy(Allocator* allocator);
void create(SlotCount cap, Allocator* allocator) {
ARDUINOJSON_ASSERT(cap > 0);
slots_ = reinterpret_cast<T*>(allocator->allocate(slotsToBytes(cap)));
capacity_ = slots_ ? cap : 0;
usage_ = 0;
}
SlotWithId allocSlot();
VariantData* getSlot(SlotId id) const;
void clear();
void shrinkToFit(Allocator*);
SlotCount usage() const;
void destroy(Allocator* allocator) {
if (slots_)
allocator->deallocate(slots_);
slots_ = nullptr;
capacity_ = 0;
usage_ = 0;
}
static SlotCount bytesToSlots(size_t);
static size_t slotsToBytes(SlotCount);
SlotWithId<T> allocSlot() {
if (!slots_)
return {};
if (usage_ >= capacity_)
return {};
auto index = usage_++;
return {slots_ + index, SlotId(index)};
}
T* getSlot(SlotId id) const {
ARDUINOJSON_ASSERT(id < usage_);
return slots_ + id;
}
void clear() {
usage_ = 0;
}
void shrinkToFit(Allocator* allocator) {
auto newSlots = reinterpret_cast<T*>(
allocator->reallocate(slots_, slotsToBytes(usage_)));
if (newSlots) {
slots_ = newSlots;
capacity_ = usage_;
}
}
SlotCount usage() const {
return usage_;
}
static SlotCount bytesToSlots(size_t n) {
return static_cast<SlotCount>(n / sizeof(T));
}
static size_t slotsToBytes(SlotCount n) {
return n * sizeof(T);
}
private:
SlotCount capacity_;
SlotCount usage_;
VariantData* slots_;
T* slots_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -1,81 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline void MemoryPool::create(SlotCount cap, Allocator* allocator) {
ARDUINOJSON_ASSERT(cap > 0);
slots_ =
reinterpret_cast<VariantData*>(allocator->allocate(slotsToBytes(cap)));
capacity_ = slots_ ? cap : 0;
usage_ = 0;
}
inline void MemoryPool::destroy(Allocator* allocator) {
if (slots_)
allocator->deallocate(slots_);
slots_ = nullptr;
capacity_ = 0;
usage_ = 0;
}
inline void MemoryPool::shrinkToFit(Allocator* allocator) {
auto newSlots = reinterpret_cast<VariantData*>(
allocator->reallocate(slots_, slotsToBytes(usage_)));
if (newSlots) {
slots_ = newSlots;
capacity_ = usage_;
}
}
inline SlotWithId MemoryPool::allocSlot() {
if (!slots_)
return {};
if (usage_ >= capacity_)
return {};
auto index = usage_++;
auto slot = &slots_[index];
return {slot, SlotId(index)};
}
inline VariantData* MemoryPool::getSlot(SlotId id) const {
ARDUINOJSON_ASSERT(id < usage_);
return &slots_[id];
}
inline SlotCount MemoryPool::usage() const {
return usage_;
}
inline void MemoryPool::clear() {
usage_ = 0;
}
inline SlotCount MemoryPool::bytesToSlots(size_t n) {
return static_cast<SlotCount>(n / sizeof(VariantData));
}
inline size_t MemoryPool::slotsToBytes(SlotCount n) {
return n * sizeof(VariantData);
}
inline SlotWithId MemoryPoolList::allocFromFreeList() {
ARDUINOJSON_ASSERT(freeList_ != NULL_SLOT);
auto id = freeList_;
auto slot = getSlot(freeList_);
freeList_ = reinterpret_cast<FreeSlot*>(slot)->next;
return {slot, id};
}
inline void MemoryPoolList::freeSlot(SlotWithId slot) {
reinterpret_cast<FreeSlot*>(slot.ptr())->next = freeList_;
freeList_ = slot.id();
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -14,12 +14,17 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
using PoolCount = SlotId;
template <typename T>
class MemoryPoolList {
struct FreeSlot {
SlotId next;
};
static_assert(sizeof(FreeSlot) <= sizeof(T), "T is too small");
public:
using Pool = MemoryPool<T>;
MemoryPoolList() = default;
~MemoryPoolList() {
@ -74,7 +79,7 @@ class MemoryPoolList {
return *this;
}
SlotWithId allocSlot(Allocator* allocator) {
SlotWithId<T> allocSlot(Allocator* allocator) {
// try to allocate from free list
if (freeList_ != NULL_SLOT) {
return allocFromFreeList();
@ -95,9 +100,12 @@ class MemoryPoolList {
return allocFromLastPool();
}
void freeSlot(SlotWithId slot);
void freeSlot(SlotWithId<T> slot) {
reinterpret_cast<FreeSlot*>(slot.ptr())->next = freeList_;
freeList_ = slot.id();
}
VariantData* getSlot(SlotId id) const {
T* getSlot(SlotId id) const {
if (id == NULL_SLOT)
return nullptr;
auto poolIndex = SlotId(id / ARDUINOJSON_POOL_CAPACITY);
@ -125,21 +133,31 @@ class MemoryPoolList {
return total;
}
size_t size() const {
return Pool::slotsToBytes(usage());
}
void shrinkToFit(Allocator* allocator) {
if (count_ > 0)
pools_[count_ - 1].shrinkToFit(allocator);
if (pools_ != preallocatedPools_ && count_ != capacity_) {
pools_ = static_cast<MemoryPool*>(
allocator->reallocate(pools_, count_ * sizeof(MemoryPool)));
pools_ = static_cast<Pool*>(
allocator->reallocate(pools_, count_ * sizeof(Pool)));
ARDUINOJSON_ASSERT(pools_ != nullptr); // realloc to smaller can't fail
capacity_ = count_;
}
}
private:
SlotWithId allocFromFreeList();
SlotWithId<T> allocFromFreeList() {
ARDUINOJSON_ASSERT(freeList_ != NULL_SLOT);
auto id = freeList_;
auto slot = getSlot(freeList_);
freeList_ = reinterpret_cast<FreeSlot*>(slot)->next;
return {slot, id};
}
SlotWithId allocFromLastPool() {
SlotWithId<T> allocFromLastPool() {
ARDUINOJSON_ASSERT(count_ > 0);
auto poolIndex = SlotId(count_ - 1);
auto slot = pools_[poolIndex].allocSlot();
@ -149,7 +167,7 @@ class MemoryPoolList {
SlotId(poolIndex * ARDUINOJSON_POOL_CAPACITY + slot.id())};
}
MemoryPool* addPool(Allocator* allocator) {
Pool* addPool(Allocator* allocator) {
if (count_ == capacity_ && !increaseCapacity(allocator))
return nullptr;
auto pool = &pools_[count_++];
@ -167,24 +185,23 @@ class MemoryPoolList {
auto newCapacity = PoolCount(capacity_ * 2);
if (pools_ == preallocatedPools_) {
newPools = allocator->allocate(newCapacity * sizeof(MemoryPool));
newPools = allocator->allocate(newCapacity * sizeof(Pool));
if (!newPools)
return false;
memcpy(newPools, preallocatedPools_, sizeof(preallocatedPools_));
} else {
newPools =
allocator->reallocate(pools_, newCapacity * sizeof(MemoryPool));
newPools = allocator->reallocate(pools_, newCapacity * sizeof(Pool));
if (!newPools)
return false;
}
pools_ = static_cast<MemoryPool*>(newPools);
pools_ = static_cast<Pool*>(newPools);
capacity_ = newCapacity;
return true;
}
MemoryPool preallocatedPools_[ARDUINOJSON_INITIAL_POOL_COUNT];
MemoryPool* pools_ = preallocatedPools_;
Pool preallocatedPools_[ARDUINOJSON_INITIAL_POOL_COUNT];
Pool* pools_ = preallocatedPools_;
PoolCount count_ = 0;
PoolCount capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT;
SlotId freeList_ = NULL_SLOT;

View File

@ -10,10 +10,10 @@
#include <ArduinoJson/Polyfills/assert.hpp>
#include <ArduinoJson/Polyfills/utility.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class MemoryPool;
class VariantData;
class VariantWithId;
@ -42,16 +42,16 @@ class ResourceManager {
}
size_t size() const {
return MemoryPool::slotsToBytes(variantPools_.usage()) + stringPool_.size();
return variantPools_.size() + stringPool_.size();
}
bool overflowed() const {
return overflowed_;
}
VariantWithId allocVariant();
SlotWithId<VariantData> allocVariant();
void freeVariant(VariantWithId slot);
void freeVariant(SlotWithId<VariantData> slot);
VariantData* getVariant(SlotId id) const;
@ -112,7 +112,7 @@ class ResourceManager {
Allocator* allocator_;
bool overflowed_;
StringPool stringPool_;
MemoryPoolList variantPools_;
MemoryPoolList<VariantData> variantPools_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -10,7 +10,7 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline VariantWithId ResourceManager::allocVariant() {
inline SlotWithId<VariantData> ResourceManager::allocVariant() {
auto p = variantPools_.allocSlot(allocator_);
if (!p) {
overflowed_ = true;
@ -19,7 +19,7 @@ inline VariantWithId ResourceManager::allocVariant() {
return {new (p.ptr()) VariantData, p.id()};
}
inline void ResourceManager::freeVariant(VariantWithId variant) {
inline void ResourceManager::freeVariant(SlotWithId<VariantData> variant) {
variant->setNull(this);
variantPools_.freeSlot(variant);
}

View File

@ -12,8 +12,6 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class MemoryPool;
class StringPool {
public:
StringPool() = default;

View File

@ -408,14 +408,7 @@ class VariantData {
}
template <typename T>
void setRawString(SerializedValue<T> value, ResourceManager* resources) {
release(resources);
auto dup = resources->saveString(adaptString(value.data(), value.size()));
if (dup)
setRawString(dup);
else
setNull();
}
void setRawString(SerializedValue<T> value, ResourceManager* resources);
template <typename T>
static void setRawString(VariantData* var, SerializedValue<T> value,
@ -426,25 +419,7 @@ class VariantData {
}
template <typename TAdaptedString>
bool setString(TAdaptedString value, ResourceManager* resources) {
setNull(resources);
if (value.isNull())
return false;
if (value.isLinked()) {
setLinkedString(value.data());
return true;
}
auto dup = resources->saveString(value);
if (dup) {
setOwnedString(dup);
return true;
}
return false;
}
bool setString(TAdaptedString value, ResourceManager* resources);
bool setString(StringNode* s, ResourceManager*) {
setOwnedString(s);
@ -524,30 +499,7 @@ class VariantData {
}
private:
void release(ResourceManager* resources) {
if (type_ & OWNED_VALUE_BIT)
resources->dereferenceString(content_.asOwnedString->data);
auto collection = asCollection();
if (collection)
collection->clear(resources);
}
};
class VariantWithId : public SlotWithId {
public:
VariantWithId() {}
VariantWithId(VariantData* data, SlotId id)
: SlotWithId(reinterpret_cast<VariantData*>(data), id) {}
VariantData* ptr() {
return reinterpret_cast<VariantData*>(SlotWithId::ptr());
}
VariantData* operator->() {
ARDUINOJSON_ASSERT(ptr() != nullptr);
return ptr();
}
void release(ResourceManager* resources);
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -0,0 +1,54 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename T>
inline void VariantData::setRawString(SerializedValue<T> value,
ResourceManager* resources) {
release(resources);
auto dup = resources->saveString(adaptString(value.data(), value.size()));
if (dup)
setRawString(dup);
else
setNull();
}
template <typename TAdaptedString>
inline bool VariantData::setString(TAdaptedString value,
ResourceManager* resources) {
setNull(resources);
if (value.isNull())
return false;
if (value.isLinked()) {
setLinkedString(value.data());
return true;
}
auto dup = resources->saveString(value);
if (dup) {
setOwnedString(dup);
return true;
}
return false;
}
inline void VariantData::release(ResourceManager* resources) {
if (type_ & OWNED_VALUE_BIT)
resources->dereferenceString(content_.asOwnedString->data);
auto collection = asCollection();
if (collection)
collection->clear(resources);
}
ARDUINOJSON_END_PRIVATE_NAMESPACE