Preallocate pool list

This commit is contained in:
Benoit Blanchon
2023-07-21 10:38:35 +02:00
parent f427706e06
commit 1a14499612
19 changed files with 183 additions and 143 deletions

View File

@ -32,38 +32,60 @@ struct FailingAllocator : ArduinoJson::Allocator {
class AllocatorLog {
public:
static std::string Allocate(size_t s) {
class Entry {
public:
Entry(std::string s, size_t n = 1) : str_(s), count_(n) {}
const std::string& str() const {
return str_;
}
size_t count() const {
return count_;
}
Entry operator*(size_t n) const {
return Entry(str_, n);
}
private:
std::string str_;
size_t count_;
};
static Entry Allocate(size_t s) {
char buffer[32];
sprintf(buffer, "allocate(%zu)", s);
return buffer;
return Entry(buffer);
}
static std::string AllocateFail(size_t s) {
static Entry AllocateFail(size_t s) {
char buffer[32];
sprintf(buffer, "allocate(%zu) -> nullptr", s);
return buffer;
return Entry(buffer);
}
static std::string Reallocate(size_t s1, size_t s2) {
static Entry Reallocate(size_t s1, size_t s2) {
char buffer[32];
sprintf(buffer, "reallocate(%zu, %zu)", s1, s2);
return buffer;
return Entry(buffer);
};
static std::string ReallocateFail(size_t s1, size_t s2) {
static Entry ReallocateFail(size_t s1, size_t s2) {
char buffer[32];
sprintf(buffer, "reallocate(%zu, %zu) -> nullptr", s1, s2);
return buffer;
return Entry(buffer);
};
static std::string Deallocate(size_t s) {
static Entry Deallocate(size_t s) {
char buffer[32];
sprintf(buffer, "deallocate(%zu)", s);
return buffer;
return Entry(buffer);
};
AllocatorLog& operator<<(const std::string& s) {
log_ << s << "\n";
AllocatorLog& operator<<(const Entry& entry) {
for (size_t i = 0; i < entry.count(); i++)
log_ << entry.str() << "\n";
return *this;
}

View File

@ -39,10 +39,7 @@ TEST_CASE("JsonArray::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
);
REQUIRE(allocator.log() == AllocatorLog()
<< AllocatorLog::Allocate(sizeofPool()));
}
}

View File

@ -105,9 +105,7 @@ TEST_CASE("Removed elements are recycled") {
// 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
REQUIRE(allocator.log() == AllocatorLog() << AllocatorLog::Allocate(
sizeofPool()) // only one pool
);
}

View File

@ -281,7 +281,7 @@ TEST_CASE("deserialize JSON array under memory constraints") {
}
SECTION("allocation of pool fails") {
allocator.setCountdown(1);
allocator.setCountdown(0);
char input[] = "[1]";
DeserializationError err = deserializeJson(doc, input);
@ -291,7 +291,7 @@ TEST_CASE("deserialize JSON array under memory constraints") {
}
SECTION("allocation of string fails in array") {
allocator.setCountdown(2);
allocator.setCountdown(1);
char input[] = "[0,\"hi!\"]";
DeserializationError err = deserializeJson(doc, input);

View File

@ -344,7 +344,7 @@ TEST_CASE("deserialize JSON object under memory constraints") {
REQUIRE(doc.as<std::string>() == "{}");
}
SECTION("pool list allocation fails") {
SECTION("pool allocation fails") {
allocator.setCountdown(2);
char input[] = "{\"a\":1}";
@ -354,18 +354,8 @@ TEST_CASE("deserialize JSON object under memory constraints") {
REQUIRE(doc.as<std::string>() == "{}");
}
SECTION("pool allocation fails") {
allocator.setCountdown(3);
char input[] = "{\"a\":1}";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
REQUIRE(doc.as<std::string>() == "{}");
}
SECTION("string allocation fails") {
allocator.setCountdown(4);
allocator.setCountdown(3);
char input[] = "{\"a\":\"b\"}";
DeserializationError err = deserializeJson(doc, input);

View File

@ -111,14 +111,13 @@ TEST_CASE("Allocation of the key fails") {
}
SECTION("Quoted string, second member") {
timebombAllocator.setCountdown(4);
timebombAllocator.setCountdown(3);
REQUIRE(deserializeJson(doc, "{\"hello\":1,\"world\"}") ==
DeserializationError::NoMemory);
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofString(31))
<< AllocatorLog::Reallocate(sizeofString(31),
sizeofString(5))
<< AllocatorLog::Allocate(sizeofPoolList())
<< AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::AllocateFail(sizeofString(31)));
}
@ -131,14 +130,13 @@ TEST_CASE("Allocation of the key fails") {
}
SECTION("Non-Quoted string, second member") {
timebombAllocator.setCountdown(4);
timebombAllocator.setCountdown(3);
REQUIRE(deserializeJson(doc, "{hello:1,world}") ==
DeserializationError::NoMemory);
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofString(31))
<< AllocatorLog::Reallocate(sizeofString(31),
sizeofString(5))
<< AllocatorLog::Allocate(sizeofPoolList())
<< AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::AllocateFail(sizeofString(31)));
}

View File

@ -24,7 +24,6 @@ TEST_CASE("JsonDocument assignment") {
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofString(5)) // hello
<< AllocatorLog::Allocate(sizeofPoolList())
<< AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Allocate(sizeofString(5)) // world
);
@ -40,8 +39,7 @@ TEST_CASE("JsonDocument assignment") {
REQUIRE(doc2.as<std::string>() == "[{\"hello\":\"world\"}]");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
<< AllocatorLog::Allocate(sizeofPool())
AllocatorLog() << AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Allocate(sizeofString(5)) // hello
<< AllocatorLog::Allocate(sizeofString(5)) // world
);
@ -58,7 +56,6 @@ TEST_CASE("JsonDocument assignment") {
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofString(5)) // hello
<< AllocatorLog::Allocate(sizeofPoolList())
<< AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Allocate(sizeofString(5)) // world
);
@ -78,13 +75,11 @@ TEST_CASE("JsonDocument assignment") {
REQUIRE(
spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofString(5)) // hello
<< AllocatorLog::Allocate(sizeofPoolList())
<< AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Allocate(sizeofString(5)) // world
<< AllocatorLog::Deallocate(sizeofString(5)) // hello
<< AllocatorLog::Deallocate(sizeofString(5)) // world
<< AllocatorLog::Deallocate(sizeofPool())
<< AllocatorLog::Deallocate(sizeofPoolList()));
<< AllocatorLog::Deallocate(sizeofPool()));
}
SECTION("Assign from JsonObject") {

View File

@ -59,8 +59,7 @@ TEST_CASE("JsonDocument constructor") {
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
<< AllocatorLog::Allocate(sizeofPool()));
AllocatorLog() << AllocatorLog::Allocate(sizeofPool()));
}
SECTION("Construct from JsonArray") {
@ -72,8 +71,7 @@ TEST_CASE("JsonDocument constructor") {
REQUIRE(doc2.as<std::string>() == "[\"hello\"]");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
<< AllocatorLog::Allocate(sizeofPool()));
AllocatorLog() << AllocatorLog::Allocate(sizeofPool()));
}
SECTION("Construct from JsonVariant") {

View File

@ -30,11 +30,9 @@ TEST_CASE("JsonDocument::garbageCollect()") {
REQUIRE(doc.as<std::string>() == "{\"dancing\":2}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofString(7))
<< AllocatorLog::Allocate(sizeofPoolList())
<< AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Deallocate(sizeofString(7))
<< AllocatorLog::Deallocate(sizeofPool())
<< AllocatorLog::Deallocate(sizeofPoolList()));
<< AllocatorLog::Deallocate(sizeofPool()));
}
SECTION("when allocation fails") {

View File

@ -47,13 +47,13 @@ TEST_CASE("JsonDocument::overflowed()") {
}
SECTION("returns true after a failed deserialization") {
allocator.setCountdown(1);
allocator.setCountdown(0);
deserializeJson(doc, "[1, 2]");
CHECK(doc.overflowed() == true);
}
SECTION("returns false after a successful deserialization") {
allocator.setCountdown(4);
allocator.setCountdown(3);
deserializeJson(doc, "[\"example\"]");
CHECK(doc.overflowed() == false);
}

View File

@ -105,13 +105,10 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "{\"key\":42}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
<< AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Reallocate(sizeofPool(),
sizeofObject(1))
<< AllocatorLog::Reallocate(sizeofPoolList(),
sizeofPoolList(1)));
REQUIRE(spyingAllocator.log() == AllocatorLog()
<< AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Reallocate(
sizeofPool(), sizeofObject(1)));
}
SECTION("owned key") {
@ -122,12 +119,9 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
REQUIRE(doc.as<std::string>() == "{\"abcdefg\":42}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofString(7))
<< AllocatorLog::Allocate(sizeofPoolList())
<< AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Reallocate(sizeofPool(),
sizeofObject(1))
<< AllocatorLog::Reallocate(sizeofPoolList(),
sizeofPoolList(1)));
sizeofObject(1)));
}
SECTION("linked string in array") {
@ -136,13 +130,10 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "[\"hello\"]");
REQUIRE(
spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
<< AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Reallocate(sizeofPool(), sizeofArray(1))
<< AllocatorLog::Reallocate(sizeofPoolList(),
sizeofPoolList(1)));
REQUIRE(spyingAllocator.log() == AllocatorLog()
<< AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Reallocate(
sizeofPool(), sizeofArray(1)));
}
SECTION("owned string in array") {
@ -151,14 +142,11 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "[\"abcdefg\"]");
REQUIRE(
spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
<< AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Allocate(sizeofString(7))
<< AllocatorLog::Reallocate(sizeofPool(), sizeofArray(1))
<< AllocatorLog::Reallocate(sizeofPoolList(),
sizeofPoolList(1)));
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Allocate(sizeofString(7))
<< AllocatorLog::Reallocate(sizeofPool(),
sizeofArray(1)));
}
SECTION("linked string in object") {
@ -167,13 +155,10 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "{\"key\":\"hello\"}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
<< AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Reallocate(sizeofPool(),
sizeofObject(1))
<< AllocatorLog::Reallocate(sizeofPoolList(),
sizeofPoolList(1)));
REQUIRE(spyingAllocator.log() == AllocatorLog()
<< AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Reallocate(
sizeofPool(), sizeofObject(1)));
}
SECTION("owned string in object") {
@ -182,13 +167,10 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "{\"key\":\"abcdefg\"}");
REQUIRE(
spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
<< AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Allocate(sizeofString(7))
<< AllocatorLog::Reallocate(sizeofPool(), sizeofPool(1))
<< AllocatorLog::Reallocate(sizeofPoolList(),
sizeofPoolList(1)));
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Allocate(sizeofString(7))
<< AllocatorLog::Reallocate(sizeofPool(),
sizeofPool(1)));
}
}

View File

@ -74,7 +74,7 @@ TEST_CASE("JsonObject::set()") {
}
SECTION("copy fails in the middle of an object") {
TimebombAllocator allocator(3);
TimebombAllocator allocator(2);
JsonDocument doc3(&allocator);
JsonObject obj3 = doc3.to<JsonObject>();
@ -88,7 +88,7 @@ TEST_CASE("JsonObject::set()") {
}
SECTION("copy fails in the middle of an array") {
TimebombAllocator allocator(2);
TimebombAllocator allocator(1);
JsonDocument doc3(&allocator);
JsonObject obj3 = doc3.to<JsonObject>();

View File

@ -178,23 +178,19 @@ TEST_CASE("deserializeMsgPack() under memory constaints") {
checkError(0, "\x91\x01",
DeserializationError::NoMemory); // [1]
checkError(1, "\x91\x01",
DeserializationError::NoMemory); // [1]
checkError(2, "\x91\x01",
DeserializationError::Ok); // [1]
}
SECTION("array 16") {
checkError(0, "\xDC\x00\x00", DeserializationError::Ok);
checkError(0, "\xDC\x00\x01\x01", DeserializationError::NoMemory);
checkError(1, "\xDC\x00\x01\x01", DeserializationError::NoMemory);
checkError(2, "\xDC\x00\x01\x01", DeserializationError::Ok);
checkError(1, "\xDC\x00\x01\x01", DeserializationError::Ok);
}
SECTION("array 32") {
checkError(0, "\xDD\x00\x00\x00\x00", DeserializationError::Ok);
checkError(0, "\xDD\x00\x00\x00\x01\x01", DeserializationError::NoMemory);
checkError(1, "\xDD\x00\x00\x00\x01\x01", DeserializationError::NoMemory);
checkError(2, "\xDD\x00\x00\x00\x01\x01", DeserializationError::Ok);
checkError(1, "\xDD\x00\x00\x00\x01\x01", DeserializationError::Ok);
}
SECTION("fixmap") {
@ -203,11 +199,11 @@ TEST_CASE("deserializeMsgPack() under memory constaints") {
}
SECTION("{H:1}") {
checkError(0, "\x81\xA1H\x01", DeserializationError::NoMemory);
checkError(4, "\x81\xA1H\x01", DeserializationError::Ok);
checkError(3, "\x81\xA1H\x01", DeserializationError::Ok);
}
SECTION("{H:1,W:2}") {
checkError(4, "\x82\xA1H\x01\xA1W\x02", DeserializationError::NoMemory);
checkError(6, "\x82\xA1H\x01\xA1W\x02", DeserializationError::Ok);
checkError(3, "\x82\xA1H\x01\xA1W\x02", DeserializationError::NoMemory);
checkError(5, "\x82\xA1H\x01\xA1W\x02", DeserializationError::Ok);
}
}
@ -217,12 +213,12 @@ TEST_CASE("deserializeMsgPack() under memory constaints") {
}
SECTION("{H:1}") {
checkError(2, "\xDE\x00\x01\xA1H\x01", DeserializationError::NoMemory);
checkError(4, "\xDE\x00\x01\xA1H\x01", DeserializationError::Ok);
checkError(3, "\xDE\x00\x01\xA1H\x01", DeserializationError::Ok);
}
SECTION("{H:1,W:2}") {
checkError(4, "\xDE\x00\x02\xA1H\x01\xA1W\x02",
checkError(3, "\xDE\x00\x02\xA1H\x01\xA1W\x02",
DeserializationError::NoMemory);
checkError(6, "\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok);
checkError(5, "\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok);
}
}
@ -233,12 +229,12 @@ TEST_CASE("deserializeMsgPack() under memory constaints") {
SECTION("{H:1}") {
checkError(2, "\xDF\x00\x00\x00\x01\xA1H\x01",
DeserializationError::NoMemory);
checkError(4, "\xDF\x00\x00\x00\x01\xA1H\x01", DeserializationError::Ok);
checkError(3, "\xDF\x00\x00\x00\x01\xA1H\x01", DeserializationError::Ok);
}
SECTION("{H:1,W:2}") {
checkError(4, "\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02",
checkError(3, "\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02",
DeserializationError::NoMemory);
checkError(6, "\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02",
checkError(5, "\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02",
DeserializationError::Ok);
}
}

View File

@ -6,6 +6,7 @@ add_executable(ResourceManagerTests
allocVariant.cpp
clear.cpp
saveString.cpp
shrinkToFit.cpp
size.cpp
StringBuilder.cpp
)

View File

@ -57,8 +57,7 @@ TEST_CASE("ResourceManager::allocSlot()") {
}
SECTION("Returns null if pool allocation fails") {
TimebombAllocator allocator(1);
ResourceManager resources(&allocator);
ResourceManager resources(FailingAllocator::instance());
resources.allocSlot();

View File

@ -0,0 +1,57 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
#include <catch.hpp>
#include "Allocators.hpp"
using namespace ArduinoJson::detail;
TEST_CASE("ResourceManager::shrinkToFit()") {
TimebombAllocator allocator(100);
SpyingAllocator spyingAllocator(&allocator);
ResourceManager resources(&spyingAllocator);
SECTION("empty") {
resources.shrinkToFit();
REQUIRE(spyingAllocator.log() == AllocatorLog());
}
SECTION("only one pool") {
resources.allocSlot();
resources.shrinkToFit();
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofPool())
<< AllocatorLog::Reallocate(sizeofPool(),
sizeof(VariantSlot)));
}
SECTION("more pools than initial count") {
for (size_t i = 0;
i < ARDUINOJSON_POOL_CAPACITY * ARDUINOJSON_INITIAL_POOL_COUNT + 1;
i++)
resources.allocSlot();
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofPool()) *
ARDUINOJSON_INITIAL_POOL_COUNT
<< AllocatorLog::Allocate(sizeofPoolList(
ARDUINOJSON_INITIAL_POOL_COUNT * 2))
<< AllocatorLog::Allocate(sizeofPool()));
spyingAllocator.clearLog();
resources.shrinkToFit();
REQUIRE(spyingAllocator.log() ==
AllocatorLog()
<< AllocatorLog::Reallocate(sizeofPool(), sizeof(VariantSlot))
<< AllocatorLog::Reallocate(
sizeofPoolList(ARDUINOJSON_INITIAL_POOL_COUNT * 2),
sizeofPoolList(ARDUINOJSON_INITIAL_POOL_COUNT + 1)));
}
}

View File

@ -19,7 +19,7 @@ TEST_CASE("ResourceManager::size()") {
}
SECTION("Doesn't grow when allocation of second pool fails") {
allocator.setCountdown(2);
allocator.setCountdown(1);
for (size_t i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
resources.allocSlot();
size_t size = resources.size();

View File

@ -105,7 +105,7 @@
# endif
#endif
// Capacity of each variant pool (in slots)
// Initial capacity of the pool list
#ifndef ARDUINOJSON_INITIAL_POOL_COUNT
# define ARDUINOJSON_INITIAL_POOL_COUNT 4
#endif

View File

@ -16,15 +16,21 @@ class VariantPoolList {
VariantPoolList() = default;
~VariantPoolList() {
ARDUINOJSON_ASSERT(pools_ == nullptr);
ARDUINOJSON_ASSERT(count_ == 0);
}
VariantPoolList& operator=(VariantPoolList&& src) {
ARDUINOJSON_ASSERT(pools_ == nullptr);
pools_ = src.pools_;
ARDUINOJSON_ASSERT(count_ == 0);
if (src.pools_ == src.preallocatedPools_) {
memcpy(preallocatedPools_, src.preallocatedPools_,
sizeof(preallocatedPools_));
pools_ = preallocatedPools_;
} else {
pools_ = src.pools_;
src.pools_ = nullptr;
}
count_ = src.count_;
capacity_ = src.capacity_;
src.pools_ = nullptr;
src.count_ = 0;
src.capacity_ = 0;
return *this;
@ -37,7 +43,7 @@ class VariantPoolList {
}
// try to allocate from last pool (other pools are full)
if (pools_) {
if (count_) {
auto slot = allocFromLastPool();
if (slot)
return slot;
@ -63,14 +69,14 @@ class VariantPoolList {
}
void clear(Allocator* allocator) {
if (!pools_)
return;
for (PoolCount i = 0; i < count_; i++)
pools_[i].destroy(allocator);
allocator->deallocate(pools_);
pools_ = nullptr;
count_ = 0;
capacity_ = 0;
if (pools_ != preallocatedPools_) {
allocator->deallocate(pools_);
pools_ = preallocatedPools_;
capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT;
}
}
SlotCount usage() const {
@ -81,9 +87,9 @@ class VariantPoolList {
}
void shrinkToFit(Allocator* allocator) {
if (pools_)
if (count_ > 0)
pools_[count_ - 1].shrinkToFit(allocator);
if (count_ != capacity_) {
if (pools_ != preallocatedPools_ && count_ != capacity_) {
pools_ = static_cast<VariantPool*>(
allocator->reallocate(pools_, count_ * sizeof(VariantPool)));
ARDUINOJSON_ASSERT(pools_ != nullptr); // realloc to smaller can't fail
@ -95,10 +101,9 @@ class VariantPoolList {
SlotWithId allocFromFreeList();
SlotWithId allocFromLastPool() {
ARDUINOJSON_ASSERT(pools_ != nullptr);
ARDUINOJSON_ASSERT(count_ > 0);
auto poolIndex = SlotId(count_ - 1);
auto lastPool = count_ ? &pools_[poolIndex] : nullptr;
auto slot = lastPool->allocSlot();
auto slot = pools_[poolIndex].allocSlot();
if (!slot)
return {};
return {slot, SlotId(poolIndex * ARDUINOJSON_POOL_CAPACITY + slot.id())};
@ -116,28 +121,32 @@ class VariantPoolList {
}
bool increaseCapacity(Allocator* allocator) {
if (count_ == maxPools)
if (capacity_ == maxPools)
return false;
void* newPools;
PoolCount newCapacity;
if (pools_) {
newCapacity = PoolCount(capacity_ * 2);
auto newCapacity = PoolCount(capacity_ * 2);
if (pools_ == preallocatedPools_) {
newPools = allocator->allocate(newCapacity * sizeof(VariantPool));
if (!newPools)
return false;
memcpy(newPools, preallocatedPools_, sizeof(preallocatedPools_));
} else {
newPools =
allocator->reallocate(pools_, newCapacity * sizeof(VariantPool));
} else {
newCapacity = ARDUINOJSON_INITIAL_POOL_COUNT;
newPools = allocator->allocate(newCapacity * sizeof(VariantPool));
if (!newPools)
return false;
}
if (!newPools)
return false;
pools_ = static_cast<VariantPool*>(newPools);
capacity_ = newCapacity;
return true;
}
VariantPool* pools_ = nullptr;
VariantPool preallocatedPools_[ARDUINOJSON_INITIAL_POOL_COUNT];
VariantPool* pools_ = preallocatedPools_;
PoolCount count_ = 0;
PoolCount capacity_ = 0;
PoolCount capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT;
SlotId freeList_ = NULL_SLOT;
public: