Store the strings in the heap

This commit is contained in:
Benoit Blanchon
2023-04-11 10:03:47 +02:00
parent 7c0fa7c276
commit d8f3058efa
27 changed files with 434 additions and 377 deletions

View File

@ -9,63 +9,59 @@
using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject;
using ArduinoJson::detail::sizeofString;
TEST_CASE("JsonDocument assignment") {
SpyingAllocator spyingAllocator;
SECTION("Copy assignment same capacity") {
{
JsonDocument doc1(1024, &spyingAllocator);
deserializeJson(doc1, "{\"hello\":\"world\"}");
JsonDocument doc2(1024, &spyingAllocator);
JsonDocument doc1(1024, &spyingAllocator);
deserializeJson(doc1, "{\"hello\":\"world\"}");
JsonDocument doc2(1024, &spyingAllocator);
spyingAllocator.clearLog();
doc2 = doc1;
doc2 = doc1;
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
}
REQUIRE(spyingAllocator.log() == AllocatorLog()
<< AllocatorLog::Allocate(1024)
<< AllocatorLog::Allocate(1024)
<< AllocatorLog::Deallocate(1024)
<< AllocatorLog::Deallocate(1024));
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(sizeofString(5)) // hello
<< AllocatorLog::Allocate(sizeofString(5)) // world
);
}
SECTION("Copy assignment reallocates when capacity is smaller") {
{
JsonDocument doc1(4096, &spyingAllocator);
deserializeJson(doc1, "{\"hello\":\"world\"}");
JsonDocument doc2(8, &spyingAllocator);
JsonDocument doc1(4096, &spyingAllocator);
deserializeJson(doc1, "{\"hello\":\"world\"}");
JsonDocument doc2(8, &spyingAllocator);
spyingAllocator.clearLog();
doc2 = doc1;
doc2 = doc1;
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
}
REQUIRE(spyingAllocator.log() == AllocatorLog()
<< AllocatorLog::Allocate(4096)
<< AllocatorLog::Allocate(8)
<< AllocatorLog::Deallocate(8)
<< AllocatorLog::Allocate(4096)
<< AllocatorLog::Deallocate(4096)
<< AllocatorLog::Deallocate(4096));
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Deallocate(8)
<< AllocatorLog::Allocate(4096)
<< AllocatorLog::Allocate(sizeofString(5)) // hello
<< AllocatorLog::Allocate(sizeofString(5)) // world
);
}
SECTION("Copy assignment reallocates when capacity is larger") {
{
JsonDocument doc1(1024, &spyingAllocator);
deserializeJson(doc1, "{\"hello\":\"world\"}");
JsonDocument doc2(4096, &spyingAllocator);
JsonDocument doc1(1024, &spyingAllocator);
deserializeJson(doc1, "{\"hello\":\"world\"}");
JsonDocument doc2(4096, &spyingAllocator);
spyingAllocator.clearLog();
doc2 = doc1;
doc2 = doc1;
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
}
REQUIRE(spyingAllocator.log() == AllocatorLog()
<< AllocatorLog::Allocate(1024)
<< AllocatorLog::Allocate(4096)
<< AllocatorLog::Deallocate(4096)
<< AllocatorLog::Allocate(1024)
<< AllocatorLog::Deallocate(1024)
<< AllocatorLog::Deallocate(1024));
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Deallocate(4096)
<< AllocatorLog::Allocate(1024)
<< AllocatorLog::Allocate(sizeofString(5)) // hello
<< AllocatorLog::Allocate(sizeofString(5)) // world
);
}
SECTION("Move assign") {
@ -79,11 +75,13 @@ TEST_CASE("JsonDocument assignment") {
REQUIRE(doc2.as<std::string>() == "The size of this string is 32!!");
REQUIRE(doc1.as<std::string>() == "null");
}
REQUIRE(spyingAllocator.log() == AllocatorLog()
<< AllocatorLog::Allocate(4096)
<< AllocatorLog::Allocate(8)
<< AllocatorLog::Deallocate(8)
<< AllocatorLog::Deallocate(4096));
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(4096)
<< AllocatorLog::Allocate(sizeofString(31))
<< AllocatorLog::Allocate(8)
<< AllocatorLog::Deallocate(8)
<< AllocatorLog::Deallocate(sizeofString(31))
<< AllocatorLog::Deallocate(4096));
}
SECTION("Assign from JsonObject") {

View File

@ -8,6 +8,7 @@
#include "Allocators.hpp"
using ArduinoJson::detail::addPadding;
using ArduinoJson::detail::sizeofString;
TEST_CASE("JsonDocument constructor") {
SpyingAllocator spyingAllocator;
@ -29,11 +30,15 @@ TEST_CASE("JsonDocument constructor") {
REQUIRE(doc1.as<std::string>() == "The size of this string is 32!!");
REQUIRE(doc2.as<std::string>() == "The size of this string is 32!!");
}
REQUIRE(spyingAllocator.log() == AllocatorLog()
<< AllocatorLog::Allocate(4096)
<< AllocatorLog::Allocate(4096)
<< AllocatorLog::Deallocate(4096)
<< AllocatorLog::Deallocate(4096));
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(4096)
<< AllocatorLog::Allocate(sizeofString(31))
<< AllocatorLog::Allocate(4096)
<< AllocatorLog::Allocate(sizeofString(31))
<< AllocatorLog::Deallocate(sizeofString(31))
<< AllocatorLog::Deallocate(4096)
<< AllocatorLog::Deallocate(sizeofString(31))
<< AllocatorLog::Deallocate(4096));
}
SECTION("JsonDocument(JsonDocument&&)") {
@ -46,9 +51,11 @@ TEST_CASE("JsonDocument constructor") {
REQUIRE(doc2.as<std::string>() == "The size of this string is 32!!");
REQUIRE(doc1.as<std::string>() == "null");
}
REQUIRE(spyingAllocator.log() == AllocatorLog()
<< AllocatorLog::Allocate(4096)
<< AllocatorLog::Deallocate(4096));
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(4096)
<< AllocatorLog::Allocate(sizeofString(31))
<< AllocatorLog::Deallocate(sizeofString(31))
<< AllocatorLog::Deallocate(4096));
}
SECTION("JsonDocument(JsonObject)") {
@ -82,7 +89,9 @@ TEST_CASE("JsonDocument constructor") {
JsonDocument doc2(doc1.as<JsonVariant>(), &spyingAllocator);
REQUIRE(doc2.as<std::string>() == "hello");
REQUIRE(spyingAllocator.log() == AllocatorLog() << AllocatorLog::Allocate(
addPadding(doc1.memoryUsage())));
REQUIRE(
spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(addPadding(doc1.memoryUsage()))
<< AllocatorLog::Allocate(sizeofString(5)));
}
}

View File

@ -21,16 +21,19 @@ TEST_CASE("JsonDocument::garbageCollect()") {
deserializeJson(doc, "{\"blanket\":1,\"dancing\":2}");
REQUIRE(doc.memoryUsage() == sizeofObject(2) + 2 * sizeofString(7));
doc.remove("blanket");
spyingAllocator.clearLog();
bool result = doc.garbageCollect();
REQUIRE(result == true);
REQUIRE(doc.memoryUsage() == sizeofObject(1) + sizeofString(7));
REQUIRE(doc.as<std::string>() == "{\"dancing\":2}");
REQUIRE(spyingAllocator.log() == AllocatorLog()
<< AllocatorLog::Allocate(4096)
<< AllocatorLog::Allocate(4096)
<< AllocatorLog::Deallocate(4096));
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(4096)
<< AllocatorLog::Allocate(sizeofString(7))
<< AllocatorLog::Deallocate(sizeofString(7))
<< AllocatorLog::Deallocate(sizeofString(7))
<< AllocatorLog::Deallocate(4096));
}
SECTION("when allocation fails") {
@ -38,6 +41,7 @@ TEST_CASE("JsonDocument::garbageCollect()") {
REQUIRE(doc.memoryUsage() == sizeofObject(2) + 2 * sizeofString(7));
doc.remove("blanket");
controllableAllocator.disable();
spyingAllocator.clearLog();
bool result = doc.garbageCollect();
@ -46,7 +50,6 @@ TEST_CASE("JsonDocument::garbageCollect()") {
REQUIRE(doc.as<std::string>() == "{\"dancing\":2}");
REQUIRE(spyingAllocator.log() == AllocatorLog()
<< AllocatorLog::Allocate(4096)
<< AllocatorLog::AllocateFail(4096));
}
}

View File

@ -5,8 +5,9 @@
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofString;
TEST_CASE("JsonDocument::overflowed()") {
SECTION("returns false on a fresh object") {
@ -27,13 +28,15 @@ TEST_CASE("JsonDocument::overflowed()") {
}
SECTION("returns true after a failed string copy") {
JsonDocument doc(sizeofArray(1));
ControllableAllocator allocator;
JsonDocument doc(sizeofArray(1), &allocator);
allocator.disable();
doc.add(std::string("example"));
CHECK(doc.overflowed() == true);
}
SECTION("returns false after a successful string copy") {
JsonDocument doc(sizeofArray(1) + sizeofString(7));
JsonDocument doc(sizeofArray(1));
doc.add(std::string("example"));
CHECK(doc.overflowed() == false);
}
@ -46,12 +49,12 @@ TEST_CASE("JsonDocument::overflowed()") {
SECTION("returns true after a failed deserialization") {
JsonDocument doc(sizeofArray(1));
deserializeJson(doc, "[\"example\"]");
deserializeJson(doc, "[1, 2]");
CHECK(doc.overflowed() == true);
}
SECTION("returns false after a successful deserialization") {
JsonDocument doc(sizeofArray(1) + sizeofString(7));
JsonDocument doc(sizeofArray(1));
deserializeJson(doc, "[\"example\"]");
CHECK(doc.overflowed() == false);
}

View File

@ -86,13 +86,15 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
SECTION("owned string") {
doc.set(std::string("abcdefg"));
REQUIRE(doc.as<std::string>() == "abcdefg");
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "abcdefg");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(4096)
<< AllocatorLog::Reallocate(4096, sizeofString(7)));
<< AllocatorLog::Allocate(sizeofString(7))
<< AllocatorLog::Reallocate(4096, 0));
}
SECTION("linked raw") {
@ -114,7 +116,8 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
REQUIRE(doc.as<std::string>() == "[{},12]");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(4096)
<< AllocatorLog::Reallocate(4096, sizeofString(7)));
<< AllocatorLog::Allocate(sizeofString(7))
<< AllocatorLog::Reallocate(4096, 0));
}
SECTION("linked key") {
@ -136,8 +139,8 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
REQUIRE(doc.as<std::string>() == "{\"abcdefg\":42}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(4096)
<< AllocatorLog::Reallocate(
4096, sizeofObject(1) + sizeofString(7)));
<< AllocatorLog::Allocate(sizeofString(7))
<< AllocatorLog::Reallocate(4096, sizeofObject(1)));
}
SECTION("linked string in array") {
@ -159,8 +162,8 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
REQUIRE(doc.as<std::string>() == "[\"abcdefg\"]");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(4096)
<< AllocatorLog::Reallocate(
4096, sizeofArray(1) + sizeofString(7)));
<< AllocatorLog::Allocate(sizeofString(7))
<< AllocatorLog::Reallocate(4096, sizeofArray(1)));
}
SECTION("linked string in object") {
@ -182,21 +185,7 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
REQUIRE(doc.as<std::string>() == "{\"key\":\"abcdefg\"}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(4096)
<< AllocatorLog::Reallocate(
4096, sizeofObject(1) + sizeofString(7)));
}
SECTION("unaligned") {
doc.add(std::string("?")); // two bytes in the string pool
REQUIRE(doc.memoryUsage() == sizeofObject(1) + sizeofString(1));
doc.shrinkToFit();
// the new capacity should be padded to align the pointers
REQUIRE(doc[0] == "?");
REQUIRE(spyingAllocator.log() ==
AllocatorLog() << AllocatorLog::Allocate(4096)
<< AllocatorLog::Reallocate(
4096, sizeofArray(1) + sizeof(void*)));
<< AllocatorLog::Allocate(sizeofString(7))
<< AllocatorLog::Reallocate(4096, sizeofObject(1)));
}
}