Create more memory pools as needed (resolves #1074)

This commit is contained in:
Benoit Blanchon
2023-07-18 09:08:55 +02:00
parent 65c67d317a
commit 42b2840009
22 changed files with 396 additions and 312 deletions
+20
View File
@@ -90,6 +90,26 @@
# endif
#endif
// Capacity of each variant pool (in slots)
#ifndef ARDUINOJSON_POOL_CAPACITY
# if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ <= 2
// Address space == 16-bit
# define ARDUINOJSON_POOL_CAPACITY 16 // 128 bytes
# elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ >= 8 || \
defined(_WIN64) && _WIN64
// Address space == 64-bit
# define ARDUINOJSON_POOL_CAPACITY 128 // 3072 bytes
# else
// Address space == 32-bit
# define ARDUINOJSON_POOL_CAPACITY 64 // 1024 bytes
# endif
#endif
// Capacity of each variant pool (in slots)
#ifndef ARDUINOJSON_INITIAL_POOL_COUNT
# define ARDUINOJSON_INITIAL_POOL_COUNT 4
#endif
#ifdef ARDUINO
// Enable support for Arduino's String class
+1 -1
View File
@@ -94,7 +94,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
bool garbageCollect() {
// make a temporary clone and move assign
JsonDocument tmp(*this);
if (!tmp.resources_.capacity())
if (tmp.overflowed())
return false;
moveAssignFrom(tmp);
return true;
+16 -18
View File
@@ -6,7 +6,7 @@
#include <ArduinoJson/Memory/Allocator.hpp>
#include <ArduinoJson/Memory/StringPool.hpp>
#include <ArduinoJson/Memory/VariantPool.hpp>
#include <ArduinoJson/Memory/VariantPoolList.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <ArduinoJson/Polyfills/utility.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
@@ -18,15 +18,13 @@ class VariantPool;
class ResourceManager {
public:
ResourceManager(size_t capa,
ResourceManager(size_t /*capa*/,
Allocator* allocator = DefaultAllocator::instance())
: allocator_(allocator), overflowed_(false) {
variantPool_.create(capa, allocator);
}
: allocator_(allocator), overflowed_(false) {}
~ResourceManager() {
stringPool_.clear(allocator_);
variantPool_.destroy(allocator_);
variantPools_.clear(allocator_);
}
ResourceManager(const ResourceManager&) = delete;
@@ -34,9 +32,9 @@ class ResourceManager {
ResourceManager& operator=(ResourceManager&& src) {
stringPool_.clear(allocator_);
variantPool_.destroy(allocator_);
variantPools_.clear(allocator_);
allocator_ = src.allocator_;
variantPool_ = detail::move(src.variantPool_);
variantPools_ = detail::move(src.variantPools_);
overflowed_ = src.overflowed_;
stringPool_ = detail::move(src.stringPool_);
return *this;
@@ -48,19 +46,19 @@ class ResourceManager {
void reallocPool(size_t requiredSize) {
size_t capa = VariantPool::bytesToSlots(requiredSize);
if (capa == variantPool_.capacity())
if (capa == variantPools_.capacity())
return;
variantPool_.destroy(allocator_);
variantPool_.create(requiredSize, allocator_);
variantPools_.clear(allocator_);
}
// Gets the capacity of the memoryPool in bytes
size_t capacity() const {
return VariantPool::slotsToBytes(variantPool_.capacity());
return VariantPool::slotsToBytes(variantPools_.capacity());
}
size_t size() const {
return VariantPool::slotsToBytes(variantPool_.usage()) + stringPool_.size();
return VariantPool::slotsToBytes(variantPools_.usage()) +
stringPool_.size();
}
bool overflowed() const {
@@ -68,14 +66,14 @@ class ResourceManager {
}
SlotWithId allocSlot() {
auto p = variantPool_.allocSlot();
auto p = variantPools_.allocSlot(allocator_);
if (!p)
overflowed_ = true;
return p;
}
VariantSlot* getSlot(SlotId id) const {
return variantPool_.getSlot(id);
return variantPools_.getSlot(id);
}
template <typename TAdaptedString>
@@ -122,20 +120,20 @@ class ResourceManager {
}
void clear() {
variantPool_.clear();
variantPools_.clear(allocator_);
overflowed_ = false;
stringPool_.clear(allocator_);
}
void shrinkToFit() {
variantPool_.shrinkToFit(allocator_);
variantPools_.shrinkToFit(allocator_);
}
private:
Allocator* allocator_;
bool overflowed_;
StringPool stringPool_;
VariantPool variantPool_;
VariantPoolList variantPools_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE
+1
View File
@@ -31,6 +31,7 @@ class StringBuilder {
StringNode* node = resources_->getString(adaptString(node_->data, size_));
if (!node) {
node = resources_->resizeString(node_, size_);
ARDUINOJSON_ASSERT(node != nullptr); // realloc to smaller can't fail
resources_->saveString(node);
node_ = nullptr; // next time we need a new string
} else {
+4 -18
View File
@@ -42,21 +42,7 @@ class SlotWithId {
class VariantPool {
public:
~VariantPool() {
ARDUINOJSON_ASSERT(slots_ == nullptr);
}
VariantPool& operator=(VariantPool&& src) {
capacity_ = src.capacity_;
src.capacity_ = 0;
usage_ = src.usage_;
src.usage_ = 0;
slots_ = src.slots_;
src.slots_ = nullptr;
return *this;
}
void create(size_t cap, Allocator* allocator);
void create(SlotCount cap, Allocator* allocator);
void destroy(Allocator* allocator);
SlotWithId allocSlot();
@@ -70,9 +56,9 @@ class VariantPool {
static size_t slotsToBytes(SlotCount);
private:
SlotCount capacity_ = 0;
SlotCount usage_ = 0;
VariantSlot* slots_ = nullptr;
SlotCount capacity_;
SlotCount usage_;
VariantSlot* slots_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE
+6 -9
View File
@@ -9,15 +9,12 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline void VariantPool::create(size_t cap, Allocator* allocator) {
ARDUINOJSON_ASSERT(slots_ == nullptr);
if (!cap)
return;
slots_ = reinterpret_cast<VariantSlot*>(allocator->allocate(cap));
if (slots_) {
capacity_ = bytesToSlots(cap);
usage_ = 0;
}
inline void VariantPool::create(SlotCount cap, Allocator* allocator) {
ARDUINOJSON_ASSERT(cap > 0);
slots_ =
reinterpret_cast<VariantSlot*>(allocator->allocate(slotsToBytes(cap)));
capacity_ = slots_ ? cap : 0;
usage_ = 0;
}
inline void VariantPool::destroy(Allocator* allocator) {
+133
View File
@@ -0,0 +1,133 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/VariantPool.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantPoolList {
public:
VariantPoolList() = default;
~VariantPoolList() {
ARDUINOJSON_ASSERT(pools_ == nullptr);
}
VariantPoolList& operator=(VariantPoolList&& src) {
ARDUINOJSON_ASSERT(pools_ == nullptr);
pools_ = src.pools_;
count_ = src.count_;
capacity_ = src.capacity_;
src.pools_ = nullptr;
src.count_ = 0;
src.capacity_ = 0;
return *this;
}
SlotWithId allocSlot(Allocator* allocator) {
// try to allocate from last pool (other pools are full)
if (pools_) {
auto slot = allocFromLastPool();
if (slot)
return slot;
}
// create a new pool and try again
auto pool = addPool(allocator);
if (!pool)
return {};
return allocFromLastPool();
}
VariantSlot* getSlot(SlotId id) const {
auto poolIndex = SlotId(id / ARDUINOJSON_POOL_CAPACITY);
auto indexInPool = SlotId(id % ARDUINOJSON_POOL_CAPACITY);
if (poolIndex >= count_)
return nullptr;
return pools_[poolIndex].getSlot(indexInPool);
}
void clear(Allocator* allocator) {
if (!pools_)
return;
for (size_t i = 0; i < count_; i++)
pools_[i].destroy(allocator);
allocator->deallocate(pools_);
pools_ = nullptr;
count_ = 0;
capacity_ = 0;
}
SlotCount capacity() const {
SlotCount total = 0;
for (size_t i = 0; i < count_; i++)
total = SlotCount(total + pools_[i].capacity());
return total;
}
SlotCount usage() const {
SlotCount total = 0;
for (size_t i = 0; i < count_; i++)
total = SlotCount(total + pools_[i].usage());
return total;
}
void shrinkToFit(Allocator* allocator) {
if (pools_)
pools_[count_ - 1].shrinkToFit(allocator);
if (count_ != capacity_) {
pools_ = static_cast<VariantPool*>(
allocator->reallocate(pools_, count_ * sizeof(VariantPool)));
ARDUINOJSON_ASSERT(pools_ != nullptr); // realloc to smaller can't fail
capacity_ = count_;
}
}
private:
SlotWithId allocFromLastPool() {
ARDUINOJSON_ASSERT(pools_ != nullptr);
auto poolIndex = SlotId(count_ - 1);
auto lastPool = count_ ? &pools_[poolIndex] : nullptr;
auto slot = lastPool->allocSlot();
if (!slot)
return {};
return {slot, SlotId(poolIndex * ARDUINOJSON_POOL_CAPACITY + slot.id())};
}
VariantPool* addPool(Allocator* allocator) {
if (count_ == capacity_ && !increaseCapacity(allocator))
return nullptr;
auto pool = &pools_[count_++];
pool->create(ARDUINOJSON_POOL_CAPACITY, allocator);
return pool;
}
bool increaseCapacity(Allocator* allocator) {
void* newPools;
size_t newCapacity;
if (pools_) {
newCapacity = capacity_ * 2;
newPools =
allocator->reallocate(pools_, newCapacity * sizeof(VariantPool));
} else {
newCapacity = ARDUINOJSON_INITIAL_POOL_COUNT;
newPools = allocator->allocate(newCapacity * sizeof(VariantPool));
}
if (!newPools)
return false;
pools_ = static_cast<VariantPool*>(newPools);
capacity_ = newCapacity;
return true;
}
VariantPool* pools_ = nullptr;
size_t count_ = 0;
size_t capacity_ = 0;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE