forked from bblanchon/ArduinoJson
Create more memory pools as needed (resolves #1074)
This commit is contained in:
@ -5,6 +5,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ArduinoJson/Memory/Allocator.hpp>
|
#include <ArduinoJson/Memory/Allocator.hpp>
|
||||||
|
#include <ArduinoJson/Memory/VariantPool.hpp>
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
@ -218,3 +219,12 @@ class TimebombAllocator : public ArduinoJson::Allocator {
|
|||||||
size_t countdown_ = 0;
|
size_t countdown_ = 0;
|
||||||
Allocator* upstream_;
|
Allocator* upstream_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline size_t sizeofPoolList(size_t n = ARDUINOJSON_INITIAL_POOL_COUNT) {
|
||||||
|
return sizeof(ArduinoJson::detail::VariantPool) * n;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline size_t sizeofPool(
|
||||||
|
ArduinoJson::detail::SlotCount n = ARDUINOJSON_POOL_CAPACITY) {
|
||||||
|
return ArduinoJson::detail::VariantPool::slotsToBytes(n);
|
||||||
|
}
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
#include "Allocators.hpp"
|
||||||
|
|
||||||
using ArduinoJson::detail::sizeofArray;
|
using ArduinoJson::detail::sizeofArray;
|
||||||
|
|
||||||
TEST_CASE("copyArray()") {
|
TEST_CASE("copyArray()") {
|
||||||
@ -109,17 +111,12 @@ TEST_CASE("copyArray()") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SECTION("int[] -> JsonArray, but not enough memory") {
|
SECTION("int[] -> JsonArray, but not enough memory") {
|
||||||
const size_t SIZE = sizeofArray(2);
|
JsonDocument doc(0, FailingAllocator::instance());
|
||||||
JsonDocument doc(SIZE);
|
|
||||||
JsonArray array = doc.to<JsonArray>();
|
JsonArray array = doc.to<JsonArray>();
|
||||||
char json[32];
|
|
||||||
int source[] = {1, 2, 3};
|
int source[] = {1, 2, 3};
|
||||||
|
|
||||||
bool ok = copyArray(source, array);
|
bool ok = copyArray(source, array);
|
||||||
REQUIRE_FALSE(ok);
|
REQUIRE_FALSE(ok);
|
||||||
|
|
||||||
serializeJson(array, json);
|
|
||||||
CHECK(std::string("[1,2]") == json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("int[][] -> JsonArray") {
|
SECTION("int[][] -> JsonArray") {
|
||||||
@ -160,20 +157,12 @@ TEST_CASE("copyArray()") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SECTION("int[][] -> JsonArray, but not enough memory") {
|
SECTION("int[][] -> JsonArray, but not enough memory") {
|
||||||
const size_t SIZE = sizeofArray(2) + sizeofArray(3) + sizeofArray(2);
|
JsonDocument doc(0, FailingAllocator::instance());
|
||||||
JsonDocument doc(SIZE);
|
|
||||||
JsonArray array = doc.to<JsonArray>();
|
JsonArray array = doc.to<JsonArray>();
|
||||||
char json[32] = "";
|
|
||||||
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
|
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
|
||||||
|
|
||||||
CAPTURE(SIZE);
|
|
||||||
|
|
||||||
bool ok = copyArray(source, array);
|
bool ok = copyArray(source, array);
|
||||||
CAPTURE(doc.memoryUsage());
|
REQUIRE(ok == false);
|
||||||
CHECK_FALSE(ok);
|
|
||||||
|
|
||||||
serializeJson(array, json);
|
|
||||||
CHECK(std::string("[[1,2,3],[4,5]]") == json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("JsonArray -> int[], with more space than needed") {
|
SECTION("JsonArray -> int[], with more space than needed") {
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
#include "Allocators.hpp"
|
||||||
|
|
||||||
using ArduinoJson::detail::sizeofArray;
|
using ArduinoJson::detail::sizeofArray;
|
||||||
using ArduinoJson::detail::sizeofObject;
|
using ArduinoJson::detail::sizeofObject;
|
||||||
using ArduinoJson::detail::sizeofString;
|
using ArduinoJson::detail::sizeofString;
|
||||||
@ -257,8 +259,11 @@ TEST_CASE("deserialize JSON array") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("deserialize JSON array under memory constraints") {
|
TEST_CASE("deserialize JSON array under memory constraints") {
|
||||||
SECTION("buffer of the right size for an empty array") {
|
TimebombAllocator allocator(100);
|
||||||
JsonDocument doc(sizeofArray(0));
|
JsonDocument doc(0, &allocator);
|
||||||
|
|
||||||
|
SECTION("empty array requires no allocation") {
|
||||||
|
allocator.setCountdown(0);
|
||||||
char input[] = "[]";
|
char input[] = "[]";
|
||||||
|
|
||||||
DeserializationError err = deserializeJson(doc, input);
|
DeserializationError err = deserializeJson(doc, input);
|
||||||
@ -266,73 +271,39 @@ TEST_CASE("deserialize JSON array under memory constraints") {
|
|||||||
REQUIRE(err == DeserializationError::Ok);
|
REQUIRE(err == DeserializationError::Ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("buffer too small for an array with one element") {
|
SECTION("allocation of pool list fails") {
|
||||||
JsonDocument doc(sizeofArray(0));
|
allocator.setCountdown(0);
|
||||||
char input[] = "[1]";
|
char input[] = "[1]";
|
||||||
|
|
||||||
DeserializationError err = deserializeJson(doc, input);
|
DeserializationError err = deserializeJson(doc, input);
|
||||||
|
|
||||||
REQUIRE(err == DeserializationError::NoMemory);
|
REQUIRE(err == DeserializationError::NoMemory);
|
||||||
|
REQUIRE(doc.as<std::string>() == "[]");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("buffer of the right size for an array with one element") {
|
SECTION("allocation of pool fails") {
|
||||||
JsonDocument doc(sizeofArray(1));
|
allocator.setCountdown(1);
|
||||||
char input[] = "[1]";
|
char input[] = "[1]";
|
||||||
|
|
||||||
DeserializationError err = deserializeJson(doc, input);
|
DeserializationError err = deserializeJson(doc, input);
|
||||||
|
|
||||||
REQUIRE(err == DeserializationError::Ok);
|
REQUIRE(err == DeserializationError::NoMemory);
|
||||||
|
REQUIRE(doc.as<std::string>() == "[]");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("buffer too small for an array with a nested object") {
|
SECTION("allocation of string fails in array") {
|
||||||
JsonDocument doc(sizeofArray(0) + sizeofObject(0));
|
allocator.setCountdown(2);
|
||||||
char input[] = "[{}]";
|
char input[] = "[0,\"hi!\"]";
|
||||||
|
|
||||||
DeserializationError err = deserializeJson(doc, input);
|
DeserializationError err = deserializeJson(doc, input);
|
||||||
|
|
||||||
REQUIRE(err == DeserializationError::NoMemory);
|
REQUIRE(err == DeserializationError::NoMemory);
|
||||||
}
|
REQUIRE(doc.as<std::string>() == "[0,null]");
|
||||||
|
|
||||||
SECTION("buffer of the right size for an array with a nested object") {
|
|
||||||
JsonDocument doc(sizeofArray(1) + sizeofObject(0));
|
|
||||||
char input[] = "[{}]";
|
|
||||||
|
|
||||||
DeserializationError err = deserializeJson(doc, input);
|
|
||||||
|
|
||||||
REQUIRE(err == DeserializationError::Ok);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("don't store space characters") {
|
SECTION("don't store space characters") {
|
||||||
JsonDocument doc(100);
|
|
||||||
|
|
||||||
deserializeJson(doc, " [ \"1234567\" ] ");
|
deserializeJson(doc, " [ \"1234567\" ] ");
|
||||||
|
|
||||||
REQUIRE(sizeofArray(1) + sizeofString(7) == doc.memoryUsage());
|
REQUIRE(sizeofArray(1) + sizeofString(7) == doc.memoryUsage());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Should clear the JsonArray") {
|
|
||||||
JsonDocument doc(sizeofArray(4));
|
|
||||||
char input[] = "[1,2,3,4]";
|
|
||||||
|
|
||||||
deserializeJson(doc, input);
|
|
||||||
deserializeJson(doc, "[]");
|
|
||||||
|
|
||||||
JsonArray arr = doc.as<JsonArray>();
|
|
||||||
REQUIRE(arr.size() == 0);
|
|
||||||
REQUIRE(doc.memoryUsage() == sizeofArray(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("buffer of the right size for an array with two element") {
|
|
||||||
JsonDocument doc(sizeofArray(2));
|
|
||||||
char input[] = "[1,2]";
|
|
||||||
|
|
||||||
DeserializationError err = deserializeJson(doc, input);
|
|
||||||
JsonArray arr = doc.as<JsonArray>();
|
|
||||||
|
|
||||||
REQUIRE(err == DeserializationError::Ok);
|
|
||||||
REQUIRE(doc.is<JsonArray>());
|
|
||||||
REQUIRE(doc.memoryUsage() == sizeofArray(2));
|
|
||||||
REQUIRE(arr[0] == 1);
|
|
||||||
REQUIRE(arr[1] == 2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
#include "Allocators.hpp"
|
||||||
|
|
||||||
using ArduinoJson::detail::sizeofArray;
|
using ArduinoJson::detail::sizeofArray;
|
||||||
using ArduinoJson::detail::sizeofObject;
|
using ArduinoJson::detail::sizeofObject;
|
||||||
using ArduinoJson::detail::sizeofString;
|
using ArduinoJson::detail::sizeofString;
|
||||||
@ -320,59 +322,56 @@ TEST_CASE("deserialize JSON object") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("deserialize JSON object under memory constraints") {
|
TEST_CASE("deserialize JSON object under memory constraints") {
|
||||||
SECTION("buffer for the right size for an empty object") {
|
TimebombAllocator allocator(1024);
|
||||||
JsonDocument doc(sizeofObject(0));
|
JsonDocument doc(0, &allocator);
|
||||||
|
|
||||||
|
SECTION("empty object requires no allocation") {
|
||||||
|
allocator.setCountdown(0);
|
||||||
char input[] = "{}";
|
char input[] = "{}";
|
||||||
|
|
||||||
DeserializationError err = deserializeJson(doc, input);
|
DeserializationError err = deserializeJson(doc, input);
|
||||||
|
|
||||||
REQUIRE(err == DeserializationError::Ok);
|
REQUIRE(err == DeserializationError::Ok);
|
||||||
|
REQUIRE(doc.as<std::string>() == "{}");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("buffer too small for an empty object") {
|
SECTION("key allocation fails") {
|
||||||
JsonDocument doc(sizeofObject(0));
|
allocator.setCountdown(0);
|
||||||
char input[] = "{\"a\":1}";
|
char input[] = "{\"a\":1}";
|
||||||
|
|
||||||
DeserializationError err = deserializeJson(doc, input);
|
DeserializationError err = deserializeJson(doc, input);
|
||||||
|
|
||||||
REQUIRE(err == DeserializationError::NoMemory);
|
REQUIRE(err == DeserializationError::NoMemory);
|
||||||
|
REQUIRE(doc.as<std::string>() == "{}");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("buffer of the right size for an object with one member") {
|
SECTION("pool list allocation fails") {
|
||||||
JsonDocument doc(sizeofObject(1));
|
allocator.setCountdown(2);
|
||||||
char input[] = "{\"a\":1}";
|
char input[] = "{\"a\":1}";
|
||||||
|
|
||||||
DeserializationError err = deserializeJson(doc, input);
|
DeserializationError err = deserializeJson(doc, input);
|
||||||
|
|
||||||
REQUIRE(err == DeserializationError::Ok);
|
REQUIRE(err == DeserializationError::NoMemory);
|
||||||
|
REQUIRE(doc.as<std::string>() == "{}");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("buffer too small for an object with a nested array") {
|
SECTION("pool allocation fails") {
|
||||||
JsonDocument doc(sizeofObject(0) + sizeofArray(0));
|
allocator.setCountdown(3);
|
||||||
char input[] = "{\"a\":[]}";
|
char input[] = "{\"a\":1}";
|
||||||
|
|
||||||
DeserializationError err = deserializeJson(doc, input);
|
DeserializationError err = deserializeJson(doc, input);
|
||||||
|
|
||||||
REQUIRE(err == DeserializationError::NoMemory);
|
REQUIRE(err == DeserializationError::NoMemory);
|
||||||
|
REQUIRE(doc.as<std::string>() == "{}");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("buffer of the right size for an object with a nested array") {
|
SECTION("string allocation fails") {
|
||||||
JsonDocument doc(sizeofObject(1) + sizeofArray(0));
|
allocator.setCountdown(4);
|
||||||
char input[] = "{\"a\":[]}";
|
char input[] = "{\"a\":\"b\"}";
|
||||||
|
|
||||||
DeserializationError err = deserializeJson(doc, input);
|
DeserializationError err = deserializeJson(doc, input);
|
||||||
|
|
||||||
REQUIRE(err == DeserializationError::Ok);
|
REQUIRE(err == DeserializationError::NoMemory);
|
||||||
}
|
REQUIRE(doc.as<std::string>() == "{\"a\":null}");
|
||||||
|
|
||||||
SECTION("Should clear the JsonObject") {
|
|
||||||
JsonDocument doc(sizeofObject(1));
|
|
||||||
char input[] = "{\"hello\":\"world\"}";
|
|
||||||
|
|
||||||
deserializeJson(doc, input);
|
|
||||||
deserializeJson(doc, "{}");
|
|
||||||
|
|
||||||
REQUIRE(doc.as<JsonObject>().size() == 0);
|
|
||||||
REQUIRE(doc.memoryUsage() == sizeofObject(0));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ TEST_CASE("Invalid JSON string") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Allocation of the key fails") {
|
TEST_CASE("Allocation of the key fails") {
|
||||||
TimebombAllocator timebombAllocator(1);
|
TimebombAllocator timebombAllocator(0);
|
||||||
SpyingAllocator spyingAllocator(&timebombAllocator);
|
SpyingAllocator spyingAllocator(&timebombAllocator);
|
||||||
JsonDocument doc(1024, &spyingAllocator);
|
JsonDocument doc(1024, &spyingAllocator);
|
||||||
|
|
||||||
@ -107,19 +107,19 @@ TEST_CASE("Allocation of the key fails") {
|
|||||||
REQUIRE(deserializeJson(doc, "{\"example\":1}") ==
|
REQUIRE(deserializeJson(doc, "{\"example\":1}") ==
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(1024)
|
AllocatorLog() << AllocatorLog::AllocateFail(sizeofString(31)));
|
||||||
<< AllocatorLog::AllocateFail(sizeofString(31)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Quoted string, second member") {
|
SECTION("Quoted string, second member") {
|
||||||
timebombAllocator.setCountdown(2);
|
timebombAllocator.setCountdown(4);
|
||||||
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(1024)
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(31))
|
||||||
<< AllocatorLog::Allocate(sizeofString(31))
|
|
||||||
<< AllocatorLog::Reallocate(sizeofString(31),
|
<< AllocatorLog::Reallocate(sizeofString(31),
|
||||||
sizeofString(5))
|
sizeofString(5))
|
||||||
|
<< AllocatorLog::Allocate(sizeofPoolList())
|
||||||
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::AllocateFail(sizeofString(31)));
|
<< AllocatorLog::AllocateFail(sizeofString(31)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,19 +127,19 @@ TEST_CASE("Allocation of the key fails") {
|
|||||||
REQUIRE(deserializeJson(doc, "{example:1}") ==
|
REQUIRE(deserializeJson(doc, "{example:1}") ==
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(1024)
|
AllocatorLog() << AllocatorLog::AllocateFail(sizeofString(31)));
|
||||||
<< AllocatorLog::AllocateFail(sizeofString(31)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Non-Quoted string, second member") {
|
SECTION("Non-Quoted string, second member") {
|
||||||
timebombAllocator.setCountdown(2);
|
timebombAllocator.setCountdown(4);
|
||||||
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(1024)
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(31))
|
||||||
<< AllocatorLog::Allocate(sizeofString(31))
|
|
||||||
<< AllocatorLog::Reallocate(sizeofString(31),
|
<< AllocatorLog::Reallocate(sizeofString(31),
|
||||||
sizeofString(5))
|
sizeofString(5))
|
||||||
|
<< AllocatorLog::Allocate(sizeofPoolList())
|
||||||
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::AllocateFail(sizeofString(31)));
|
<< AllocatorLog::AllocateFail(sizeofString(31)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ 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(sizeofString(5)) // world
|
<< AllocatorLog::Allocate(sizeofString(5)) // world
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -41,8 +43,8 @@ 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::Deallocate(sizeofArray(1))
|
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
||||||
<< AllocatorLog::Allocate(capacity)
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::Allocate(sizeofString(5)) // hello
|
<< AllocatorLog::Allocate(sizeofString(5)) // hello
|
||||||
<< AllocatorLog::Allocate(sizeofString(5)) // world
|
<< AllocatorLog::Allocate(sizeofString(5)) // world
|
||||||
);
|
);
|
||||||
@ -59,9 +61,9 @@ 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::Deallocate(4096)
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(5)) // hello
|
||||||
<< AllocatorLog::Allocate(capacity1)
|
<< AllocatorLog::Allocate(sizeofPoolList())
|
||||||
<< AllocatorLog::Allocate(sizeofString(5)) // hello
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::Allocate(sizeofString(5)) // world
|
<< AllocatorLog::Allocate(sizeofString(5)) // world
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -69,21 +71,24 @@ TEST_CASE("JsonDocument assignment") {
|
|||||||
SECTION("Move assign") {
|
SECTION("Move assign") {
|
||||||
{
|
{
|
||||||
JsonDocument doc1(4096, &spyingAllocator);
|
JsonDocument doc1(4096, &spyingAllocator);
|
||||||
doc1.set(std::string("The size of this string is 32!!"));
|
doc1[std::string("hello")] = std::string("world");
|
||||||
JsonDocument doc2(128, &spyingAllocator);
|
JsonDocument doc2(128, &spyingAllocator);
|
||||||
|
|
||||||
doc2 = std::move(doc1);
|
doc2 = std::move(doc1);
|
||||||
|
|
||||||
REQUIRE(doc2.as<std::string>() == "The size of this string is 32!!");
|
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
|
||||||
REQUIRE(doc1.as<std::string>() == "null");
|
REQUIRE(doc1.as<std::string>() == "null");
|
||||||
}
|
}
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(
|
||||||
AllocatorLog() << AllocatorLog::Allocate(4096)
|
spyingAllocator.log() ==
|
||||||
<< AllocatorLog::Allocate(sizeofString(31))
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(5)) // hello
|
||||||
<< AllocatorLog::Allocate(128)
|
<< AllocatorLog::Allocate(sizeofPoolList())
|
||||||
<< AllocatorLog::Deallocate(128)
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::Deallocate(sizeofString(31))
|
<< AllocatorLog::Allocate(sizeofString(5)) // world
|
||||||
<< AllocatorLog::Deallocate(4096));
|
<< AllocatorLog::Deallocate(sizeofString(5)) // hello
|
||||||
|
<< AllocatorLog::Deallocate(sizeofString(5)) // world
|
||||||
|
<< AllocatorLog::Deallocate(sizeofPool())
|
||||||
|
<< AllocatorLog::Deallocate(sizeofPoolList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Assign from JsonObject") {
|
SECTION("Assign from JsonObject") {
|
||||||
|
@ -16,9 +16,7 @@ TEST_CASE("JsonDocument constructor") {
|
|||||||
|
|
||||||
SECTION("JsonDocument(size_t)") {
|
SECTION("JsonDocument(size_t)") {
|
||||||
{ JsonDocument doc(4096, &spyingAllocator); }
|
{ JsonDocument doc(4096, &spyingAllocator); }
|
||||||
REQUIRE(spyingAllocator.log() == AllocatorLog()
|
REQUIRE(spyingAllocator.log() == AllocatorLog());
|
||||||
<< AllocatorLog::Allocate(4096)
|
|
||||||
<< AllocatorLog::Deallocate(4096));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("JsonDocument(const JsonDocument&)") {
|
SECTION("JsonDocument(const JsonDocument&)") {
|
||||||
@ -33,14 +31,10 @@ TEST_CASE("JsonDocument constructor") {
|
|||||||
REQUIRE(doc2.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() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(capacity)
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(31))
|
||||||
<< AllocatorLog::Allocate(sizeofString(31))
|
|
||||||
<< AllocatorLog::Allocate(capacity)
|
|
||||||
<< AllocatorLog::Allocate(sizeofString(31))
|
<< AllocatorLog::Allocate(sizeofString(31))
|
||||||
<< AllocatorLog::Deallocate(sizeofString(31))
|
<< AllocatorLog::Deallocate(sizeofString(31))
|
||||||
<< AllocatorLog::Deallocate(capacity)
|
<< AllocatorLog::Deallocate(sizeofString(31)));
|
||||||
<< AllocatorLog::Deallocate(sizeofString(31))
|
|
||||||
<< AllocatorLog::Deallocate(capacity));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("JsonDocument(JsonDocument&&)") {
|
SECTION("JsonDocument(JsonDocument&&)") {
|
||||||
@ -54,10 +48,8 @@ TEST_CASE("JsonDocument constructor") {
|
|||||||
REQUIRE(doc1.as<std::string>() == "null");
|
REQUIRE(doc1.as<std::string>() == "null");
|
||||||
}
|
}
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(4096)
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(31))
|
||||||
<< AllocatorLog::Allocate(sizeofString(31))
|
<< AllocatorLog::Deallocate(sizeofString(31)));
|
||||||
<< AllocatorLog::Deallocate(sizeofString(31))
|
|
||||||
<< AllocatorLog::Deallocate(4096));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("JsonDocument(JsonObject)") {
|
SECTION("JsonDocument(JsonObject)") {
|
||||||
@ -69,7 +61,8 @@ 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(sizeofObject(1)));
|
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
||||||
|
<< AllocatorLog::Allocate(sizeofPool()));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Construct from JsonArray") {
|
SECTION("Construct from JsonArray") {
|
||||||
@ -80,8 +73,9 @@ TEST_CASE("JsonDocument constructor") {
|
|||||||
JsonDocument doc2(arr, &spyingAllocator);
|
JsonDocument doc2(arr, &spyingAllocator);
|
||||||
|
|
||||||
REQUIRE(doc2.as<std::string>() == "[\"hello\"]");
|
REQUIRE(doc2.as<std::string>() == "[\"hello\"]");
|
||||||
REQUIRE(spyingAllocator.log() == AllocatorLog() << AllocatorLog::Allocate(
|
REQUIRE(spyingAllocator.log() ==
|
||||||
addPadding(doc1.memoryUsage())));
|
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
||||||
|
<< AllocatorLog::Allocate(sizeofPool()));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Construct from JsonVariant") {
|
SECTION("Construct from JsonVariant") {
|
||||||
@ -92,8 +86,6 @@ 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(
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(5)));
|
||||||
sizeofString(5)) // TODO: remove
|
|
||||||
<< AllocatorLog::Allocate(sizeofString(5)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,10 +30,12 @@ TEST_CASE("JsonDocument::garbageCollect()") {
|
|||||||
REQUIRE(doc.memoryUsage() == sizeofObject(1) + sizeofString(7));
|
REQUIRE(doc.memoryUsage() == sizeofObject(1) + sizeofString(7));
|
||||||
REQUIRE(doc.as<std::string>() == "{\"dancing\":2}");
|
REQUIRE(doc.as<std::string>() == "{\"dancing\":2}");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(capacity)
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(7))
|
||||||
<< AllocatorLog::Allocate(sizeofString(7))
|
<< AllocatorLog::Allocate(sizeofPoolList())
|
||||||
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
<< AllocatorLog::Deallocate(sizeofString(7))
|
<< AllocatorLog::Deallocate(sizeofString(7))
|
||||||
<< AllocatorLog::Deallocate(capacity));
|
<< AllocatorLog::Deallocate(sizeofPool())
|
||||||
|
<< AllocatorLog::Deallocate(sizeofPoolList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("when allocation fails") {
|
SECTION("when allocation fails") {
|
||||||
@ -50,7 +52,6 @@ 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::AllocateFail(capacity)
|
AllocatorLog() << AllocatorLog::AllocateFail(sizeofString(7)));
|
||||||
<< AllocatorLog::AllocateFail(sizeofString(7)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,80 +10,81 @@
|
|||||||
using ArduinoJson::detail::sizeofArray;
|
using ArduinoJson::detail::sizeofArray;
|
||||||
|
|
||||||
TEST_CASE("JsonDocument::overflowed()") {
|
TEST_CASE("JsonDocument::overflowed()") {
|
||||||
|
TimebombAllocator allocator(10);
|
||||||
|
JsonDocument doc(0, &allocator);
|
||||||
|
|
||||||
SECTION("returns false on a fresh object") {
|
SECTION("returns false on a fresh object") {
|
||||||
JsonDocument doc(0);
|
allocator.setCountdown(0);
|
||||||
CHECK(doc.overflowed() == false);
|
CHECK(doc.overflowed() == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("returns true after a failed insertion") {
|
SECTION("returns true after a failed insertion") {
|
||||||
JsonDocument doc(0);
|
allocator.setCountdown(0);
|
||||||
doc.add(0);
|
doc.add(0);
|
||||||
CHECK(doc.overflowed() == true);
|
CHECK(doc.overflowed() == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("returns false after successful insertion") {
|
SECTION("returns false after successful insertion") {
|
||||||
JsonDocument doc(sizeofArray(1));
|
allocator.setCountdown(2);
|
||||||
doc.add(0);
|
doc.add(0);
|
||||||
CHECK(doc.overflowed() == false);
|
CHECK(doc.overflowed() == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("returns true after a failed string copy") {
|
SECTION("returns true after a failed string copy") {
|
||||||
ControllableAllocator allocator;
|
allocator.setCountdown(0);
|
||||||
JsonDocument doc(sizeofArray(1), &allocator);
|
|
||||||
allocator.disable();
|
|
||||||
doc.add(std::string("example"));
|
doc.add(std::string("example"));
|
||||||
CHECK(doc.overflowed() == true);
|
CHECK(doc.overflowed() == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("returns false after a successful string copy") {
|
SECTION("returns false after a successful string copy") {
|
||||||
JsonDocument doc(sizeofArray(1));
|
allocator.setCountdown(3);
|
||||||
doc.add(std::string("example"));
|
doc.add(std::string("example"));
|
||||||
CHECK(doc.overflowed() == false);
|
CHECK(doc.overflowed() == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("returns true after a failed member add") {
|
SECTION("returns true after a failed member add") {
|
||||||
JsonDocument doc(1);
|
allocator.setCountdown(0);
|
||||||
doc["example"] = true;
|
doc["example"] = true;
|
||||||
CHECK(doc.overflowed() == true);
|
CHECK(doc.overflowed() == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("returns true after a failed deserialization") {
|
SECTION("returns true after a failed deserialization") {
|
||||||
JsonDocument doc(sizeofArray(1));
|
allocator.setCountdown(1);
|
||||||
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") {
|
||||||
JsonDocument doc(sizeofArray(1));
|
allocator.setCountdown(4);
|
||||||
deserializeJson(doc, "[\"example\"]");
|
deserializeJson(doc, "[\"example\"]");
|
||||||
CHECK(doc.overflowed() == false);
|
CHECK(doc.overflowed() == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("returns false after clear()") {
|
SECTION("returns false after clear()") {
|
||||||
JsonDocument doc(0);
|
allocator.setCountdown(0);
|
||||||
doc.add(0);
|
doc.add(0);
|
||||||
doc.clear();
|
doc.clear();
|
||||||
CHECK(doc.overflowed() == false);
|
CHECK(doc.overflowed() == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("remains false after shrinkToFit()") {
|
SECTION("remains false after shrinkToFit()") {
|
||||||
JsonDocument doc(sizeofArray(1));
|
allocator.setCountdown(2);
|
||||||
doc.add(0);
|
doc.add(0);
|
||||||
|
allocator.setCountdown(2);
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
CHECK(doc.overflowed() == false);
|
CHECK(doc.overflowed() == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("remains true after shrinkToFit()") {
|
SECTION("remains true after shrinkToFit()") {
|
||||||
JsonDocument doc(sizeofArray(1));
|
allocator.setCountdown(0);
|
||||||
doc.add(0);
|
|
||||||
doc.add(0);
|
doc.add(0);
|
||||||
|
allocator.setCountdown(2);
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
CHECK(doc.overflowed() == true);
|
CHECK(doc.overflowed() == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("return false after garbageCollect()") {
|
SECTION("return false after garbageCollect()") {
|
||||||
JsonDocument doc(sizeofArray(1));
|
allocator.setCountdown(0);
|
||||||
doc.add(0);
|
|
||||||
doc.add(0);
|
doc.add(0);
|
||||||
doc.garbageCollect();
|
doc.garbageCollect();
|
||||||
CHECK(doc.overflowed() == false);
|
CHECK(doc.overflowed() == false);
|
||||||
|
@ -48,9 +48,7 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "null");
|
REQUIRE(doc.as<std::string>() == "null");
|
||||||
REQUIRE(spyingAllocator.log() == AllocatorLog()
|
REQUIRE(spyingAllocator.log() == AllocatorLog());
|
||||||
<< AllocatorLog::Allocate(4096)
|
|
||||||
<< AllocatorLog::Reallocate(4096, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("empty object") {
|
SECTION("empty object") {
|
||||||
@ -59,9 +57,7 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "{}");
|
REQUIRE(doc.as<std::string>() == "{}");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() == AllocatorLog());
|
||||||
AllocatorLog() << AllocatorLog::Allocate(4096)
|
|
||||||
<< AllocatorLog::Reallocate(4096, sizeofObject(0)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("empty array") {
|
SECTION("empty array") {
|
||||||
@ -70,9 +66,7 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "[]");
|
REQUIRE(doc.as<std::string>() == "[]");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() == AllocatorLog());
|
||||||
AllocatorLog() << AllocatorLog::Allocate(4096)
|
|
||||||
<< AllocatorLog::Reallocate(4096, sizeofArray(0)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("linked string") {
|
SECTION("linked string") {
|
||||||
@ -81,9 +75,7 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "hello");
|
REQUIRE(doc.as<std::string>() == "hello");
|
||||||
REQUIRE(spyingAllocator.log() == AllocatorLog()
|
REQUIRE(spyingAllocator.log() == AllocatorLog());
|
||||||
<< AllocatorLog::Allocate(4096)
|
|
||||||
<< AllocatorLog::Reallocate(4096, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("owned string") {
|
SECTION("owned string") {
|
||||||
@ -94,9 +86,7 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "abcdefg");
|
REQUIRE(doc.as<std::string>() == "abcdefg");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(4096)
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(7)));
|
||||||
<< AllocatorLog::Allocate(sizeofString(7))
|
|
||||||
<< AllocatorLog::Reallocate(4096, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("raw string") {
|
SECTION("raw string") {
|
||||||
@ -106,9 +96,7 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "[{},12]");
|
REQUIRE(doc.as<std::string>() == "[{},12]");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(4096)
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(7)));
|
||||||
<< AllocatorLog::Allocate(sizeofString(7))
|
|
||||||
<< AllocatorLog::Reallocate(4096, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("linked key") {
|
SECTION("linked key") {
|
||||||
@ -118,8 +106,12 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "{\"key\":42}");
|
REQUIRE(doc.as<std::string>() == "{\"key\":42}");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(4096)
|
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
||||||
<< AllocatorLog::Reallocate(4096, sizeofObject(1)));
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
|
<< AllocatorLog::Reallocate(sizeofPool(),
|
||||||
|
sizeofObject(1))
|
||||||
|
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
||||||
|
sizeofPoolList(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("owned key") {
|
SECTION("owned key") {
|
||||||
@ -129,9 +121,13 @@ 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(4096)
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString(7))
|
||||||
<< AllocatorLog::Allocate(sizeofString(7))
|
<< AllocatorLog::Allocate(sizeofPoolList())
|
||||||
<< AllocatorLog::Reallocate(4096, sizeofObject(1)));
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
|
<< AllocatorLog::Reallocate(sizeofPool(),
|
||||||
|
sizeofObject(1))
|
||||||
|
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
||||||
|
sizeofPoolList(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("linked string in array") {
|
SECTION("linked string in array") {
|
||||||
@ -140,9 +136,13 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "[\"hello\"]");
|
REQUIRE(doc.as<std::string>() == "[\"hello\"]");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(
|
||||||
AllocatorLog() << AllocatorLog::Allocate(4096)
|
spyingAllocator.log() ==
|
||||||
<< AllocatorLog::Reallocate(4096, sizeofArray(1)));
|
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
||||||
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
|
<< AllocatorLog::Reallocate(sizeofPool(), sizeofArray(1))
|
||||||
|
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
||||||
|
sizeofPoolList(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("owned string in array") {
|
SECTION("owned string in array") {
|
||||||
@ -151,10 +151,14 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "[\"abcdefg\"]");
|
REQUIRE(doc.as<std::string>() == "[\"abcdefg\"]");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(
|
||||||
AllocatorLog() << AllocatorLog::Allocate(4096)
|
spyingAllocator.log() ==
|
||||||
<< AllocatorLog::Allocate(sizeofString(7))
|
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
||||||
<< AllocatorLog::Reallocate(4096, sizeofArray(1)));
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
|
<< AllocatorLog::Allocate(sizeofString(7))
|
||||||
|
<< AllocatorLog::Reallocate(sizeofPool(), sizeofArray(1))
|
||||||
|
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
||||||
|
sizeofPoolList(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("linked string in object") {
|
SECTION("linked string in object") {
|
||||||
@ -164,8 +168,12 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "{\"key\":\"hello\"}");
|
REQUIRE(doc.as<std::string>() == "{\"key\":\"hello\"}");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(spyingAllocator.log() ==
|
||||||
AllocatorLog() << AllocatorLog::Allocate(4096)
|
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
||||||
<< AllocatorLog::Reallocate(4096, sizeofObject(1)));
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
|
<< AllocatorLog::Reallocate(sizeofPool(),
|
||||||
|
sizeofObject(1))
|
||||||
|
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
||||||
|
sizeofPoolList(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("owned string in object") {
|
SECTION("owned string in object") {
|
||||||
@ -174,9 +182,13 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
REQUIRE(doc.as<std::string>() == "{\"key\":\"abcdefg\"}");
|
REQUIRE(doc.as<std::string>() == "{\"key\":\"abcdefg\"}");
|
||||||
REQUIRE(spyingAllocator.log() ==
|
REQUIRE(
|
||||||
AllocatorLog() << AllocatorLog::Allocate(4096)
|
spyingAllocator.log() ==
|
||||||
<< AllocatorLog::Allocate(sizeofString(7))
|
AllocatorLog() << AllocatorLog::Allocate(sizeofPoolList())
|
||||||
<< AllocatorLog::Reallocate(4096, sizeofObject(1)));
|
<< AllocatorLog::Allocate(sizeofPool())
|
||||||
|
<< AllocatorLog::Allocate(sizeofString(7))
|
||||||
|
<< AllocatorLog::Reallocate(sizeofPool(), sizeofPool(1))
|
||||||
|
<< AllocatorLog::Reallocate(sizeofPoolList(),
|
||||||
|
sizeofPoolList(1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
#include "Allocators.hpp"
|
||||||
|
|
||||||
using ArduinoJson::detail::sizeofObject;
|
using ArduinoJson::detail::sizeofObject;
|
||||||
|
|
||||||
TEST_CASE("JsonObject::set()") {
|
TEST_CASE("JsonObject::set()") {
|
||||||
@ -73,12 +75,13 @@ TEST_CASE("JsonObject::set()") {
|
|||||||
REQUIRE(obj2["hello"] == std::string("world"));
|
REQUIRE(obj2["hello"] == std::string("world"));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("destination too small to store the key") {
|
SECTION("copy fails in the middle of an object") {
|
||||||
JsonDocument doc3(sizeofObject(1));
|
TimebombAllocator allocator(3);
|
||||||
|
JsonDocument doc3(0, &allocator);
|
||||||
JsonObject obj3 = doc3.to<JsonObject>();
|
JsonObject obj3 = doc3.to<JsonObject>();
|
||||||
|
|
||||||
obj1["a"] = 1;
|
obj1[std::string("a")] = 1;
|
||||||
obj1["b"] = 2;
|
obj1[std::string("b")] = 2;
|
||||||
|
|
||||||
bool success = obj3.set(obj1);
|
bool success = obj3.set(obj1);
|
||||||
|
|
||||||
@ -86,16 +89,17 @@ TEST_CASE("JsonObject::set()") {
|
|||||||
REQUIRE(doc3.as<std::string>() == "{\"a\":1}");
|
REQUIRE(doc3.as<std::string>() == "{\"a\":1}");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("destination too small to store the value") {
|
SECTION("copy fails in the middle of an array") {
|
||||||
JsonDocument doc3(sizeofObject(1));
|
TimebombAllocator allocator(2);
|
||||||
|
JsonDocument doc3(0, &allocator);
|
||||||
JsonObject obj3 = doc3.to<JsonObject>();
|
JsonObject obj3 = doc3.to<JsonObject>();
|
||||||
|
|
||||||
obj1["hello"][1] = "world";
|
obj1["hello"][0] = std::string("world");
|
||||||
|
|
||||||
bool success = obj3.set(obj1);
|
bool success = obj3.set(obj1);
|
||||||
|
|
||||||
REQUIRE(success == false);
|
REQUIRE(success == false);
|
||||||
REQUIRE(doc3.as<std::string>() == "{\"hello\":[]}");
|
REQUIRE(doc3.as<std::string>() == "{\"hello\":[null]}");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("destination is null") {
|
SECTION("destination is null") {
|
||||||
|
@ -43,14 +43,6 @@ TEST_CASE("serializeJson(JsonArray)") {
|
|||||||
check(array, "[\"hello\",\"world\"]");
|
check(array, "[\"hello\",\"world\"]");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("OneStringOverCapacity") {
|
|
||||||
array.add("hello");
|
|
||||||
array.add("world");
|
|
||||||
array.add("lost");
|
|
||||||
|
|
||||||
check(array, "[\"hello\",\"world\"]");
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("One double") {
|
SECTION("One double") {
|
||||||
array.add(3.1415927);
|
array.add(3.1415927);
|
||||||
check(array, "[3.1415927]");
|
check(array, "[3.1415927]");
|
||||||
@ -82,14 +74,6 @@ TEST_CASE("serializeJson(JsonArray)") {
|
|||||||
check(array, "[{\"key\":\"value\"}]");
|
check(array, "[{\"key\":\"value\"}]");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("OneIntegerOverCapacity") {
|
|
||||||
array.add(1);
|
|
||||||
array.add(2);
|
|
||||||
array.add(3);
|
|
||||||
|
|
||||||
check(array, "[1,2]");
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("OneTrue") {
|
SECTION("OneTrue") {
|
||||||
array.add(true);
|
array.add(true);
|
||||||
|
|
||||||
@ -109,14 +93,6 @@ TEST_CASE("serializeJson(JsonArray)") {
|
|||||||
check(array, "[false,true]");
|
check(array, "[false,true]");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("OneBooleanOverCapacity") {
|
|
||||||
array.add(false);
|
|
||||||
array.add(true);
|
|
||||||
array.add(false);
|
|
||||||
|
|
||||||
check(array, "[false,true]");
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("OneEmptyNestedArray") {
|
SECTION("OneEmptyNestedArray") {
|
||||||
array.createNestedArray();
|
array.createNestedArray();
|
||||||
|
|
||||||
|
@ -178,33 +178,33 @@ TEST_CASE("deserializeMsgPack() under memory constaints") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SECTION("fixarray") {
|
SECTION("fixarray") {
|
||||||
checkError(sizeofArray(0), 1, "\x90", DeserializationError::Ok); // []
|
checkError(sizeofArray(0), 0, "\x90", DeserializationError::Ok); // []
|
||||||
|
checkError(sizeofArray(0), 0, "\x91\x01",
|
||||||
|
DeserializationError::NoMemory); // [1]
|
||||||
checkError(sizeofArray(0), 1, "\x91\x01",
|
checkError(sizeofArray(0), 1, "\x91\x01",
|
||||||
DeserializationError::NoMemory); // [1]
|
DeserializationError::NoMemory); // [1]
|
||||||
checkError(sizeofArray(1), 1, "\x91\x01",
|
checkError(sizeofArray(1), 2, "\x91\x01",
|
||||||
DeserializationError::Ok); // [1]
|
DeserializationError::Ok); // [1]
|
||||||
checkError(sizeofArray(1), 1, "\x92\x01\x02",
|
|
||||||
DeserializationError::NoMemory); // [1,2]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("array 16") {
|
SECTION("array 16") {
|
||||||
checkError(sizeofArray(0), 1, "\xDC\x00\x00", DeserializationError::Ok);
|
checkError(sizeofArray(0), 0, "\xDC\x00\x00", DeserializationError::Ok);
|
||||||
|
checkError(sizeofArray(0), 0, "\xDC\x00\x01\x01",
|
||||||
|
DeserializationError::NoMemory);
|
||||||
checkError(sizeofArray(0), 1, "\xDC\x00\x01\x01",
|
checkError(sizeofArray(0), 1, "\xDC\x00\x01\x01",
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
checkError(sizeofArray(1), 1, "\xDC\x00\x01\x01", DeserializationError::Ok);
|
checkError(sizeofArray(1), 2, "\xDC\x00\x01\x01", DeserializationError::Ok);
|
||||||
checkError(sizeofArray(1), 1, "\xDC\x00\x02\x01\x02",
|
|
||||||
DeserializationError::NoMemory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("array 32") {
|
SECTION("array 32") {
|
||||||
checkError(sizeofArray(0), 1, "\xDD\x00\x00\x00\x00",
|
checkError(sizeofArray(0), 0, "\xDD\x00\x00\x00\x00",
|
||||||
DeserializationError::Ok);
|
DeserializationError::Ok);
|
||||||
checkError(sizeofArray(0), 1, "\xDD\x00\x00\x00\x01\x01",
|
checkError(sizeofArray(0), 0, "\xDD\x00\x00\x00\x01\x01",
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
checkError(sizeofArray(1), 1, "\xDD\x00\x00\x00\x01\x01",
|
checkError(sizeofArray(1), 1, "\xDD\x00\x00\x00\x01\x01",
|
||||||
DeserializationError::Ok);
|
|
||||||
checkError(sizeofArray(1), 1, "\xDD\x00\x00\x00\x02\x01\x02",
|
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
|
checkError(sizeofArray(1), 2, "\xDD\x00\x00\x00\x01\x01",
|
||||||
|
DeserializationError::Ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("fixmap") {
|
SECTION("fixmap") {
|
||||||
@ -214,13 +214,13 @@ TEST_CASE("deserializeMsgPack() under memory constaints") {
|
|||||||
SECTION("{H:1}") {
|
SECTION("{H:1}") {
|
||||||
checkError(sizeofObject(0), 0, "\x81\xA1H\x01",
|
checkError(sizeofObject(0), 0, "\x81\xA1H\x01",
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
checkError(sizeofObject(1) + sizeofString(2), 3, "\x81\xA1H\x01",
|
checkError(sizeofObject(1) + sizeofString(2), 4, "\x81\xA1H\x01",
|
||||||
DeserializationError::Ok);
|
DeserializationError::Ok);
|
||||||
}
|
}
|
||||||
SECTION("{H:1,W:2}") {
|
SECTION("{H:1,W:2}") {
|
||||||
checkError(sizeofObject(1) + sizeofString(2), 3, "\x82\xA1H\x01\xA1W\x02",
|
checkError(sizeofObject(1) + sizeofString(2), 4, "\x82\xA1H\x01\xA1W\x02",
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
checkError(sizeofObject(2) + 2 * sizeofString(2), 5,
|
checkError(sizeofObject(2) + 2 * sizeofString(2), 6,
|
||||||
"\x82\xA1H\x01\xA1W\x02", DeserializationError::Ok);
|
"\x82\xA1H\x01\xA1W\x02", DeserializationError::Ok);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -230,16 +230,16 @@ TEST_CASE("deserializeMsgPack() under memory constaints") {
|
|||||||
checkError(sizeofObject(0), 0, "\xDE\x00\x00", DeserializationError::Ok);
|
checkError(sizeofObject(0), 0, "\xDE\x00\x00", DeserializationError::Ok);
|
||||||
}
|
}
|
||||||
SECTION("{H:1}") {
|
SECTION("{H:1}") {
|
||||||
checkError(sizeofObject(1) + sizeofString(2), 1, "\xDE\x00\x01\xA1H\x01",
|
checkError(sizeofObject(1) + sizeofString(2), 2, "\xDE\x00\x01\xA1H\x01",
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
checkError(sizeofObject(1) + sizeofString(2), 3, "\xDE\x00\x01\xA1H\x01",
|
checkError(sizeofObject(1) + sizeofString(2), 4, "\xDE\x00\x01\xA1H\x01",
|
||||||
DeserializationError::Ok);
|
DeserializationError::Ok);
|
||||||
}
|
}
|
||||||
SECTION("{H:1,W:2}") {
|
SECTION("{H:1,W:2}") {
|
||||||
checkError(sizeofObject(1) + sizeofString(2), 3,
|
checkError(sizeofObject(1) + sizeofString(2), 4,
|
||||||
"\xDE\x00\x02\xA1H\x01\xA1W\x02",
|
"\xDE\x00\x02\xA1H\x01\xA1W\x02",
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
checkError(sizeofObject(2) + 2 * sizeofObject(1), 5,
|
checkError(sizeofObject(2) + 2 * sizeofObject(1), 6,
|
||||||
"\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok);
|
"\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -250,17 +250,17 @@ TEST_CASE("deserializeMsgPack() under memory constaints") {
|
|||||||
DeserializationError::Ok);
|
DeserializationError::Ok);
|
||||||
}
|
}
|
||||||
SECTION("{H:1}") {
|
SECTION("{H:1}") {
|
||||||
checkError(sizeofObject(1) + sizeofString(2), 1,
|
checkError(sizeofObject(1) + sizeofString(2), 2,
|
||||||
"\xDF\x00\x00\x00\x01\xA1H\x01",
|
"\xDF\x00\x00\x00\x01\xA1H\x01",
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
checkError(sizeofObject(1) + sizeofString(2), 3,
|
checkError(sizeofObject(1) + sizeofString(2), 4,
|
||||||
"\xDF\x00\x00\x00\x01\xA1H\x01", DeserializationError::Ok);
|
"\xDF\x00\x00\x00\x01\xA1H\x01", DeserializationError::Ok);
|
||||||
}
|
}
|
||||||
SECTION("{H:1,W:2}") {
|
SECTION("{H:1,W:2}") {
|
||||||
checkError(sizeofObject(1) + 2 * sizeofString(2), 3,
|
checkError(sizeofObject(1) + 2 * sizeofString(2), 4,
|
||||||
"\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02",
|
"\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02",
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
checkError(sizeofObject(2) + 2 * sizeofObject(1), 5,
|
checkError(sizeofObject(2) + 2 * sizeofObject(1), 6,
|
||||||
"\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02",
|
"\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02",
|
||||||
DeserializationError::Ok);
|
DeserializationError::Ok);
|
||||||
}
|
}
|
||||||
|
@ -30,15 +30,7 @@ TEST_CASE("ResourceManager::allocSlot()") {
|
|||||||
REQUIRE(isAligned(resources.allocSlot().operator VariantSlot*()));
|
REQUIRE(isAligned(resources.allocSlot().operator VariantSlot*()));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Returns zero if capacity is 0") {
|
SECTION("Returns null if pool list allocation fails") {
|
||||||
ResourceManager resources(0);
|
|
||||||
|
|
||||||
auto variant = resources.allocSlot();
|
|
||||||
REQUIRE(variant.id() == NULL_SLOT);
|
|
||||||
REQUIRE(static_cast<VariantSlot*>(variant) == nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Returns zero if buffer is null") {
|
|
||||||
ResourceManager resources(4096, FailingAllocator::instance());
|
ResourceManager resources(4096, FailingAllocator::instance());
|
||||||
|
|
||||||
auto variant = resources.allocSlot();
|
auto variant = resources.allocSlot();
|
||||||
@ -46,8 +38,9 @@ TEST_CASE("ResourceManager::allocSlot()") {
|
|||||||
REQUIRE(static_cast<VariantSlot*>(variant) == nullptr);
|
REQUIRE(static_cast<VariantSlot*>(variant) == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Returns zero if capacity is insufficient") {
|
SECTION("Returns null if pool allocation fails") {
|
||||||
ResourceManager resources(sizeof(VariantSlot));
|
TimebombAllocator allocator(1);
|
||||||
|
ResourceManager resources(4096, &allocator);
|
||||||
|
|
||||||
resources.allocSlot();
|
resources.allocSlot();
|
||||||
|
|
||||||
|
@ -6,25 +6,21 @@
|
|||||||
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
|
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
#include "Allocators.hpp"
|
||||||
|
|
||||||
using namespace ArduinoJson::detail;
|
using namespace ArduinoJson::detail;
|
||||||
|
|
||||||
TEST_CASE("ResourceManager::capacity()") {
|
|
||||||
const size_t capacity = 4 * sizeof(VariantSlot);
|
|
||||||
ResourceManager resources(capacity);
|
|
||||||
REQUIRE(capacity == resources.capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("ResourceManager::size()") {
|
TEST_CASE("ResourceManager::size()") {
|
||||||
ResourceManager resources(4096);
|
TimebombAllocator allocator(0);
|
||||||
|
ResourceManager resources(4096, &allocator);
|
||||||
|
|
||||||
SECTION("Initial size is 0") {
|
SECTION("Initial size is 0") {
|
||||||
REQUIRE(0 == resources.size());
|
REQUIRE(0 == resources.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Doesn't grow when memory pool is full") {
|
SECTION("Doesn't grow when allocation of second pool fails") {
|
||||||
const size_t variantCount = resources.capacity() / sizeof(VariantSlot);
|
allocator.setCountdown(2);
|
||||||
|
for (size_t i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
|
||||||
for (size_t i = 0; i < variantCount; i++)
|
|
||||||
resources.allocSlot();
|
resources.allocSlot();
|
||||||
size_t size = resources.size();
|
size_t size = resources.size();
|
||||||
|
|
||||||
|
@ -90,6 +90,26 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Capacity of each variant pool (in slots)
|
||||||
|
#ifndef ARDUINOJSON_POOL_CAPACITY
|
||||||
|
# if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ <= 2
|
||||||
|
// Address space == 16-bit
|
||||||
|
# define ARDUINOJSON_POOL_CAPACITY 16 // 128 bytes
|
||||||
|
# elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ >= 8 || \
|
||||||
|
defined(_WIN64) && _WIN64
|
||||||
|
// Address space == 64-bit
|
||||||
|
# define ARDUINOJSON_POOL_CAPACITY 128 // 3072 bytes
|
||||||
|
# else
|
||||||
|
// Address space == 32-bit
|
||||||
|
# define ARDUINOJSON_POOL_CAPACITY 64 // 1024 bytes
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Capacity of each variant pool (in slots)
|
||||||
|
#ifndef ARDUINOJSON_INITIAL_POOL_COUNT
|
||||||
|
# define ARDUINOJSON_INITIAL_POOL_COUNT 4
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ARDUINO
|
#ifdef ARDUINO
|
||||||
|
|
||||||
// Enable support for Arduino's String class
|
// Enable support for Arduino's String class
|
||||||
|
@ -94,7 +94,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
|||||||
bool garbageCollect() {
|
bool garbageCollect() {
|
||||||
// make a temporary clone and move assign
|
// make a temporary clone and move assign
|
||||||
JsonDocument tmp(*this);
|
JsonDocument tmp(*this);
|
||||||
if (!tmp.resources_.capacity())
|
if (tmp.overflowed())
|
||||||
return false;
|
return false;
|
||||||
moveAssignFrom(tmp);
|
moveAssignFrom(tmp);
|
||||||
return true;
|
return true;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include <ArduinoJson/Memory/Allocator.hpp>
|
#include <ArduinoJson/Memory/Allocator.hpp>
|
||||||
#include <ArduinoJson/Memory/StringPool.hpp>
|
#include <ArduinoJson/Memory/StringPool.hpp>
|
||||||
#include <ArduinoJson/Memory/VariantPool.hpp>
|
#include <ArduinoJson/Memory/VariantPoolList.hpp>
|
||||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||||
#include <ArduinoJson/Polyfills/utility.hpp>
|
#include <ArduinoJson/Polyfills/utility.hpp>
|
||||||
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
||||||
@ -18,15 +18,13 @@ class VariantPool;
|
|||||||
|
|
||||||
class ResourceManager {
|
class ResourceManager {
|
||||||
public:
|
public:
|
||||||
ResourceManager(size_t capa,
|
ResourceManager(size_t /*capa*/,
|
||||||
Allocator* allocator = DefaultAllocator::instance())
|
Allocator* allocator = DefaultAllocator::instance())
|
||||||
: allocator_(allocator), overflowed_(false) {
|
: allocator_(allocator), overflowed_(false) {}
|
||||||
variantPool_.create(capa, allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
~ResourceManager() {
|
~ResourceManager() {
|
||||||
stringPool_.clear(allocator_);
|
stringPool_.clear(allocator_);
|
||||||
variantPool_.destroy(allocator_);
|
variantPools_.clear(allocator_);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceManager(const ResourceManager&) = delete;
|
ResourceManager(const ResourceManager&) = delete;
|
||||||
@ -34,9 +32,9 @@ class ResourceManager {
|
|||||||
|
|
||||||
ResourceManager& operator=(ResourceManager&& src) {
|
ResourceManager& operator=(ResourceManager&& src) {
|
||||||
stringPool_.clear(allocator_);
|
stringPool_.clear(allocator_);
|
||||||
variantPool_.destroy(allocator_);
|
variantPools_.clear(allocator_);
|
||||||
allocator_ = src.allocator_;
|
allocator_ = src.allocator_;
|
||||||
variantPool_ = detail::move(src.variantPool_);
|
variantPools_ = detail::move(src.variantPools_);
|
||||||
overflowed_ = src.overflowed_;
|
overflowed_ = src.overflowed_;
|
||||||
stringPool_ = detail::move(src.stringPool_);
|
stringPool_ = detail::move(src.stringPool_);
|
||||||
return *this;
|
return *this;
|
||||||
@ -48,19 +46,19 @@ class ResourceManager {
|
|||||||
|
|
||||||
void reallocPool(size_t requiredSize) {
|
void reallocPool(size_t requiredSize) {
|
||||||
size_t capa = VariantPool::bytesToSlots(requiredSize);
|
size_t capa = VariantPool::bytesToSlots(requiredSize);
|
||||||
if (capa == variantPool_.capacity())
|
if (capa == variantPools_.capacity())
|
||||||
return;
|
return;
|
||||||
variantPool_.destroy(allocator_);
|
variantPools_.clear(allocator_);
|
||||||
variantPool_.create(requiredSize, allocator_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the capacity of the memoryPool in bytes
|
// Gets the capacity of the memoryPool in bytes
|
||||||
size_t capacity() const {
|
size_t capacity() const {
|
||||||
return VariantPool::slotsToBytes(variantPool_.capacity());
|
return VariantPool::slotsToBytes(variantPools_.capacity());
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size() const {
|
size_t size() const {
|
||||||
return VariantPool::slotsToBytes(variantPool_.usage()) + stringPool_.size();
|
return VariantPool::slotsToBytes(variantPools_.usage()) +
|
||||||
|
stringPool_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool overflowed() const {
|
bool overflowed() const {
|
||||||
@ -68,14 +66,14 @@ class ResourceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SlotWithId allocSlot() {
|
SlotWithId allocSlot() {
|
||||||
auto p = variantPool_.allocSlot();
|
auto p = variantPools_.allocSlot(allocator_);
|
||||||
if (!p)
|
if (!p)
|
||||||
overflowed_ = true;
|
overflowed_ = true;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
VariantSlot* getSlot(SlotId id) const {
|
VariantSlot* getSlot(SlotId id) const {
|
||||||
return variantPool_.getSlot(id);
|
return variantPools_.getSlot(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
@ -122,20 +120,20 @@ class ResourceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
variantPool_.clear();
|
variantPools_.clear(allocator_);
|
||||||
overflowed_ = false;
|
overflowed_ = false;
|
||||||
stringPool_.clear(allocator_);
|
stringPool_.clear(allocator_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void shrinkToFit() {
|
void shrinkToFit() {
|
||||||
variantPool_.shrinkToFit(allocator_);
|
variantPools_.shrinkToFit(allocator_);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Allocator* allocator_;
|
Allocator* allocator_;
|
||||||
bool overflowed_;
|
bool overflowed_;
|
||||||
StringPool stringPool_;
|
StringPool stringPool_;
|
||||||
VariantPool variantPool_;
|
VariantPoolList variantPools_;
|
||||||
};
|
};
|
||||||
|
|
||||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||||
|
@ -31,6 +31,7 @@ class StringBuilder {
|
|||||||
StringNode* node = resources_->getString(adaptString(node_->data, size_));
|
StringNode* node = resources_->getString(adaptString(node_->data, size_));
|
||||||
if (!node) {
|
if (!node) {
|
||||||
node = resources_->resizeString(node_, size_);
|
node = resources_->resizeString(node_, size_);
|
||||||
|
ARDUINOJSON_ASSERT(node != nullptr); // realloc to smaller can't fail
|
||||||
resources_->saveString(node);
|
resources_->saveString(node);
|
||||||
node_ = nullptr; // next time we need a new string
|
node_ = nullptr; // next time we need a new string
|
||||||
} else {
|
} else {
|
||||||
|
@ -42,21 +42,7 @@ class SlotWithId {
|
|||||||
|
|
||||||
class VariantPool {
|
class VariantPool {
|
||||||
public:
|
public:
|
||||||
~VariantPool() {
|
void create(SlotCount cap, Allocator* allocator);
|
||||||
ARDUINOJSON_ASSERT(slots_ == nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
VariantPool& operator=(VariantPool&& src) {
|
|
||||||
capacity_ = src.capacity_;
|
|
||||||
src.capacity_ = 0;
|
|
||||||
usage_ = src.usage_;
|
|
||||||
src.usage_ = 0;
|
|
||||||
slots_ = src.slots_;
|
|
||||||
src.slots_ = nullptr;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void create(size_t cap, Allocator* allocator);
|
|
||||||
void destroy(Allocator* allocator);
|
void destroy(Allocator* allocator);
|
||||||
|
|
||||||
SlotWithId allocSlot();
|
SlotWithId allocSlot();
|
||||||
@ -70,9 +56,9 @@ class VariantPool {
|
|||||||
static size_t slotsToBytes(SlotCount);
|
static size_t slotsToBytes(SlotCount);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SlotCount capacity_ = 0;
|
SlotCount capacity_;
|
||||||
SlotCount usage_ = 0;
|
SlotCount usage_;
|
||||||
VariantSlot* slots_ = nullptr;
|
VariantSlot* slots_;
|
||||||
};
|
};
|
||||||
|
|
||||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||||
|
@ -9,15 +9,12 @@
|
|||||||
|
|
||||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||||
|
|
||||||
inline void VariantPool::create(size_t cap, Allocator* allocator) {
|
inline void VariantPool::create(SlotCount cap, Allocator* allocator) {
|
||||||
ARDUINOJSON_ASSERT(slots_ == nullptr);
|
ARDUINOJSON_ASSERT(cap > 0);
|
||||||
if (!cap)
|
slots_ =
|
||||||
return;
|
reinterpret_cast<VariantSlot*>(allocator->allocate(slotsToBytes(cap)));
|
||||||
slots_ = reinterpret_cast<VariantSlot*>(allocator->allocate(cap));
|
capacity_ = slots_ ? cap : 0;
|
||||||
if (slots_) {
|
usage_ = 0;
|
||||||
capacity_ = bytesToSlots(cap);
|
|
||||||
usage_ = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void VariantPool::destroy(Allocator* allocator) {
|
inline void VariantPool::destroy(Allocator* allocator) {
|
||||||
|
133
src/ArduinoJson/Memory/VariantPoolList.hpp
Normal file
133
src/ArduinoJson/Memory/VariantPoolList.hpp
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
// ArduinoJson - https://arduinojson.org
|
||||||
|
// Copyright © 2014-2023, Benoit BLANCHON
|
||||||
|
// MIT License
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ArduinoJson/Memory/VariantPool.hpp>
|
||||||
|
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||||
|
|
||||||
|
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||||
|
|
||||||
|
class VariantPoolList {
|
||||||
|
public:
|
||||||
|
VariantPoolList() = default;
|
||||||
|
|
||||||
|
~VariantPoolList() {
|
||||||
|
ARDUINOJSON_ASSERT(pools_ == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
VariantPoolList& operator=(VariantPoolList&& src) {
|
||||||
|
ARDUINOJSON_ASSERT(pools_ == nullptr);
|
||||||
|
pools_ = src.pools_;
|
||||||
|
count_ = src.count_;
|
||||||
|
capacity_ = src.capacity_;
|
||||||
|
src.pools_ = nullptr;
|
||||||
|
src.count_ = 0;
|
||||||
|
src.capacity_ = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SlotWithId allocSlot(Allocator* allocator) {
|
||||||
|
// try to allocate from last pool (other pools are full)
|
||||||
|
if (pools_) {
|
||||||
|
auto slot = allocFromLastPool();
|
||||||
|
if (slot)
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new pool and try again
|
||||||
|
auto pool = addPool(allocator);
|
||||||
|
if (!pool)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return allocFromLastPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
VariantSlot* getSlot(SlotId id) const {
|
||||||
|
auto poolIndex = SlotId(id / ARDUINOJSON_POOL_CAPACITY);
|
||||||
|
auto indexInPool = SlotId(id % ARDUINOJSON_POOL_CAPACITY);
|
||||||
|
if (poolIndex >= count_)
|
||||||
|
return nullptr;
|
||||||
|
return pools_[poolIndex].getSlot(indexInPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear(Allocator* allocator) {
|
||||||
|
if (!pools_)
|
||||||
|
return;
|
||||||
|
for (size_t i = 0; i < count_; i++)
|
||||||
|
pools_[i].destroy(allocator);
|
||||||
|
allocator->deallocate(pools_);
|
||||||
|
pools_ = nullptr;
|
||||||
|
count_ = 0;
|
||||||
|
capacity_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SlotCount capacity() const {
|
||||||
|
SlotCount total = 0;
|
||||||
|
for (size_t i = 0; i < count_; i++)
|
||||||
|
total = SlotCount(total + pools_[i].capacity());
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
SlotCount usage() const {
|
||||||
|
SlotCount total = 0;
|
||||||
|
for (size_t i = 0; i < count_; i++)
|
||||||
|
total = SlotCount(total + pools_[i].usage());
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shrinkToFit(Allocator* allocator) {
|
||||||
|
if (pools_)
|
||||||
|
pools_[count_ - 1].shrinkToFit(allocator);
|
||||||
|
if (count_ != capacity_) {
|
||||||
|
pools_ = static_cast<VariantPool*>(
|
||||||
|
allocator->reallocate(pools_, count_ * sizeof(VariantPool)));
|
||||||
|
ARDUINOJSON_ASSERT(pools_ != nullptr); // realloc to smaller can't fail
|
||||||
|
capacity_ = count_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SlotWithId allocFromLastPool() {
|
||||||
|
ARDUINOJSON_ASSERT(pools_ != nullptr);
|
||||||
|
auto poolIndex = SlotId(count_ - 1);
|
||||||
|
auto lastPool = count_ ? &pools_[poolIndex] : nullptr;
|
||||||
|
auto slot = lastPool->allocSlot();
|
||||||
|
if (!slot)
|
||||||
|
return {};
|
||||||
|
return {slot, SlotId(poolIndex * ARDUINOJSON_POOL_CAPACITY + slot.id())};
|
||||||
|
}
|
||||||
|
|
||||||
|
VariantPool* addPool(Allocator* allocator) {
|
||||||
|
if (count_ == capacity_ && !increaseCapacity(allocator))
|
||||||
|
return nullptr;
|
||||||
|
auto pool = &pools_[count_++];
|
||||||
|
pool->create(ARDUINOJSON_POOL_CAPACITY, allocator);
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool increaseCapacity(Allocator* allocator) {
|
||||||
|
void* newPools;
|
||||||
|
size_t newCapacity;
|
||||||
|
if (pools_) {
|
||||||
|
newCapacity = capacity_ * 2;
|
||||||
|
newPools =
|
||||||
|
allocator->reallocate(pools_, newCapacity * sizeof(VariantPool));
|
||||||
|
} else {
|
||||||
|
newCapacity = ARDUINOJSON_INITIAL_POOL_COUNT;
|
||||||
|
newPools = allocator->allocate(newCapacity * sizeof(VariantPool));
|
||||||
|
}
|
||||||
|
if (!newPools)
|
||||||
|
return false;
|
||||||
|
pools_ = static_cast<VariantPool*>(newPools);
|
||||||
|
capacity_ = newCapacity;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VariantPool* pools_ = nullptr;
|
||||||
|
size_t count_ = 0;
|
||||||
|
size_t capacity_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
Reference in New Issue
Block a user