mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-07-17 04:22:18 +02:00
Preallocate pool list
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)));
|
||||
}
|
||||
|
@ -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") {
|
||||
|
@ -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") {
|
||||
|
@ -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") {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -105,13 +105,10 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
||||
doc.shrinkToFit();
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":42}");
|
||||
REQUIRE(spyingAllocator.log() ==
|
||||
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
||||
REQUIRE(spyingAllocator.log() == AllocatorLog()
|
||||
<< AllocatorLog::Allocate(sizeofPool())
|
||||
<< AllocatorLog::Reallocate(sizeofPool(),
|
||||
sizeofObject(1))
|
||||
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
||||
sizeofPoolList(1)));
|
||||
<< 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())
|
||||
REQUIRE(spyingAllocator.log() == AllocatorLog()
|
||||
<< AllocatorLog::Allocate(sizeofPool())
|
||||
<< AllocatorLog::Reallocate(sizeofPool(), sizeofArray(1))
|
||||
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
||||
sizeofPoolList(1)));
|
||||
<< 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())
|
||||
REQUIRE(spyingAllocator.log() ==
|
||||
AllocatorLog() << AllocatorLog::Allocate(sizeofPool())
|
||||
<< AllocatorLog::Allocate(sizeofString(7))
|
||||
<< AllocatorLog::Reallocate(sizeofPool(), sizeofArray(1))
|
||||
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
||||
sizeofPoolList(1)));
|
||||
<< 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())
|
||||
REQUIRE(spyingAllocator.log() == AllocatorLog()
|
||||
<< AllocatorLog::Allocate(sizeofPool())
|
||||
<< AllocatorLog::Reallocate(sizeofPool(),
|
||||
sizeofObject(1))
|
||||
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
||||
sizeofPoolList(1)));
|
||||
<< 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())
|
||||
REQUIRE(spyingAllocator.log() ==
|
||||
AllocatorLog() << AllocatorLog::Allocate(sizeofPool())
|
||||
<< AllocatorLog::Allocate(sizeofString(7))
|
||||
<< AllocatorLog::Reallocate(sizeofPool(), sizeofPool(1))
|
||||
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
||||
sizeofPoolList(1)));
|
||||
<< AllocatorLog::Reallocate(sizeofPool(),
|
||||
sizeofPool(1)));
|
||||
}
|
||||
}
|
||||
|
@ -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>();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ add_executable(ResourceManagerTests
|
||||
allocVariant.cpp
|
||||
clear.cpp
|
||||
saveString.cpp
|
||||
shrinkToFit.cpp
|
||||
size.cpp
|
||||
StringBuilder.cpp
|
||||
)
|
||||
|
@ -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();
|
||||
|
||||
|
57
extras/tests/ResourceManager/shrinkToFit.cpp
Normal file
57
extras/tests/ResourceManager/shrinkToFit.cpp
Normal 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)));
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
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);
|
||||
newPools =
|
||||
allocator->reallocate(pools_, newCapacity * sizeof(VariantPool));
|
||||
} else {
|
||||
newCapacity = ARDUINOJSON_INITIAL_POOL_COUNT;
|
||||
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));
|
||||
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:
|
||||
|
Reference in New Issue
Block a user