forked from bblanchon/ArduinoJson
Create more memory pools as needed (resolves #1074)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user