mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-07-17 12:32:17 +02:00
Preallocate pool list
This commit is contained in:
@ -32,38 +32,60 @@ struct FailingAllocator : ArduinoJson::Allocator {
|
|||||||
|
|
||||||
class AllocatorLog {
|
class AllocatorLog {
|
||||||
public:
|
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];
|
char buffer[32];
|
||||||
sprintf(buffer, "allocate(%zu)", s);
|
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];
|
char buffer[32];
|
||||||
sprintf(buffer, "allocate(%zu) -> nullptr", s);
|
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];
|
char buffer[32];
|
||||||
sprintf(buffer, "reallocate(%zu, %zu)", s1, s2);
|
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];
|
char buffer[32];
|
||||||
sprintf(buffer, "reallocate(%zu, %zu) -> nullptr", s1, s2);
|
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];
|
char buffer[32];
|
||||||
sprintf(buffer, "deallocate(%zu)", s);
|
sprintf(buffer, "deallocate(%zu)", s);
|
||||||
return buffer;
|
return Entry(buffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
AllocatorLog& operator<<(const std::string& s) {
|
AllocatorLog& operator<<(const Entry& entry) {
|
||||||
log_ << s << "\n";
|
for (size_t i = 0; i < entry.count(); i++)
|
||||||
|
log_ << entry.str() << "\n";
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,10 +39,7 @@ TEST_CASE("JsonArray::clear()") {
|
|||||||
for (int i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
|
for (int i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
|
||||||
array.add(i);
|
array.add(i);
|
||||||
|
|
||||||
REQUIRE(
|
REQUIRE(allocator.log() == AllocatorLog()
|
||||||
allocator.log() ==
|
<< AllocatorLog::Allocate(sizeofPool()));
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
|
||||||
<< AllocatorLog::Allocate(sizeofPool()) // only one pool
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,9 +105,7 @@ TEST_CASE("Removed elements are recycled") {
|
|||||||
// add one element; it should use the free slot
|
// add one element; it should use the free slot
|
||||||
array.add(42);
|
array.add(42);
|
||||||
|
|
||||||
REQUIRE(
|
REQUIRE(allocator.log() == AllocatorLog() << AllocatorLog::Allocate(
|
||||||
allocator.log() ==
|
sizeofPool()) // only one pool
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
|
||||||
<< AllocatorLog::Allocate(sizeofPool()) // only one pool
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -281,7 +281,7 @@ TEST_CASE("deserialize JSON array under memory constraints") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SECTION("allocation of pool fails") {
|
SECTION("allocation of pool fails") {
|
||||||
allocator.setCountdown(1);
|
allocator.setCountdown(0);
|
||||||
char input[] = "[1]";
|
char input[] = "[1]";
|
||||||
|
|
||||||
DeserializationError err = deserializeJson(doc, input);
|
DeserializationError err = deserializeJson(doc, input);
|
||||||
@ -291,7 +291,7 @@ TEST_CASE("deserialize JSON array under memory constraints") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SECTION("allocation of string fails in array") {
|
SECTION("allocation of string fails in array") {
|
||||||
allocator.setCountdown(2);
|
allocator.setCountdown(1);
|
||||||
char input[] = "[0,\"hi!\"]";
|
char input[] = "[0,\"hi!\"]";
|
||||||
|
|
||||||
DeserializationError err = deserializeJson(doc, input);
|
DeserializationError err = deserializeJson(doc, input);
|
||||||
|
@ -344,7 +344,7 @@ TEST_CASE("deserialize JSON object under memory constraints") {
|
|||||||
REQUIRE(doc.as<std::string>() == "{}");
|
REQUIRE(doc.as<std::string>() == "{}");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("pool list allocation fails") {
|
SECTION("pool allocation fails") {
|
||||||
allocator.setCountdown(2);
|
allocator.setCountdown(2);
|
||||||
char input[] = "{\"a\":1}";
|
char input[] = "{\"a\":1}";
|
||||||
|
|
||||||
@ -354,18 +354,8 @@ TEST_CASE("deserialize JSON object under memory constraints") {
|
|||||||
REQUIRE(doc.as<std::string>() == "{}");
|
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") {
|
SECTION("string allocation fails") {
|
||||||
allocator.setCountdown(4);
|
allocator.setCountdown(3);
|
||||||
char input[] = "{\"a\":\"b\"}";
|
char input[] = "{\"a\":\"b\"}";
|
||||||
|
|
||||||
DeserializationError err = deserializeJson(doc, input);
|
DeserializationError err = deserializeJson(doc, input);
|
||||||
|
@ -111,14 +111,13 @@ TEST_CASE("Allocation of the key fails") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Quoted string, second member") {
|
SECTION("Quoted string, second member") {
|
||||||
timebombAllocator.setCountdown(4);
|
timebombAllocator.setCountdown(3);
|
||||||
REQUIRE(deserializeJson(doc, "{\"hello\":1,\"world\"}") ==
|
REQUIRE(deserializeJson(doc, "{\"hello\":1,\"world\"}") ==
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofString(31))
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(31))
|
||||||
<< AllocatorLog::Reallocate(sizeofString(31),
|
<< AllocatorLog::Reallocate(sizeofString(31),
|
||||||
sizeofString(5))
|
sizeofString(5))
|
||||||
<< AllocatorLog::Allocate(sizeofPoolList())
|
|
||||||
<< AllocatorLog::Allocate(sizeofPool())
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::AllocateFail(sizeofString(31)));
|
<< AllocatorLog::AllocateFail(sizeofString(31)));
|
||||||
}
|
}
|
||||||
@ -131,14 +130,13 @@ TEST_CASE("Allocation of the key fails") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Non-Quoted string, second member") {
|
SECTION("Non-Quoted string, second member") {
|
||||||
timebombAllocator.setCountdown(4);
|
timebombAllocator.setCountdown(3);
|
||||||
REQUIRE(deserializeJson(doc, "{hello:1,world}") ==
|
REQUIRE(deserializeJson(doc, "{hello:1,world}") ==
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofString(31))
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(31))
|
||||||
<< AllocatorLog::Reallocate(sizeofString(31),
|
<< AllocatorLog::Reallocate(sizeofString(31),
|
||||||
sizeofString(5))
|
sizeofString(5))
|
||||||
<< AllocatorLog::Allocate(sizeofPoolList())
|
|
||||||
<< AllocatorLog::Allocate(sizeofPool())
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::AllocateFail(sizeofString(31)));
|
<< AllocatorLog::AllocateFail(sizeofString(31)));
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ TEST_CASE("JsonDocument assignment") {
|
|||||||
|
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofString(5)) // hello
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(5)) // hello
|
||||||
<< AllocatorLog::Allocate(sizeofPoolList())
|
|
||||||
<< AllocatorLog::Allocate(sizeofPool())
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::Allocate(sizeofString(5)) // world
|
<< AllocatorLog::Allocate(sizeofString(5)) // world
|
||||||
);
|
);
|
||||||
@ -40,8 +39,7 @@ TEST_CASE("JsonDocument assignment") {
|
|||||||
|
|
||||||
REQUIRE(doc2.as<std::string>() == "[{\"hello\":\"world\"}]");
|
REQUIRE(doc2.as<std::string>() == "[{\"hello\":\"world\"}]");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
AllocatorLog() << AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::Allocate(sizeofPool())
|
|
||||||
<< AllocatorLog::Allocate(sizeofString(5)) // hello
|
<< AllocatorLog::Allocate(sizeofString(5)) // hello
|
||||||
<< AllocatorLog::Allocate(sizeofString(5)) // world
|
<< AllocatorLog::Allocate(sizeofString(5)) // world
|
||||||
);
|
);
|
||||||
@ -58,7 +56,6 @@ TEST_CASE("JsonDocument assignment") {
|
|||||||
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
|
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofString(5)) // hello
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(5)) // hello
|
||||||
<< AllocatorLog::Allocate(sizeofPoolList())
|
|
||||||
<< AllocatorLog::Allocate(sizeofPool())
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::Allocate(sizeofString(5)) // world
|
<< AllocatorLog::Allocate(sizeofString(5)) // world
|
||||||
);
|
);
|
||||||
@ -78,13 +75,11 @@ TEST_CASE("JsonDocument assignment") {
|
|||||||
REQUIRE(
|
REQUIRE(
|
||||||
spyingAllocator.log() ==
|
spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofString(5)) // hello
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(5)) // hello
|
||||||
<< AllocatorLog::Allocate(sizeofPoolList())
|
|
||||||
<< AllocatorLog::Allocate(sizeofPool())
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::Allocate(sizeofString(5)) // world
|
<< AllocatorLog::Allocate(sizeofString(5)) // world
|
||||||
<< AllocatorLog::Deallocate(sizeofString(5)) // hello
|
<< AllocatorLog::Deallocate(sizeofString(5)) // hello
|
||||||
<< AllocatorLog::Deallocate(sizeofString(5)) // world
|
<< AllocatorLog::Deallocate(sizeofString(5)) // world
|
||||||
<< AllocatorLog::Deallocate(sizeofPool())
|
<< AllocatorLog::Deallocate(sizeofPool()));
|
||||||
<< AllocatorLog::Deallocate(sizeofPoolList()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Assign from JsonObject") {
|
SECTION("Assign from JsonObject") {
|
||||||
|
@ -59,8 +59,7 @@ TEST_CASE("JsonDocument constructor") {
|
|||||||
|
|
||||||
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
|
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
AllocatorLog() << AllocatorLog::Allocate(sizeofPool()));
|
||||||
<< AllocatorLog::Allocate(sizeofPool()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Construct from JsonArray") {
|
SECTION("Construct from JsonArray") {
|
||||||
@ -72,8 +71,7 @@ TEST_CASE("JsonDocument constructor") {
|
|||||||
|
|
||||||
REQUIRE(doc2.as<std::string>() == "[\"hello\"]");
|
REQUIRE(doc2.as<std::string>() == "[\"hello\"]");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
AllocatorLog() << AllocatorLog::Allocate(sizeofPool()));
|
||||||
<< AllocatorLog::Allocate(sizeofPool()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Construct from JsonVariant") {
|
SECTION("Construct from JsonVariant") {
|
||||||
|
@ -30,11 +30,9 @@ TEST_CASE("JsonDocument::garbageCollect()") {
|
|||||||
REQUIRE(doc.as<std::string>() == "{\"dancing\":2}");
|
REQUIRE(doc.as<std::string>() == "{\"dancing\":2}");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofString(7))
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(7))
|
||||||
<< AllocatorLog::Allocate(sizeofPoolList())
|
|
||||||
<< AllocatorLog::Allocate(sizeofPool())
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::Deallocate(sizeofString(7))
|
<< AllocatorLog::Deallocate(sizeofString(7))
|
||||||
<< AllocatorLog::Deallocate(sizeofPool())
|
<< AllocatorLog::Deallocate(sizeofPool()));
|
||||||
<< AllocatorLog::Deallocate(sizeofPoolList()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("when allocation fails") {
|
SECTION("when allocation fails") {
|
||||||
|
@ -47,13 +47,13 @@ TEST_CASE("JsonDocument::overflowed()") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SECTION("returns true after a failed deserialization") {
|
SECTION("returns true after a failed deserialization") {
|
||||||
allocator.setCountdown(1);
|
allocator.setCountdown(0);
|
||||||
deserializeJson(doc, "[1, 2]");
|
deserializeJson(doc, "[1, 2]");
|
||||||
CHECK(doc.overflowed() == true);
|
CHECK(doc.overflowed() == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("returns false after a successful deserialization") {
|
SECTION("returns false after a successful deserialization") {
|
||||||
allocator.setCountdown(4);
|
allocator.setCountdown(3);
|
||||||
deserializeJson(doc, "[\"example\"]");
|
deserializeJson(doc, "[\"example\"]");
|
||||||
CHECK(doc.overflowed() == false);
|
CHECK(doc.overflowed() == false);
|
||||||
}
|
}
|
||||||
|
@ -105,13 +105,10 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "{\"key\":42}");
|
REQUIRE(doc.as<std::string>() == "{\"key\":42}");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() == AllocatorLog()
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
|
||||||
<< AllocatorLog::Allocate(sizeofPool())
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::Reallocate(sizeofPool(),
|
<< AllocatorLog::Reallocate(
|
||||||
sizeofObject(1))
|
sizeofPool(), sizeofObject(1)));
|
||||||
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
|
||||||
sizeofPoolList(1)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("owned key") {
|
SECTION("owned key") {
|
||||||
@ -122,12 +119,9 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
REQUIRE(doc.as<std::string>() == "{\"abcdefg\":42}");
|
REQUIRE(doc.as<std::string>() == "{\"abcdefg\":42}");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofString(7))
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(7))
|
||||||
<< AllocatorLog::Allocate(sizeofPoolList())
|
|
||||||
<< AllocatorLog::Allocate(sizeofPool())
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::Reallocate(sizeofPool(),
|
<< AllocatorLog::Reallocate(sizeofPool(),
|
||||||
sizeofObject(1))
|
sizeofObject(1)));
|
||||||
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
|
||||||
sizeofPoolList(1)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("linked string in array") {
|
SECTION("linked string in array") {
|
||||||
@ -136,13 +130,10 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "[\"hello\"]");
|
REQUIRE(doc.as<std::string>() == "[\"hello\"]");
|
||||||
REQUIRE(
|
REQUIRE(spyingAllocator.log() == AllocatorLog()
|
||||||
spyingAllocator.log() ==
|
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
|
||||||
<< AllocatorLog::Allocate(sizeofPool())
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::Reallocate(sizeofPool(), sizeofArray(1))
|
<< AllocatorLog::Reallocate(
|
||||||
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
sizeofPool(), sizeofArray(1)));
|
||||||
sizeofPoolList(1)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("owned string in array") {
|
SECTION("owned string in array") {
|
||||||
@ -151,14 +142,11 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "[\"abcdefg\"]");
|
REQUIRE(doc.as<std::string>() == "[\"abcdefg\"]");
|
||||||
REQUIRE(
|
REQUIRE(spyingAllocator.log() ==
|
||||||
spyingAllocator.log() ==
|
AllocatorLog() << AllocatorLog::Allocate(sizeofPool())
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
|
||||||
<< AllocatorLog::Allocate(sizeofPool())
|
|
||||||
<< AllocatorLog::Allocate(sizeofString(7))
|
<< AllocatorLog::Allocate(sizeofString(7))
|
||||||
<< AllocatorLog::Reallocate(sizeofPool(), sizeofArray(1))
|
<< AllocatorLog::Reallocate(sizeofPool(),
|
||||||
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
sizeofArray(1)));
|
||||||
sizeofPoolList(1)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("linked string in object") {
|
SECTION("linked string in object") {
|
||||||
@ -167,13 +155,10 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "{\"key\":\"hello\"}");
|
REQUIRE(doc.as<std::string>() == "{\"key\":\"hello\"}");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() == AllocatorLog()
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
|
||||||
<< AllocatorLog::Allocate(sizeofPool())
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::Reallocate(sizeofPool(),
|
<< AllocatorLog::Reallocate(
|
||||||
sizeofObject(1))
|
sizeofPool(), sizeofObject(1)));
|
||||||
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
|
||||||
sizeofPoolList(1)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("owned string in object") {
|
SECTION("owned string in object") {
|
||||||
@ -182,13 +167,10 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "{\"key\":\"abcdefg\"}");
|
REQUIRE(doc.as<std::string>() == "{\"key\":\"abcdefg\"}");
|
||||||
REQUIRE(
|
REQUIRE(spyingAllocator.log() ==
|
||||||
spyingAllocator.log() ==
|
AllocatorLog() << AllocatorLog::Allocate(sizeofPool())
|
||||||
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
|
||||||
<< AllocatorLog::Allocate(sizeofPool())
|
|
||||||
<< AllocatorLog::Allocate(sizeofString(7))
|
<< AllocatorLog::Allocate(sizeofString(7))
|
||||||
<< AllocatorLog::Reallocate(sizeofPool(), sizeofPool(1))
|
<< AllocatorLog::Reallocate(sizeofPool(),
|
||||||
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
sizeofPool(1)));
|
||||||
sizeofPoolList(1)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ TEST_CASE("JsonObject::set()") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SECTION("copy fails in the middle of an object") {
|
SECTION("copy fails in the middle of an object") {
|
||||||
TimebombAllocator allocator(3);
|
TimebombAllocator allocator(2);
|
||||||
JsonDocument doc3(&allocator);
|
JsonDocument doc3(&allocator);
|
||||||
JsonObject obj3 = doc3.to<JsonObject>();
|
JsonObject obj3 = doc3.to<JsonObject>();
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ TEST_CASE("JsonObject::set()") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SECTION("copy fails in the middle of an array") {
|
SECTION("copy fails in the middle of an array") {
|
||||||
TimebombAllocator allocator(2);
|
TimebombAllocator allocator(1);
|
||||||
JsonDocument doc3(&allocator);
|
JsonDocument doc3(&allocator);
|
||||||
JsonObject obj3 = doc3.to<JsonObject>();
|
JsonObject obj3 = doc3.to<JsonObject>();
|
||||||
|
|
||||||
|
@ -178,23 +178,19 @@ TEST_CASE("deserializeMsgPack() under memory constaints") {
|
|||||||
checkError(0, "\x91\x01",
|
checkError(0, "\x91\x01",
|
||||||
DeserializationError::NoMemory); // [1]
|
DeserializationError::NoMemory); // [1]
|
||||||
checkError(1, "\x91\x01",
|
checkError(1, "\x91\x01",
|
||||||
DeserializationError::NoMemory); // [1]
|
|
||||||
checkError(2, "\x91\x01",
|
|
||||||
DeserializationError::Ok); // [1]
|
DeserializationError::Ok); // [1]
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("array 16") {
|
SECTION("array 16") {
|
||||||
checkError(0, "\xDC\x00\x00", DeserializationError::Ok);
|
checkError(0, "\xDC\x00\x00", DeserializationError::Ok);
|
||||||
checkError(0, "\xDC\x00\x01\x01", DeserializationError::NoMemory);
|
checkError(0, "\xDC\x00\x01\x01", DeserializationError::NoMemory);
|
||||||
checkError(1, "\xDC\x00\x01\x01", DeserializationError::NoMemory);
|
checkError(1, "\xDC\x00\x01\x01", DeserializationError::Ok);
|
||||||
checkError(2, "\xDC\x00\x01\x01", DeserializationError::Ok);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("array 32") {
|
SECTION("array 32") {
|
||||||
checkError(0, "\xDD\x00\x00\x00\x00", DeserializationError::Ok);
|
checkError(0, "\xDD\x00\x00\x00\x00", DeserializationError::Ok);
|
||||||
checkError(0, "\xDD\x00\x00\x00\x01\x01", DeserializationError::NoMemory);
|
checkError(0, "\xDD\x00\x00\x00\x01\x01", DeserializationError::NoMemory);
|
||||||
checkError(1, "\xDD\x00\x00\x00\x01\x01", DeserializationError::NoMemory);
|
checkError(1, "\xDD\x00\x00\x00\x01\x01", DeserializationError::Ok);
|
||||||
checkError(2, "\xDD\x00\x00\x00\x01\x01", DeserializationError::Ok);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("fixmap") {
|
SECTION("fixmap") {
|
||||||
@ -203,11 +199,11 @@ TEST_CASE("deserializeMsgPack() under memory constaints") {
|
|||||||
}
|
}
|
||||||
SECTION("{H:1}") {
|
SECTION("{H:1}") {
|
||||||
checkError(0, "\x81\xA1H\x01", DeserializationError::NoMemory);
|
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}") {
|
SECTION("{H:1,W:2}") {
|
||||||
checkError(4, "\x82\xA1H\x01\xA1W\x02", DeserializationError::NoMemory);
|
checkError(3, "\x82\xA1H\x01\xA1W\x02", DeserializationError::NoMemory);
|
||||||
checkError(6, "\x82\xA1H\x01\xA1W\x02", DeserializationError::Ok);
|
checkError(5, "\x82\xA1H\x01\xA1W\x02", DeserializationError::Ok);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,12 +213,12 @@ TEST_CASE("deserializeMsgPack() under memory constaints") {
|
|||||||
}
|
}
|
||||||
SECTION("{H:1}") {
|
SECTION("{H:1}") {
|
||||||
checkError(2, "\xDE\x00\x01\xA1H\x01", DeserializationError::NoMemory);
|
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}") {
|
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);
|
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}") {
|
SECTION("{H:1}") {
|
||||||
checkError(2, "\xDF\x00\x00\x00\x01\xA1H\x01",
|
checkError(2, "\xDF\x00\x00\x00\x01\xA1H\x01",
|
||||||
DeserializationError::NoMemory);
|
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}") {
|
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);
|
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);
|
DeserializationError::Ok);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ add_executable(ResourceManagerTests
|
|||||||
allocVariant.cpp
|
allocVariant.cpp
|
||||||
clear.cpp
|
clear.cpp
|
||||||
saveString.cpp
|
saveString.cpp
|
||||||
|
shrinkToFit.cpp
|
||||||
size.cpp
|
size.cpp
|
||||||
StringBuilder.cpp
|
StringBuilder.cpp
|
||||||
)
|
)
|
||||||
|
@ -57,8 +57,7 @@ TEST_CASE("ResourceManager::allocSlot()") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Returns null if pool allocation fails") {
|
SECTION("Returns null if pool allocation fails") {
|
||||||
TimebombAllocator allocator(1);
|
ResourceManager resources(FailingAllocator::instance());
|
||||||
ResourceManager resources(&allocator);
|
|
||||||
|
|
||||||
resources.allocSlot();
|
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") {
|
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++)
|
for (size_t i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
|
||||||
resources.allocSlot();
|
resources.allocSlot();
|
||||||
size_t size = resources.size();
|
size_t size = resources.size();
|
||||||
|
@ -105,7 +105,7 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Capacity of each variant pool (in slots)
|
// Initial capacity of the pool list
|
||||||
#ifndef ARDUINOJSON_INITIAL_POOL_COUNT
|
#ifndef ARDUINOJSON_INITIAL_POOL_COUNT
|
||||||
# define ARDUINOJSON_INITIAL_POOL_COUNT 4
|
# define ARDUINOJSON_INITIAL_POOL_COUNT 4
|
||||||
#endif
|
#endif
|
||||||
|
@ -16,15 +16,21 @@ class VariantPoolList {
|
|||||||
VariantPoolList() = default;
|
VariantPoolList() = default;
|
||||||
|
|
||||||
~VariantPoolList() {
|
~VariantPoolList() {
|
||||||
ARDUINOJSON_ASSERT(pools_ == nullptr);
|
ARDUINOJSON_ASSERT(count_ == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
VariantPoolList& operator=(VariantPoolList&& src) {
|
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_;
|
pools_ = src.pools_;
|
||||||
|
src.pools_ = nullptr;
|
||||||
|
}
|
||||||
count_ = src.count_;
|
count_ = src.count_;
|
||||||
capacity_ = src.capacity_;
|
capacity_ = src.capacity_;
|
||||||
src.pools_ = nullptr;
|
|
||||||
src.count_ = 0;
|
src.count_ = 0;
|
||||||
src.capacity_ = 0;
|
src.capacity_ = 0;
|
||||||
return *this;
|
return *this;
|
||||||
@ -37,7 +43,7 @@ class VariantPoolList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// try to allocate from last pool (other pools are full)
|
// try to allocate from last pool (other pools are full)
|
||||||
if (pools_) {
|
if (count_) {
|
||||||
auto slot = allocFromLastPool();
|
auto slot = allocFromLastPool();
|
||||||
if (slot)
|
if (slot)
|
||||||
return slot;
|
return slot;
|
||||||
@ -63,14 +69,14 @@ class VariantPoolList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void clear(Allocator* allocator) {
|
void clear(Allocator* allocator) {
|
||||||
if (!pools_)
|
|
||||||
return;
|
|
||||||
for (PoolCount i = 0; i < count_; i++)
|
for (PoolCount i = 0; i < count_; i++)
|
||||||
pools_[i].destroy(allocator);
|
pools_[i].destroy(allocator);
|
||||||
allocator->deallocate(pools_);
|
|
||||||
pools_ = nullptr;
|
|
||||||
count_ = 0;
|
count_ = 0;
|
||||||
capacity_ = 0;
|
if (pools_ != preallocatedPools_) {
|
||||||
|
allocator->deallocate(pools_);
|
||||||
|
pools_ = preallocatedPools_;
|
||||||
|
capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SlotCount usage() const {
|
SlotCount usage() const {
|
||||||
@ -81,9 +87,9 @@ class VariantPoolList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void shrinkToFit(Allocator* allocator) {
|
void shrinkToFit(Allocator* allocator) {
|
||||||
if (pools_)
|
if (count_ > 0)
|
||||||
pools_[count_ - 1].shrinkToFit(allocator);
|
pools_[count_ - 1].shrinkToFit(allocator);
|
||||||
if (count_ != capacity_) {
|
if (pools_ != preallocatedPools_ && count_ != capacity_) {
|
||||||
pools_ = static_cast<VariantPool*>(
|
pools_ = static_cast<VariantPool*>(
|
||||||
allocator->reallocate(pools_, count_ * sizeof(VariantPool)));
|
allocator->reallocate(pools_, count_ * sizeof(VariantPool)));
|
||||||
ARDUINOJSON_ASSERT(pools_ != nullptr); // realloc to smaller can't fail
|
ARDUINOJSON_ASSERT(pools_ != nullptr); // realloc to smaller can't fail
|
||||||
@ -95,10 +101,9 @@ class VariantPoolList {
|
|||||||
SlotWithId allocFromFreeList();
|
SlotWithId allocFromFreeList();
|
||||||
|
|
||||||
SlotWithId allocFromLastPool() {
|
SlotWithId allocFromLastPool() {
|
||||||
ARDUINOJSON_ASSERT(pools_ != nullptr);
|
ARDUINOJSON_ASSERT(count_ > 0);
|
||||||
auto poolIndex = SlotId(count_ - 1);
|
auto poolIndex = SlotId(count_ - 1);
|
||||||
auto lastPool = count_ ? &pools_[poolIndex] : nullptr;
|
auto slot = pools_[poolIndex].allocSlot();
|
||||||
auto slot = lastPool->allocSlot();
|
|
||||||
if (!slot)
|
if (!slot)
|
||||||
return {};
|
return {};
|
||||||
return {slot, SlotId(poolIndex * ARDUINOJSON_POOL_CAPACITY + slot.id())};
|
return {slot, SlotId(poolIndex * ARDUINOJSON_POOL_CAPACITY + slot.id())};
|
||||||
@ -116,28 +121,32 @@ class VariantPoolList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool increaseCapacity(Allocator* allocator) {
|
bool increaseCapacity(Allocator* allocator) {
|
||||||
if (count_ == maxPools)
|
if (capacity_ == maxPools)
|
||||||
return false;
|
return false;
|
||||||
void* newPools;
|
void* newPools;
|
||||||
PoolCount newCapacity;
|
auto newCapacity = PoolCount(capacity_ * 2);
|
||||||
if (pools_) {
|
|
||||||
newCapacity = PoolCount(capacity_ * 2);
|
if (pools_ == preallocatedPools_) {
|
||||||
newPools =
|
|
||||||
allocator->reallocate(pools_, newCapacity * sizeof(VariantPool));
|
|
||||||
} else {
|
|
||||||
newCapacity = ARDUINOJSON_INITIAL_POOL_COUNT;
|
|
||||||
newPools = allocator->allocate(newCapacity * sizeof(VariantPool));
|
newPools = allocator->allocate(newCapacity * sizeof(VariantPool));
|
||||||
}
|
|
||||||
if (!newPools)
|
if (!newPools)
|
||||||
return false;
|
return false;
|
||||||
|
memcpy(newPools, preallocatedPools_, sizeof(preallocatedPools_));
|
||||||
|
} else {
|
||||||
|
newPools =
|
||||||
|
allocator->reallocate(pools_, newCapacity * sizeof(VariantPool));
|
||||||
|
if (!newPools)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
pools_ = static_cast<VariantPool*>(newPools);
|
pools_ = static_cast<VariantPool*>(newPools);
|
||||||
capacity_ = newCapacity;
|
capacity_ = newCapacity;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
VariantPool* pools_ = nullptr;
|
VariantPool preallocatedPools_[ARDUINOJSON_INITIAL_POOL_COUNT];
|
||||||
|
VariantPool* pools_ = preallocatedPools_;
|
||||||
PoolCount count_ = 0;
|
PoolCount count_ = 0;
|
||||||
PoolCount capacity_ = 0;
|
PoolCount capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT;
|
||||||
SlotId freeList_ = NULL_SLOT;
|
SlotId freeList_ = NULL_SLOT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
Reference in New Issue
Block a user