mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-07-16 12:02:14 +02:00
Recycle removed slots
This commit is contained in:
@ -20,3 +20,4 @@ HEAD
|
|||||||
* Include `ARDUINOJSON_SLOT_OFFSET_SIZE` in the namespace name
|
* Include `ARDUINOJSON_SLOT_OFFSET_SIZE` in the namespace name
|
||||||
* Remove `JsonVariant::shallowCopy()`
|
* Remove `JsonVariant::shallowCopy()`
|
||||||
* `JsonDocument`'s capacity grows as needed, no need to pass it to the constructor anymore
|
* `JsonDocument`'s capacity grows as needed, no need to pass it to the constructor anymore
|
||||||
|
* `JsonDocument`'s allocator is not monotonic anymore, removed values get recycled
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
#include "Allocators.hpp"
|
||||||
|
|
||||||
TEST_CASE("JsonArray::clear()") {
|
TEST_CASE("JsonArray::clear()") {
|
||||||
SECTION("No-op on null JsonArray") {
|
SECTION("No-op on null JsonArray") {
|
||||||
JsonArray array;
|
JsonArray array;
|
||||||
@ -22,4 +24,25 @@ TEST_CASE("JsonArray::clear()") {
|
|||||||
REQUIRE(array.size() == 0);
|
REQUIRE(array.size() == 0);
|
||||||
REQUIRE(array.isNull() == false);
|
REQUIRE(array.isNull() == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("Removed elements are recycled") {
|
||||||
|
SpyingAllocator allocator;
|
||||||
|
JsonDocument doc(&allocator);
|
||||||
|
JsonArray array = doc.to<JsonArray>();
|
||||||
|
|
||||||
|
// fill the pool entirely
|
||||||
|
for (int i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
|
||||||
|
array.add(i);
|
||||||
|
|
||||||
|
// clear and fill again
|
||||||
|
array.clear();
|
||||||
|
for (int i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
|
||||||
|
array.add(i);
|
||||||
|
|
||||||
|
REQUIRE(
|
||||||
|
allocator.log() ==
|
||||||
|
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
||||||
|
<< AllocatorLog::Allocate(sizeofPool()) // only one pool
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
#include "Allocators.hpp"
|
||||||
|
|
||||||
TEST_CASE("JsonArray::remove()") {
|
TEST_CASE("JsonArray::remove()") {
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
JsonArray array = doc.to<JsonArray>();
|
JsonArray array = doc.to<JsonArray>();
|
||||||
@ -87,3 +89,25 @@ TEST_CASE("JsonArray::remove()") {
|
|||||||
unboundArray.remove(unboundArray.begin());
|
unboundArray.remove(unboundArray.begin());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Removed elements are recycled") {
|
||||||
|
SpyingAllocator allocator;
|
||||||
|
JsonDocument doc(&allocator);
|
||||||
|
JsonArray array = doc.to<JsonArray>();
|
||||||
|
|
||||||
|
// fill the pool entirely
|
||||||
|
for (int i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
|
||||||
|
array.add(i);
|
||||||
|
|
||||||
|
// free one slot in the pool
|
||||||
|
array.remove(0);
|
||||||
|
|
||||||
|
// add one element; it should use the free slot
|
||||||
|
array.add(42);
|
||||||
|
|
||||||
|
REQUIRE(
|
||||||
|
allocator.log() ==
|
||||||
|
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
||||||
|
<< AllocatorLog::Allocate(sizeofPool()) // only one pool
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -23,6 +23,24 @@ TEST_CASE("ResourceManager::allocSlot()") {
|
|||||||
REQUIRE(s1 != s2);
|
REQUIRE(s1 != s2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("Returns the same slot after calling freeSlot()") {
|
||||||
|
ResourceManager resources;
|
||||||
|
|
||||||
|
auto s1 = resources.allocSlot();
|
||||||
|
auto s2 = resources.allocSlot();
|
||||||
|
resources.freeSlot(s1);
|
||||||
|
resources.freeSlot(s2);
|
||||||
|
auto s3 = resources.allocSlot();
|
||||||
|
auto s4 = resources.allocSlot();
|
||||||
|
auto s5 = resources.allocSlot();
|
||||||
|
|
||||||
|
REQUIRE(s2.id() != s1.id());
|
||||||
|
REQUIRE(s3.id() == s2.id());
|
||||||
|
REQUIRE(s4.id() == s1.id());
|
||||||
|
REQUIRE(s5.id() != s1.id());
|
||||||
|
REQUIRE(s5.id() != s2.id());
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("Returns aligned pointers") {
|
SECTION("Returns aligned pointers") {
|
||||||
ResourceManager resources;
|
ResourceManager resources;
|
||||||
|
|
||||||
|
@ -133,6 +133,7 @@ inline void CollectionData::releaseSlot(iterator it,
|
|||||||
if (it.ownsKey())
|
if (it.ownsKey())
|
||||||
resources->dereferenceString(it.key());
|
resources->dereferenceString(it.key());
|
||||||
it->setNull(resources);
|
it->setNull(resources);
|
||||||
|
resources->freeSlot(SlotWithId(it.slot_, it.currentId_));
|
||||||
}
|
}
|
||||||
|
|
||||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||||
|
@ -59,6 +59,10 @@ class ResourceManager {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void freeSlot(SlotWithId id) {
|
||||||
|
variantPools_.freeSlot(id);
|
||||||
|
}
|
||||||
|
|
||||||
VariantSlot* getSlot(SlotId id) const {
|
VariantSlot* getSlot(SlotId id) const {
|
||||||
return variantPools_.getSlot(id);
|
return variantPools_.getSlot(id);
|
||||||
}
|
}
|
||||||
|
@ -61,4 +61,17 @@ inline size_t VariantPool::slotsToBytes(SlotCount n) {
|
|||||||
return n * sizeof(VariantSlot);
|
return n * sizeof(VariantSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline SlotWithId VariantPoolList::allocFromFreeList() {
|
||||||
|
ARDUINOJSON_ASSERT(freeList_ != NULL_SLOT);
|
||||||
|
auto id = freeList_;
|
||||||
|
auto slot = getSlot(freeList_);
|
||||||
|
freeList_ = slot->next();
|
||||||
|
return {new (slot) VariantSlot, id};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VariantPoolList::freeSlot(SlotWithId slot) {
|
||||||
|
slot->setNext(freeList_);
|
||||||
|
freeList_ = slot.id();
|
||||||
|
}
|
||||||
|
|
||||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||||
|
@ -29,6 +29,11 @@ class VariantPoolList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SlotWithId allocSlot(Allocator* allocator) {
|
SlotWithId allocSlot(Allocator* allocator) {
|
||||||
|
// try to allocate from free list
|
||||||
|
if (freeList_ != NULL_SLOT) {
|
||||||
|
return allocFromFreeList();
|
||||||
|
}
|
||||||
|
|
||||||
// try to allocate from last pool (other pools are full)
|
// try to allocate from last pool (other pools are full)
|
||||||
if (pools_) {
|
if (pools_) {
|
||||||
auto slot = allocFromLastPool();
|
auto slot = allocFromLastPool();
|
||||||
@ -44,6 +49,8 @@ class VariantPoolList {
|
|||||||
return allocFromLastPool();
|
return allocFromLastPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void freeSlot(SlotWithId slot);
|
||||||
|
|
||||||
VariantSlot* getSlot(SlotId id) const {
|
VariantSlot* getSlot(SlotId id) const {
|
||||||
auto poolIndex = SlotId(id / ARDUINOJSON_POOL_CAPACITY);
|
auto poolIndex = SlotId(id / ARDUINOJSON_POOL_CAPACITY);
|
||||||
auto indexInPool = SlotId(id % ARDUINOJSON_POOL_CAPACITY);
|
auto indexInPool = SlotId(id % ARDUINOJSON_POOL_CAPACITY);
|
||||||
@ -82,6 +89,8 @@ class VariantPoolList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
SlotWithId allocFromFreeList();
|
||||||
|
|
||||||
SlotWithId allocFromLastPool() {
|
SlotWithId allocFromLastPool() {
|
||||||
ARDUINOJSON_ASSERT(pools_ != nullptr);
|
ARDUINOJSON_ASSERT(pools_ != nullptr);
|
||||||
auto poolIndex = SlotId(count_ - 1);
|
auto poolIndex = SlotId(count_ - 1);
|
||||||
@ -121,6 +130,7 @@ class VariantPoolList {
|
|||||||
VariantPool* pools_ = nullptr;
|
VariantPool* pools_ = nullptr;
|
||||||
size_t count_ = 0;
|
size_t count_ = 0;
|
||||||
size_t capacity_ = 0;
|
size_t capacity_ = 0;
|
||||||
|
SlotId freeList_ = NULL_SLOT;
|
||||||
};
|
};
|
||||||
|
|
||||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||||
|
Reference in New Issue
Block a user