JsonArray::remove() and JsonObject::remove() now release the memory of strings

This commit is contained in:
Benoit Blanchon
2018-11-09 17:27:32 +01:00
parent e842838a23
commit f375459d53
68 changed files with 1504 additions and 740 deletions

View File

@ -2,12 +2,14 @@
# Copyright Benoit Blanchon 2014-2018
# MIT License
add_executable(DynamicMemoryPoolTests
alloc.cpp
allocSlot.cpp
add_executable(DynamicMemoryPoolTests
allocString.cpp
allocVariant.cpp
blocks.cpp
clear.cpp
no_memory.cpp
size.cpp
startString.cpp
StringBuilder.cpp
)
target_link_libraries(DynamicMemoryPoolTests catch)

View File

@ -0,0 +1,41 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
#include <ArduinoJson/Memory/StringBuilder.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("DynamicMemoryPool::startString()") {
SECTION("WorksWhenBufferIsBigEnough") {
DynamicMemoryPool memoryPool(JSON_STRING_SIZE(8));
StringBuilder str(&memoryPool);
str.append("abcdefg");
REQUIRE(memoryPool.blockCount() == 1);
REQUIRE(str.complete().equals("abcdefg"));
}
SECTION("GrowsWhenBufferIsTooSmall") {
DynamicMemoryPool memoryPool(JSON_STRING_SIZE(8));
StringBuilder str(&memoryPool);
str.append("abcdefghABC");
REQUIRE(memoryPool.blockCount() == 2);
REQUIRE(str.complete().equals("abcdefghABC"));
}
SECTION("SizeIncreases") {
DynamicMemoryPool memoryPool(JSON_STRING_SIZE(5));
StringBuilder str(&memoryPool);
str.append('h');
str.complete();
REQUIRE(JSON_STRING_SIZE(2) == memoryPool.size());
}
}

View File

@ -1,77 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <sstream>
using namespace ARDUINOJSON_NAMESPACE;
static bool isAligned(void* ptr) {
const size_t mask = sizeof(void*) - 1;
size_t addr = reinterpret_cast<size_t>(ptr);
return (addr & mask) == 0;
}
std::stringstream allocatorLog;
struct SpyingAllocator : DefaultAllocator {
void* allocate(size_t n) {
allocatorLog << "A" << (n - DynamicMemoryPool::EmptyBlockSize);
return DefaultAllocator::allocate(n);
}
void deallocate(void* p) {
allocatorLog << "F";
return DefaultAllocator::deallocate(p);
}
};
TEST_CASE("DynamicMemoryPool::alloc()") {
SECTION("Returns different pointers") {
DynamicMemoryPool memoryPool;
void* p1 = memoryPool.alloc(1);
void* p2 = memoryPool.alloc(2);
REQUIRE(p1 != p2);
}
SECTION("Doubles allocation size when full") {
allocatorLog.str("");
{
DynamicMemoryPoolBase<SpyingAllocator> memoryPool(1);
memoryPool.alloc(1);
memoryPool.alloc(1);
}
REQUIRE(allocatorLog.str() == "A1A2FF");
}
SECTION("Resets allocation size after clear()") {
allocatorLog.str("");
{
DynamicMemoryPoolBase<SpyingAllocator> memoryPool(1);
memoryPool.alloc(1);
memoryPool.alloc(1);
memoryPool.clear();
memoryPool.alloc(1);
}
REQUIRE(allocatorLog.str() == "A1A2FFA1F");
}
SECTION("Makes a big allocation when needed") {
allocatorLog.str("");
{
DynamicMemoryPoolBase<SpyingAllocator> memoryPool(1);
memoryPool.alloc(42);
}
REQUIRE(allocatorLog.str() == "A42F");
}
SECTION("Alignment") {
// make room for two but not three
DynamicMemoryPool tinyBuf(2 * sizeof(void*) + 1);
REQUIRE(isAligned(tinyBuf.alloc(1))); // this on is aligned by design
REQUIRE(isAligned(tinyBuf.alloc(1))); // this one fits in the first block
REQUIRE(isAligned(tinyBuf.alloc(1))); // this one requires a new block
}
}

View File

@ -1,27 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("DynamicMemoryPool::allocSlot()") {
DynamicMemoryPool memoryPool;
SECTION("Returns different pointer") {
Slot* s1 = memoryPool.allocSlot();
Slot* s2 = memoryPool.allocSlot();
REQUIRE(s1 != s2);
}
SECTION("Returns same pointer after freeSlot()") {
Slot* s1 = memoryPool.allocSlot();
memoryPool.freeSlot(s1);
Slot* s2 = memoryPool.allocSlot();
REQUIRE(s1 == s2);
}
}

View File

@ -0,0 +1,28 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
#include <catch.hpp>
#include <sstream>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("DynamicMemoryPool::allocFrozenString()") {
DynamicMemoryPool pool;
SECTION("Returns different pointers") {
StringSlot* a = pool.allocFrozenString(1);
StringSlot* b = pool.allocFrozenString(2);
REQUIRE(a != b);
REQUIRE(a->value != b->value);
}
SECTION("Returns same slot after freeString") {
StringSlot* a = pool.allocFrozenString(1);
pool.freeString(a);
StringSlot* b = pool.allocFrozenString(2);
REQUIRE(a == b);
REQUIRE(a->value == b->value);
}
}

View File

@ -0,0 +1,41 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("DynamicMemoryPool::allocVariant()") {
DynamicMemoryPool memoryPool;
SECTION("Returns different pointer") {
VariantSlot* s1 = memoryPool.allocVariant();
VariantSlot* s2 = memoryPool.allocVariant();
REQUIRE(s1 != s2);
}
SECTION("Returns same pointer after freeSlot()") {
VariantSlot* s1 = memoryPool.allocVariant();
memoryPool.freeVariant(s1);
VariantSlot* s2 = memoryPool.allocVariant();
REQUIRE(s1 == s2);
}
SECTION("Returns aligned pointers") {
// make room for two but not three
// pass an uneven capacity
DynamicMemoryPool pool(2 * sizeof(VariantSlot) + 1);
REQUIRE(isAligned(pool.allocVariant()));
REQUIRE(isAligned(pool.allocVariant()));
REQUIRE(pool.blockCount() == 1);
REQUIRE(isAligned(pool.allocVariant()));
REQUIRE(isAligned(pool.allocVariant()));
REQUIRE(pool.blockCount() == 2);
}
}

View File

@ -0,0 +1,69 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
#include <catch.hpp>
#include <sstream>
using namespace ARDUINOJSON_NAMESPACE;
std::stringstream allocatorLog;
struct SpyingAllocator : DefaultAllocator {
void* allocate(size_t n) {
allocatorLog << "A" << (n - DynamicMemoryPool::EmptyBlockSize);
return DefaultAllocator::allocate(n);
}
void deallocate(void* p) {
allocatorLog << "F";
return DefaultAllocator::deallocate(p);
}
};
TEST_CASE("DynamicMemoryPool blocks") {
SECTION("Doubles allocation size when full") {
allocatorLog.str("");
{
DynamicMemoryPoolBase<SpyingAllocator> memoryPool(sizeof(VariantSlot));
memoryPool.allocVariant();
memoryPool.allocVariant();
}
std::stringstream expected;
expected << "A" << sizeof(VariantSlot) // block 1
<< "A" << 2 * sizeof(VariantSlot) // block 2, twice bigger
<< "FF";
REQUIRE(allocatorLog.str() == expected.str());
}
SECTION("Resets allocation size after clear()") {
allocatorLog.str("");
{
DynamicMemoryPoolBase<SpyingAllocator> memoryPool(sizeof(VariantSlot));
memoryPool.allocVariant();
memoryPool.allocVariant();
memoryPool.clear();
memoryPool.allocVariant();
}
std::stringstream expected;
expected << "A" << sizeof(VariantSlot) // block 1
<< "A" << 2 * sizeof(VariantSlot) // block 2, twice bigger
<< "FF" // clear
<< "A" << sizeof(VariantSlot) // block 1
<< "F";
REQUIRE(allocatorLog.str() == expected.str());
}
/* SECTION("Alloc big block for large string") {
allocatorLog.str("");
{
DynamicMemoryPoolBase<SpyingAllocator> memoryPool(1);
memoryPool.allocString(42);
}
std::stringstream expected;
expected << "A" << JSON_STRING_SIZE(42) // block 1
<< "F";
REQUIRE(allocatorLog.str() == expected.str());
}*/
}

View File

@ -0,0 +1,47 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("StaticMemoryPool::clear()") {
DynamicMemoryPool memoryPool;
SECTION("Discards allocated variants") {
memoryPool.allocVariant();
REQUIRE(memoryPool.size() > 0);
memoryPool.clear();
CHECK(memoryPool.size() == 0);
}
SECTION("Discards allocated strings") {
memoryPool.allocFrozenString(10);
REQUIRE(memoryPool.size() > 0);
memoryPool.clear();
CHECK(memoryPool.size() == 0);
}
SECTION("Purges variant cache") {
memoryPool.freeVariant(memoryPool.allocVariant());
REQUIRE(memoryPool.size() == 0);
memoryPool.clear();
CHECK(memoryPool.size() == 0);
}
SECTION("Purges string cache") {
memoryPool.freeString(memoryPool.allocFrozenString(10));
// REQUIRE(memoryPool.size() == 0);
memoryPool.clear();
CHECK(memoryPool.size() == 0);
}
}

View File

@ -2,7 +2,8 @@
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
#include <ArduinoJson/Memory/StringBuilder.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
@ -32,8 +33,8 @@ TEST_CASE("DynamicMemoryPool no memory") {
// REQUIRE(err != DeserializationError::Ok);
// }
SECTION("startString()") {
StringBuilder str = _memoryPool.startString();
SECTION("StringBuilder returns null") {
StringBuilder str(&_memoryPool);
str.append('!');
REQUIRE(str.complete().isNull());
}

View File

@ -14,35 +14,43 @@ TEST_CASE("DynamicMemoryPool::size()") {
REQUIRE(0 == memoryPool.size());
}
SECTION("Increases after alloc()") {
memoryPool.alloc(1);
REQUIRE(1U <= memoryPool.size());
memoryPool.alloc(1);
REQUIRE(2U <= memoryPool.size());
SECTION("Increases after allocExpandableString()") {
StringSlot* a = memoryPool.allocExpandableString();
memoryPool.freezeString(a, 1);
REQUIRE(memoryPool.size() == JSON_STRING_SIZE(1));
StringSlot* b = memoryPool.allocExpandableString();
memoryPool.freezeString(b, 1);
REQUIRE(memoryPool.size() == 2 * JSON_STRING_SIZE(1));
}
SECTION("Goes back to 0 after clear()") {
memoryPool.alloc(1);
memoryPool.clear();
SECTION("Increases after allocVariant()") {
memoryPool.allocVariant();
REQUIRE(sizeof(VariantSlot) == memoryPool.size());
memoryPool.allocVariant();
REQUIRE(2 * sizeof(VariantSlot) == memoryPool.size());
}
SECTION("Decreases after freeVariant()") {
VariantSlot* a = memoryPool.allocVariant();
VariantSlot* b = memoryPool.allocVariant();
memoryPool.freeVariant(b);
REQUIRE(sizeof(VariantSlot) == memoryPool.size());
memoryPool.freeVariant(a);
REQUIRE(0 == memoryPool.size());
}
SECTION("Increases after allocSlot()") {
memoryPool.allocSlot();
REQUIRE(sizeof(Slot) == memoryPool.size());
SECTION("Decreases after freeString()") {
StringSlot* a = memoryPool.allocFrozenString(5);
StringSlot* b = memoryPool.allocFrozenString(6);
memoryPool.allocSlot();
REQUIRE(2 * sizeof(Slot) == memoryPool.size());
}
memoryPool.freeString(b);
REQUIRE(memoryPool.size() == JSON_STRING_SIZE(5));
SECTION("Decreases after freeSlot()") {
Slot* s1 = memoryPool.allocSlot();
Slot* s2 = memoryPool.allocSlot();
memoryPool.freeSlot(s1);
REQUIRE(sizeof(Slot) == memoryPool.size());
memoryPool.freeSlot(s2);
REQUIRE(0 == memoryPool.size());
memoryPool.freeString(a);
REQUIRE(memoryPool.size() == 0);
}
}

View File

@ -1,46 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("DynamicMemoryPool::startString()") {
SECTION("WorksWhenBufferIsBigEnough") {
DynamicMemoryPool memoryPool(6);
StringBuilder str = memoryPool.startString();
str.append('h');
str.append('e');
str.append('l');
str.append('l');
str.append('o');
REQUIRE(str.complete().equals("hello"));
}
SECTION("GrowsWhenBufferIsTooSmall") {
DynamicMemoryPool memoryPool(5);
StringBuilder str = memoryPool.startString();
str.append('h');
str.append('e');
str.append('l');
str.append('l');
str.append('o');
REQUIRE(str.complete().equals("hello"));
}
SECTION("SizeIncreases") {
DynamicMemoryPool memoryPool(5);
StringBuilder str = memoryPool.startString();
str.append('h');
str.complete();
REQUIRE(2 == memoryPool.size());
}
}

View File

@ -102,13 +102,13 @@ TEST_CASE("JsonArray::add()") {
SECTION("should duplicate char*") {
array.add(const_cast<char*>("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate std::string") {
array.add(std::string("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
REQUIRE(expectedSize == doc.memoryUsage());
}
@ -120,19 +120,19 @@ TEST_CASE("JsonArray::add()") {
SECTION("should duplicate serialized(char*)") {
array.add(serialized(const_cast<char*>("{}")));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 2;
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(2);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate serialized(std::string)") {
array.add(serialized(std::string("{}")));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 2;
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(2);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate serialized(std::string)") {
array.add(serialized(std::string("\0XX", 3)));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 3;
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(3);
REQUIRE(expectedSize == doc.memoryUsage());
}
}

View File

@ -105,13 +105,13 @@ TEST_CASE("JsonArray::operator[]") {
SECTION("should duplicate char*") {
array[0] = const_cast<char*>("world");
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate std::string") {
array[0] = std::string("world");
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
REQUIRE(expectedSize == doc.memoryUsage());
}

View File

@ -16,7 +16,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") {
}
SECTION("TooSmallBufferForArrayWithOneValue") {
StaticJsonDocument<JSON_ARRAY_SIZE(1) - 1> doc;
StaticJsonDocument<JSON_ARRAY_SIZE(0)> doc;
char input[] = "[1]";
DeserializationError err = deserializeJson(doc, input);
@ -34,7 +34,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") {
}
SECTION("TooSmallBufferForArrayWithNestedObject") {
StaticJsonDocument<JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(0) - 1> doc;
StaticJsonDocument<JSON_ARRAY_SIZE(0) + JSON_OBJECT_SIZE(0)> doc;
char input[] = "[{}]";
DeserializationError err = deserializeJson(doc, input);
@ -56,7 +56,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") {
deserializeJson(doc, " [ \"1234567\" ] ");
REQUIRE(JSON_ARRAY_SIZE(1) + sizeof("1234567") == doc.memoryUsage());
REQUIRE(JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(8) == doc.memoryUsage());
// note: we use a string of 8 bytes to be sure that the StaticMemoryPool
// will not insert bytes to enforce alignement
}

View File

@ -16,7 +16,7 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") {
}
SECTION("TooSmallBufferForObjectWithOneValue") {
StaticJsonDocument<JSON_OBJECT_SIZE(1) - 1> doc;
StaticJsonDocument<JSON_OBJECT_SIZE(0)> doc;
char input[] = "{\"a\":1}";
DeserializationError err = deserializeJson(doc, input);
@ -34,7 +34,7 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") {
}
SECTION("TooSmallBufferForObjectWithNestedObject") {
StaticJsonDocument<JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(0) - 1> doc;
StaticJsonDocument<JSON_OBJECT_SIZE(0) + JSON_ARRAY_SIZE(0)> doc;
char input[] = "{\"a\":[]}";
DeserializationError err = deserializeJson(doc, input);

View File

@ -94,35 +94,36 @@ TEST_CASE("DynamicJsonDocument") {
}
SECTION("memoryUsage()") {
typedef ARDUINOJSON_NAMESPACE::Slot Slot;
SECTION("Increases after adding value to array") {
JsonArray arr = doc.to<JsonArray>();
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0));
arr.add(42);
REQUIRE(sizeof(Slot) == doc.memoryUsage());
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1));
arr.add(43);
REQUIRE(2 * sizeof(Slot) == doc.memoryUsage());
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(2));
}
SECTION("Decreases after remove value from array") {
SECTION("Decreases after removing value from array") {
JsonArray arr = doc.to<JsonArray>();
arr.add(42);
arr.add(43);
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(2));
arr.remove(1);
REQUIRE(sizeof(Slot) == doc.memoryUsage());
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1));
arr.remove(0);
REQUIRE(0 == doc.memoryUsage());
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0));
}
SECTION("Increases after adding value to object") {
JsonObject obj = doc.to<JsonObject>();
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
obj["a"] = 1;
REQUIRE(sizeof(Slot) == doc.memoryUsage());
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1));
obj["b"] = 2;
REQUIRE(2 * sizeof(Slot) == doc.memoryUsage());
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2));
}
SECTION("Decreases after removing value from object") {
@ -130,10 +131,11 @@ TEST_CASE("DynamicJsonDocument") {
obj["a"] = 1;
obj["b"] = 2;
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2));
obj.remove("a");
REQUIRE(sizeof(Slot) == doc.memoryUsage());
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1));
obj.remove("b");
REQUIRE(0 == doc.memoryUsage());
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
}
SECTION("Decreases after removing nested object from array") {
@ -141,8 +143,9 @@ TEST_CASE("DynamicJsonDocument") {
JsonObject obj = arr.createNestedObject();
obj["hello"] = "world";
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1));
arr.remove(0);
REQUIRE(0 == doc.memoryUsage());
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0));
}
SECTION("Decreases after removing nested array from object") {
@ -150,8 +153,9 @@ TEST_CASE("DynamicJsonDocument") {
JsonArray arr = obj.createNestedArray("hello");
arr.add("world");
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1));
obj.remove("hello");
REQUIRE(0 == doc.memoryUsage());
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
}
}
}

View File

@ -19,18 +19,47 @@ TEST_CASE("JsonObject::remove()") {
obj.remove("a");
serializeJson(obj, result);
REQUIRE("{\"b\":1,\"c\":2}" == result);
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2));
}
SECTION("Remove middle") {
obj.remove("b");
serializeJson(obj, result);
REQUIRE("{\"a\":0,\"c\":2}" == result);
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2));
}
SECTION("Remove last") {
obj.remove("c");
serializeJson(obj, result);
REQUIRE("{\"a\":0,\"b\":1}" == result);
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2));
}
SECTION("Release value string memory") {
obj["c"] = std::string("Copy me!");
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(3) + JSON_STRING_SIZE(9));
obj.remove("c");
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2));
}
SECTION("Release key string memory") {
obj[std::string("Copy me!")] = 42;
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(4) + JSON_STRING_SIZE(9));
obj.remove("Copy me!");
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(3));
}
SECTION("Release raw string memory") {
obj["c"] = serialized(std::string("Copy me!"));
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(3) + JSON_STRING_SIZE(8));
obj.remove("c");
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2));
}
}

View File

@ -107,37 +107,37 @@ TEST_CASE("JsonObject::operator[]") {
SECTION("should duplicate char* value") {
obj["hello"] = const_cast<char*>("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate char* key") {
obj[const_cast<char*>("hello")] = "world";
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate char* key&value") {
obj[const_cast<char*>("hello")] = const_cast<char*>("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12;
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(6);
REQUIRE(expectedSize <= doc.memoryUsage());
}
SECTION("should duplicate std::string value") {
obj["hello"] = std::string("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate std::string key") {
obj[std::string("hello")] = "world";
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate std::string key&value") {
obj[std::string("hello")] = std::string("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12;
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(6);
REQUIRE(expectedSize <= doc.memoryUsage());
}

View File

@ -108,6 +108,16 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<std::string>() == std::string("hello"));
}
SECTION("set(std::string(\"4.2\"))") {
variant.set(std::string("4.2"));
REQUIRE(variant.as<bool>() == true);
REQUIRE(variant.as<long>() == 4L);
REQUIRE(variant.as<double>() == 4.2);
REQUIRE(variant.as<char*>() == std::string("4.2"));
REQUIRE(variant.as<std::string>() == std::string("4.2"));
}
SECTION("set(\"true\")") {
variant.set("true");

View File

@ -216,7 +216,7 @@ TEST_CASE("JsonVariant comparisons") {
JsonVariant variant2 = doc2.to<JsonVariant>();
JsonVariant variant3 = doc3.to<JsonVariant>();
SECTION("IntegerInVariant") {
SECTION("Variants containing integers") {
variant1.set(42);
variant2.set(42);
variant3.set(666);
@ -228,7 +228,7 @@ TEST_CASE("JsonVariant comparisons") {
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("StringInVariant") {
SECTION("Variants containing linked strings") {
variant1.set("0hello" + 1); // make sure they have
variant2.set("1hello" + 1); // different addresses
variant3.set("world");
@ -240,7 +240,19 @@ TEST_CASE("JsonVariant comparisons") {
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("DoubleInVariant") {
SECTION("Variants containing owned strings") {
variant1.set(std::string("hello"));
variant2.set(std::string("hello"));
variant3.set(std::string("world"));
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("Variants containing double") {
variant1.set(42.0);
variant2.set(42.0);
variant3.set(666.0);

View File

@ -13,22 +13,24 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
SECTION("stores JsonArray by copy") {
JsonArray arr = doc2.to<JsonArray>();
arr.add(42);
JsonObject obj = arr.createNestedObject();
obj["hello"] = "world";
var1.set(arr);
arr[0] = 666;
REQUIRE(var1.as<std::string>() == "[42]");
REQUIRE(var1.as<std::string>() == "[{\"hello\":\"world\"}]");
}
SECTION("stores JsonObject by copy") {
JsonObject obj = doc2.to<JsonObject>();
obj["value"] = 42;
JsonArray arr = obj.createNestedArray("value");
arr.add(42);
var1.set(obj);
obj["value"] = 666;
REQUIRE(var1.as<std::string>() == "{\"value\":42}");
REQUIRE(var1.as<std::string>() == "{\"value\":[42]}");
}
SECTION("stores const char* by reference") {
@ -45,20 +47,20 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
var1.set(str);
var2.set(var1);
REQUIRE(doc1.memoryUsage() == 8);
REQUIRE(doc2.memoryUsage() == 8);
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
}
SECTION("stores std::string by copy") {
var1.set(std::string("hello!!"));
var2.set(var1);
REQUIRE(doc1.memoryUsage() == 8);
REQUIRE(doc2.memoryUsage() == 8);
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
}
SECTION("stores Serialized<const char*> by reference") {
var1.set(serialized("hello!!", 8));
var1.set(serialized("hello!!", JSON_STRING_SIZE(8)));
var2.set(var1);
REQUIRE(doc1.memoryUsage() == 0);
@ -70,15 +72,15 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
var1.set(serialized(str, 8));
var2.set(var1);
REQUIRE(doc1.memoryUsage() == 8);
REQUIRE(doc2.memoryUsage() == 8);
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
}
SECTION("stores Serialized<std::string> by copy") {
var1.set(serialized(std::string("hello!!!")));
var2.set(var1);
REQUIRE(doc1.memoryUsage() == 8);
REQUIRE(doc2.memoryUsage() == 8);
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
}
}

View File

@ -5,17 +5,21 @@
#include <ArduinoJson.h>
#include <catch.hpp>
static const size_t epsilon = sizeof(void*);
template <size_t Capacity>
static void check(const char* input, DeserializationError expected) {
StaticJsonDocument<Capacity> variant;
DeserializationError error = deserializeMsgPack(variant, input);
CAPTURE(input);
REQUIRE(error == expected);
}
template <size_t Size>
static void checkString(const char* input, DeserializationError expected) {
check<JSON_STRING_SIZE(Size)>(input, expected);
}
TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") {
SECTION("single values always fit") {
check<0>("\xc0", DeserializationError::Ok); // nil
@ -27,31 +31,39 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") {
}
SECTION("fixstr") {
check<0>("\xA0", DeserializationError::Ok);
check<0>("\xA1H", DeserializationError::NoMemory);
check<4>("\xA1H", DeserializationError::Ok);
check<4>("\xA5Hello", DeserializationError::NoMemory);
checkString<8>("\xA0", DeserializationError::Ok);
checkString<8>("\xA7ZZZZZZZ", DeserializationError::Ok);
checkString<8>("\xA8ZZZZZZZZ", DeserializationError::NoMemory);
checkString<16>("\xAFZZZZZZZZZZZZZZZ", DeserializationError::Ok);
checkString<16>("\xB0ZZZZZZZZZZZZZZZZ", DeserializationError::NoMemory);
}
SECTION("str 8") {
check<0>("\xD9\x00", DeserializationError::Ok);
check<0>("\xD9\x01H", DeserializationError::NoMemory);
check<4>("\xD9\x01H", DeserializationError::Ok);
check<4>("\xD9\x05Hello", DeserializationError::NoMemory);
checkString<8>("\xD9\x00", DeserializationError::Ok);
checkString<8>("\xD9\x07ZZZZZZZ", DeserializationError::Ok);
checkString<8>("\xD9\x08ZZZZZZZZ", DeserializationError::NoMemory);
checkString<16>("\xD9\x0FZZZZZZZZZZZZZZZ", DeserializationError::Ok);
checkString<16>("\xD9\x10ZZZZZZZZZZZZZZZZ", DeserializationError::NoMemory);
}
SECTION("str 16") {
check<0>("\xDA\x00\x00", DeserializationError::Ok);
check<0>("\xDA\x00\x01H", DeserializationError::NoMemory);
check<4>("\xDA\x00\x01H", DeserializationError::Ok);
check<4>("\xDA\x00\x05Hello", DeserializationError::NoMemory);
checkString<8>("\xDA\x00\x00", DeserializationError::Ok);
checkString<8>("\xDA\x00\x07ZZZZZZZ", DeserializationError::Ok);
checkString<8>("\xDA\x00\x08ZZZZZZZZ", DeserializationError::NoMemory);
checkString<16>("\xDA\x00\x0FZZZZZZZZZZZZZZZ", DeserializationError::Ok);
checkString<16>("\xDA\x00\x10ZZZZZZZZZZZZZZZZ",
DeserializationError::NoMemory);
}
SECTION("str 32") {
check<0>("\xDB\x00\x00\x00\x00", DeserializationError::Ok);
check<0>("\xDB\x00\x00\x00\x01H", DeserializationError::NoMemory);
check<4>("\xDB\x00\x00\x00\x01H", DeserializationError::Ok);
check<4>("\xDB\x00\x00\x00\x05Hello", DeserializationError::NoMemory);
checkString<8>("\xDB\x00\x00\x00\x00", DeserializationError::Ok);
checkString<8>("\xDB\x00\x00\x00\x07ZZZZZZZ", DeserializationError::Ok);
checkString<8>("\xDB\x00\x00\x00\x08ZZZZZZZZ",
DeserializationError::NoMemory);
checkString<16>("\xDB\x00\x00\x00\x0FZZZZZZZZZZZZZZZ",
DeserializationError::Ok);
checkString<16>("\xDB\x00\x00\x00\x10ZZZZZZZZZZZZZZZZ",
DeserializationError::NoMemory);
}
SECTION("fixarray") {
@ -89,14 +101,14 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") {
SECTION("{H:1}") {
check<JSON_OBJECT_SIZE(0)>("\x81\xA1H\x01",
DeserializationError::NoMemory);
check<JSON_OBJECT_SIZE(1) + epsilon>("\x81\xA1H\x01",
DeserializationError::Ok);
check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>(
"\x81\xA1H\x01", DeserializationError::Ok);
}
SECTION("{H:1,W:2}") {
check<JSON_OBJECT_SIZE(1) + epsilon>("\x82\xA1H\x01\xA1W\x02",
DeserializationError::NoMemory);
check<JSON_OBJECT_SIZE(2) + 2 * epsilon>("\x82\xA1H\x01\xA1W\x02",
DeserializationError::Ok);
check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>(
"\x82\xA1H\x01\xA1W\x02", DeserializationError::NoMemory);
check<JSON_OBJECT_SIZE(2) + 2 * JSON_STRING_SIZE(2)>(
"\x82\xA1H\x01\xA1W\x02", DeserializationError::Ok);
}
}
@ -107,14 +119,14 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") {
SECTION("{H:1}") {
check<JSON_OBJECT_SIZE(0)>("\xDE\x00\x01\xA1H\x01",
DeserializationError::NoMemory);
check<JSON_OBJECT_SIZE(1) + epsilon>("\xDE\x00\x01\xA1H\x01",
DeserializationError::Ok);
check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>(
"\xDE\x00\x01\xA1H\x01", DeserializationError::Ok);
}
SECTION("{H:1,W:2}") {
check<JSON_OBJECT_SIZE(1) + epsilon>("\xDE\x00\x02\xA1H\x01\xA1W\x02",
DeserializationError::NoMemory);
check<JSON_OBJECT_SIZE(2) + 2 * epsilon>("\xDE\x00\x02\xA1H\x01\xA1W\x02",
DeserializationError::Ok);
check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>(
"\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::NoMemory);
check<JSON_OBJECT_SIZE(2) + 2 * JSON_OBJECT_SIZE(1)>(
"\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok);
}
}
@ -126,14 +138,14 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") {
SECTION("{H:1}") {
check<JSON_OBJECT_SIZE(0)>("\xDF\x00\x00\x00\x01\xA1H\x01",
DeserializationError::NoMemory);
check<JSON_OBJECT_SIZE(1) + epsilon>("\xDF\x00\x00\x00\x01\xA1H\x01",
DeserializationError::Ok);
check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>(
"\xDF\x00\x00\x00\x01\xA1H\x01", DeserializationError::Ok);
}
SECTION("{H:1,W:2}") {
check<JSON_OBJECT_SIZE(1) + epsilon>(
check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>(
"\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02",
DeserializationError::NoMemory);
check<JSON_OBJECT_SIZE(2) + 2 * epsilon>(
check<JSON_OBJECT_SIZE(2) + 2 * JSON_OBJECT_SIZE(1)>(
"\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok);
}
}

View File

@ -3,9 +3,11 @@
# MIT License
add_executable(StaticMemoryPoolTests
alloc.cpp
allocVariant.cpp
allocString.cpp
clear.cpp
size.cpp
startString.cpp
StringBuilder.cpp
)
target_link_libraries(StaticMemoryPoolTests catch)

View File

@ -0,0 +1,39 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Memory/StaticMemoryPool.hpp>
#include <ArduinoJson/Memory/StringBuilder.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("StringBuilder") {
SECTION("WorksWhenBufferIsBigEnough") {
StaticMemoryPool<JSON_STRING_SIZE(6)> memoryPool;
StringBuilder str(&memoryPool);
str.append("hello");
REQUIRE(str.complete().equals("hello"));
}
SECTION("ReturnsNullWhenTooSmall") {
StaticMemoryPool<1> memoryPool;
StringBuilder str(&memoryPool);
str.append("hello!!!");
REQUIRE(str.complete().isNull());
}
SECTION("Increases size of memory pool") {
StaticMemoryPool<JSON_STRING_SIZE(6)> memoryPool;
StringBuilder str(&memoryPool);
str.append('h');
str.complete();
REQUIRE(JSON_STRING_SIZE(2) == memoryPool.size());
}
}

View File

@ -1,54 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Memory/StaticMemoryPool.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
static bool isAligned(void *ptr) {
const size_t mask = sizeof(void *) - 1;
size_t addr = reinterpret_cast<size_t>(ptr);
return (addr & mask) == 0;
}
TEST_CASE("StaticMemoryPool::alloc()") {
StaticMemoryPool<64> memoryPool;
SECTION("Returns different addresses") {
void *p1 = memoryPool.alloc(1);
void *p2 = memoryPool.alloc(1);
REQUIRE(p1 != p2);
}
SECTION("Returns non-NULL when using full capacity") {
void *p = memoryPool.alloc(64);
REQUIRE(0 != p);
}
SECTION("Returns NULL when full") {
memoryPool.alloc(64);
void *p = memoryPool.alloc(1);
REQUIRE(0 == p);
}
SECTION("Returns NULL when memoryPool is too small") {
void *p = memoryPool.alloc(65);
REQUIRE(0 == p);
}
SECTION("Returns aligned pointers") {
for (size_t size = 1; size <= sizeof(void *); size++) {
void *p = memoryPool.alloc(1);
REQUIRE(isAligned(p));
}
}
SECTION("Returns same address after clear()") {
void *p1 = memoryPool.alloc(1);
memoryPool.clear();
void *p2 = memoryPool.alloc(1);
REQUIRE(p1 == p2);
}
}

View File

@ -0,0 +1,131 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Memory/StaticMemoryPool.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("StaticMemoryPool::allocFrozenString()") {
const size_t poolCapacity = 64;
const size_t longestString = poolCapacity - sizeof(StringSlot);
StaticMemoryPool<poolCapacity> pool;
SECTION("Returns different addresses") {
StringSlot *a = pool.allocFrozenString(1);
StringSlot *b = pool.allocFrozenString(1);
REQUIRE(a != b);
REQUIRE(a->value != b->value);
}
SECTION("Returns a StringSlot of the right size") {
StringSlot *s = pool.allocFrozenString(12);
REQUIRE(s->size == 12);
}
SECTION("Returns NULL when full") {
pool.allocFrozenString(longestString);
void *p = pool.allocFrozenString(1);
REQUIRE(0 == p);
}
SECTION("Returns NULL when pool is too small") {
void *p = pool.allocFrozenString(longestString + 1);
REQUIRE(0 == p);
}
SECTION("Returns aligned pointers") {
REQUIRE(isAligned(pool.allocFrozenString(1)));
REQUIRE(isAligned(pool.allocFrozenString(1)));
}
SECTION("Returns same address after clear()") {
StringSlot *a = pool.allocFrozenString(1);
pool.clear();
StringSlot *b = pool.allocFrozenString(1);
REQUIRE(a == b);
REQUIRE(a->value == b->value);
}
SECTION("Returns same address after freeString()") {
StringSlot *a = pool.allocFrozenString(1);
pool.freeString(a);
StringSlot *b = pool.allocFrozenString(1);
REQUIRE(a == b);
REQUIRE(a->value == b->value);
}
SECTION("Can use full capacity when fresh") {
StringSlot *a = pool.allocFrozenString(longestString);
REQUIRE(a != 0);
}
SECTION("Can use full capacity after clear") {
pool.allocFrozenString(longestString);
pool.clear();
StringSlot *a = pool.allocFrozenString(longestString);
REQUIRE(a != 0);
}
}
TEST_CASE("StaticMemoryPool::freeString()") {
const size_t poolCapacity = 512;
const size_t longestString = poolCapacity - sizeof(StringSlot);
StaticMemoryPool<poolCapacity> pool;
static const size_t testStringSize =
(poolCapacity - sizeof(StringSlot) * 4 - sizeof(VariantSlot) * 4) / 4;
SECTION("Restores full capacity") {
StringSlot *strings[4];
VariantSlot *variants[4];
for (int i = 0; i < 4; i++) {
strings[i] = pool.allocFrozenString(testStringSize);
REQUIRE(strings[i] != 0);
variants[i] = pool.allocVariant();
REQUIRE(variants[i] != 0);
}
// In random order
pool.freeString(strings[2]);
pool.freeVariant(variants[3]);
pool.freeVariant(variants[0]);
pool.freeString(strings[0]);
pool.freeVariant(variants[1]);
pool.freeString(strings[1]);
pool.freeVariant(variants[2]);
pool.freeString(strings[3]);
StringSlot *b = pool.allocFrozenString(longestString);
REQUIRE(b != 0);
REQUIRE(b->size == longestString);
}
SECTION("Move strings") {
StringSlot *a = pool.allocFrozenString(6);
strcpy(a->value, "hello");
StringSlot *b = pool.allocFrozenString(7);
strcpy(b->value, "world!");
pool.freeString(a);
REQUIRE(b->size == 7);
REQUIRE(b->value == std::string("world!"));
REQUIRE(a->value == b->value);
}
SECTION("Accepts non-frozen string") {
StringSlot *a = pool.allocExpandableString();
pool.freeString(a);
REQUIRE(pool.size() == 0);
}
}

View File

@ -0,0 +1,38 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Memory/StaticMemoryPool.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("StaticMemoryPool::allocVariant()") {
StaticMemoryPool<128> memoryPool;
SECTION("Returns different pointer") {
VariantSlot* s1 = memoryPool.allocVariant();
REQUIRE(s1 != 0);
VariantSlot* s2 = memoryPool.allocVariant();
REQUIRE(s2 != 0);
REQUIRE(s1 != s2);
}
SECTION("Returns same pointer after freeSlot()") {
VariantSlot* s1 = memoryPool.allocVariant();
memoryPool.freeVariant(s1);
VariantSlot* s2 = memoryPool.allocVariant();
REQUIRE(s1 == s2);
}
SECTION("Returns aligned pointers") {
// make room for two
// pass an uneven capacity
StaticMemoryPool<2 * sizeof(VariantSlot) + 1> pool;
REQUIRE(isAligned(pool.allocVariant()));
REQUIRE(isAligned(pool.allocVariant()));
}
}

View File

@ -0,0 +1,81 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Memory/StaticMemoryPool.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
static const size_t poolCapacity = 512;
TEST_CASE("StaticMemoryPool::clear()") {
StaticMemoryPool<poolCapacity> memoryPool;
SECTION("Discards allocated variants") {
memoryPool.allocVariant();
memoryPool.clear();
REQUIRE(memoryPool.size() == 0);
}
SECTION("Discards allocated strings") {
memoryPool.allocFrozenString(10);
memoryPool.clear();
REQUIRE(memoryPool.size() == 0);
}
SECTION("Purges variant cache") {
VariantSlot* a = memoryPool.allocVariant();
REQUIRE(a != 0);
VariantSlot* b = memoryPool.allocVariant();
REQUIRE(b != 0);
// place slot a in the pool of free slots
memoryPool.freeVariant(a);
memoryPool.clear();
REQUIRE(memoryPool.size() == 0);
}
SECTION("Purges string cache") {
StringSlot* a = memoryPool.allocFrozenString(10);
REQUIRE(a != 0);
StringSlot* b = memoryPool.allocFrozenString(10);
REQUIRE(b != 0);
// place slot a in the pool of free slots
memoryPool.freeString(a);
memoryPool.clear();
REQUIRE(memoryPool.size() == 0);
}
SECTION("Purges list of string") {
StringSlot* a = memoryPool.allocFrozenString(6);
REQUIRE(a != 0);
strcpy(a->value, "hello");
StringSlot* b = memoryPool.allocFrozenString(6);
REQUIRE(b != 0);
strcpy(b->value, "world");
memoryPool.clear(); // ACT!
StringSlot* c = memoryPool.allocFrozenString(2);
REQUIRE(c != 0);
strcpy(c->value, "H");
StringSlot* d = memoryPool.allocFrozenString(2);
REQUIRE(d != 0);
strcpy(d->value, "W");
// if the memory pool keeps pointer to the old strings
// it will try to compact the strings
memoryPool.freeString(c);
REQUIRE(d->value == std::string("W"));
}
}

View File

@ -8,37 +8,72 @@
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("StaticMemoryPool::size()") {
StaticMemoryPool<64> memoryPool;
SECTION("Capacity equals template parameter") {
REQUIRE(64 == memoryPool.capacity());
const size_t capacity = 64;
StaticMemoryPool<capacity> memoryPool;
REQUIRE(capacity == memoryPool.capacity());
}
SECTION("Initial size is 0") {
StaticMemoryPool<32> memoryPool;
REQUIRE(0 == memoryPool.size());
}
SECTION("Increases after alloc()") {
memoryPool.alloc(1);
REQUIRE(1U <= memoryPool.size());
memoryPool.alloc(1);
REQUIRE(2U <= memoryPool.size());
SECTION("Increases after allocFrozenString()") {
StaticMemoryPool<128> memoryPool;
memoryPool.allocFrozenString(0);
REQUIRE(memoryPool.size() == JSON_STRING_SIZE(0));
memoryPool.allocFrozenString(0);
REQUIRE(memoryPool.size() == 2 * JSON_STRING_SIZE(0));
}
SECTION("Doesn't grow when memoryPool is full") {
memoryPool.alloc(64);
memoryPool.alloc(1);
REQUIRE(64 == memoryPool.size());
SECTION("Decreases after freeVariant()") {
StaticMemoryPool<128> memoryPool;
VariantSlot* a = memoryPool.allocVariant();
VariantSlot* b = memoryPool.allocVariant();
memoryPool.freeVariant(b);
REQUIRE(memoryPool.size() == sizeof(VariantSlot));
memoryPool.freeVariant(a);
REQUIRE(memoryPool.size() == 0);
}
SECTION("Does't grow when memoryPool is too small for alloc") {
memoryPool.alloc(65);
REQUIRE(0 == memoryPool.size());
SECTION("Decreases after calling freeString() in order") {
StaticMemoryPool<128> memoryPool;
StringSlot* a = memoryPool.allocFrozenString(5);
REQUIRE(a != 0);
StringSlot* b = memoryPool.allocFrozenString(6);
REQUIRE(b != 0);
memoryPool.freeString(b);
REQUIRE(memoryPool.size() == JSON_STRING_SIZE(5));
memoryPool.freeString(a);
REQUIRE(memoryPool.size() == 0);
}
SECTION("Goes back to zero after clear()") {
memoryPool.alloc(1);
memoryPool.clear();
REQUIRE(0 == memoryPool.size());
SECTION("Decreases after calling freeString() in reverse order") {
StaticMemoryPool<128> memoryPool;
StringSlot* a = memoryPool.allocFrozenString(5);
REQUIRE(a != 0);
StringSlot* b = memoryPool.allocFrozenString(6);
REQUIRE(b != 0);
memoryPool.freeString(a);
REQUIRE(memoryPool.size() == JSON_STRING_SIZE(6));
memoryPool.freeString(b);
REQUIRE(memoryPool.size() == 0);
}
SECTION("Doesn't grow when memory pool is full") {
const size_t variantCount = 4;
const size_t capacity = variantCount * sizeof(VariantSlot);
StaticMemoryPool<capacity> memoryPool;
for (size_t i = 0; i < variantCount; i++) memoryPool.allocVariant();
REQUIRE(capacity == memoryPool.size());
memoryPool.allocVariant();
REQUIRE(capacity == memoryPool.size());
}
}

View File

@ -1,46 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("StaticMemoryPool::startString()") {
SECTION("WorksWhenBufferIsBigEnough") {
StaticMemoryPool<6> memoryPool;
StringBuilder str = memoryPool.startString();
str.append('h');
str.append('e');
str.append('l');
str.append('l');
str.append('o');
REQUIRE(str.complete().equals("hello"));
}
SECTION("ReturnsNullWhenTooSmall") {
StaticMemoryPool<5> memoryPool;
StringBuilder str = memoryPool.startString();
str.append('h');
str.append('e');
str.append('l');
str.append('l');
str.append('o');
REQUIRE(str.complete().isNull());
}
SECTION("SizeIncreases") {
StaticMemoryPool<5> memoryPool;
StringBuilder str = memoryPool.startString();
str.append('h');
str.complete();
REQUIRE(2 == memoryPool.size());
}
}