Update to ArduinoJson 7.0.4

This commit is contained in:
iranl
2024-04-19 14:44:01 +02:00
parent 1378732081
commit 81be0a689a
444 changed files with 10842 additions and 9422 deletions

View File

@@ -0,0 +1,25 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2024, Benoit BLANCHON
# MIT License
add_executable(ResourceManagerTests
allocVariant.cpp
clear.cpp
saveString.cpp
shrinkToFit.cpp
size.cpp
StringBuilder.cpp
swap.cpp
)
add_compile_definitions(ResourceManagerTests
ARDUINOJSON_SLOT_ID_SIZE=1 # require less RAM for overflow tests
ARDUINOJSON_POOL_CAPACITY=16
)
add_test(ResourceManager ResourceManagerTests)
set_tests_properties(ResourceManager
PROPERTIES
LABELS "Catch"
)

View File

@@ -0,0 +1,143 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson/Memory/StringBuilder.hpp>
#include <catch.hpp>
#include "Allocators.hpp"
using namespace ArduinoJson::detail;
TEST_CASE("StringBuilder") {
KillswitchAllocator killswitch;
SpyingAllocator spyingAllocator(&killswitch);
ResourceManager resources(&spyingAllocator);
SECTION("Empty string") {
StringBuilder str(&resources);
str.startString();
str.save();
REQUIRE(resources.size() == sizeofString(""));
REQUIRE(resources.overflowed() == false);
REQUIRE(spyingAllocator.log() ==
AllocatorLog{
Allocate(sizeofStringBuffer()),
Reallocate(sizeofStringBuffer(), sizeofString("")),
});
}
SECTION("Short string fits in first allocation") {
StringBuilder str(&resources);
str.startString();
str.append("hello");
REQUIRE(str.isValid() == true);
REQUIRE(str.str() == "hello");
REQUIRE(resources.overflowed() == false);
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofStringBuffer()),
});
}
SECTION("Long string needs reallocation") {
StringBuilder str(&resources);
const char* lorem =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
"eiusmod tempor incididunt ut labore et dolore magna aliqua.";
str.startString();
str.append(lorem);
REQUIRE(str.isValid() == true);
REQUIRE(str.str() == lorem);
REQUIRE(resources.overflowed() == false);
REQUIRE(spyingAllocator.log() ==
AllocatorLog{
Allocate(sizeofStringBuffer(1)),
Reallocate(sizeofStringBuffer(1), sizeofStringBuffer(2)),
Reallocate(sizeofStringBuffer(2), sizeofStringBuffer(3)),
});
}
SECTION("Realloc fails") {
StringBuilder str(&resources);
str.startString();
killswitch.on();
str.append(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
"eiusmod tempor incididunt ut labore et dolore magna aliqua.");
REQUIRE(spyingAllocator.log() ==
AllocatorLog{
Allocate(sizeofStringBuffer()),
ReallocateFail(sizeofStringBuffer(), sizeofStringBuffer(2)),
Deallocate(sizeofStringBuffer()),
});
REQUIRE(str.isValid() == false);
REQUIRE(resources.overflowed() == true);
}
SECTION("Initial allocation fails") {
StringBuilder str(&resources);
killswitch.on();
str.startString();
REQUIRE(str.isValid() == false);
REQUIRE(resources.overflowed() == true);
REQUIRE(spyingAllocator.log() == AllocatorLog{
AllocateFail(sizeofStringBuffer()),
});
}
}
static StringNode* addStringToPool(ResourceManager& resources, const char* s) {
StringBuilder str(&resources);
str.startString();
str.append(s);
return str.save();
}
TEST_CASE("StringBuilder::save() deduplicates strings") {
ResourceManager resources;
SECTION("Basic") {
auto s1 = addStringToPool(resources, "hello");
auto s2 = addStringToPool(resources, "world");
auto s3 = addStringToPool(resources, "hello");
REQUIRE(s1 == s3);
REQUIRE(s2 != s3);
REQUIRE(s1->references == 2);
REQUIRE(s2->references == 1);
REQUIRE(s3->references == 2);
REQUIRE(resources.size() == sizeofString("hello") + sizeofString("world"));
}
SECTION("Requires terminator") {
auto s1 = addStringToPool(resources, "hello world");
auto s2 = addStringToPool(resources, "hello");
REQUIRE(s2 != s1);
REQUIRE(s1->references == 1);
REQUIRE(s2->references == 1);
REQUIRE(resources.size() ==
sizeofString("hello world") + sizeofString("hello"));
}
SECTION("Don't overrun") {
auto s1 = addStringToPool(resources, "hello world");
auto s2 = addStringToPool(resources, "wor");
REQUIRE(s2 != s1);
REQUIRE(s1->references == 1);
REQUIRE(s2->references == 1);
REQUIRE(resources.size() ==
sizeofString("hello world") + sizeofString("wor"));
}
}

View File

@@ -0,0 +1,94 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson/Memory/Alignment.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
#include <catch.hpp>
#include "Allocators.hpp"
using namespace ArduinoJson::detail;
TEST_CASE("ResourceManager::allocSlot()") {
SECTION("Returns different pointer") {
ResourceManager resources;
VariantSlot* s1 = resources.allocSlot();
REQUIRE(s1 != 0);
VariantSlot* s2 = resources.allocSlot();
REQUIRE(s2 != 0);
REQUIRE(s1 != s2);
}
SECTION("Returns the same slot after calling freeSlot()") {
ResourceManager resources;
auto s1 = resources.allocSlot();
auto s2 = resources.allocSlot();
resources.freeSlot(s1);
resources.freeSlot(s2);
auto s3 = resources.allocSlot();
auto s4 = resources.allocSlot();
auto s5 = resources.allocSlot();
REQUIRE(s2.id() != s1.id());
REQUIRE(s3.id() == s2.id());
REQUIRE(s4.id() == s1.id());
REQUIRE(s5.id() != s1.id());
REQUIRE(s5.id() != s2.id());
}
SECTION("Returns aligned pointers") {
ResourceManager resources;
REQUIRE(isAligned(resources.allocSlot().operator VariantSlot*()));
REQUIRE(isAligned(resources.allocSlot().operator VariantSlot*()));
}
SECTION("Returns null if pool list allocation fails") {
ResourceManager resources(FailingAllocator::instance());
auto variant = resources.allocSlot();
REQUIRE(variant.id() == NULL_SLOT);
REQUIRE(static_cast<VariantSlot*>(variant) == nullptr);
}
SECTION("Returns null if pool allocation fails") {
ResourceManager resources(FailingAllocator::instance());
resources.allocSlot();
auto variant = resources.allocSlot();
REQUIRE(variant.id() == NULL_SLOT);
REQUIRE(static_cast<VariantSlot*>(variant) == nullptr);
}
SECTION("Try overflow pool counter") {
ResourceManager resources;
// this test assumes SlotId is 8-bit; otherwise it consumes a lot of memory
// tyhe GitHub Workflow gets killed
REQUIRE(NULL_SLOT == 255);
// fill all the pools
for (SlotId i = 0; i < NULL_SLOT; i++) {
auto slot = resources.allocSlot();
REQUIRE(slot.id() == i); // or != NULL_SLOT
REQUIRE(static_cast<VariantSlot*>(slot) != nullptr);
}
REQUIRE(resources.overflowed() == false);
// now all allocations should fail
for (int i = 0; i < 10; i++) {
auto slot = resources.allocSlot();
REQUIRE(slot.id() == NULL_SLOT);
REQUIRE(static_cast<VariantSlot*>(slot) == nullptr);
}
REQUIRE(resources.overflowed() == true);
}
}

View File

@@ -0,0 +1,30 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <catch.hpp>
using namespace ArduinoJson::detail;
TEST_CASE("ResourceManager::clear()") {
ResourceManager resources;
SECTION("Discards allocated variants") {
resources.allocSlot();
resources.clear();
REQUIRE(resources.size() == 0);
}
SECTION("Discards allocated strings") {
resources.saveString(adaptString("123456789"));
REQUIRE(resources.size() == sizeofString(9));
resources.clear();
REQUIRE(resources.size() == 0);
}
}

View File

@@ -0,0 +1,70 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <catch.hpp>
#include "Allocators.hpp"
using namespace ArduinoJson::detail;
static StringNode* saveString(ResourceManager& resources, const char* s) {
return resources.saveString(adaptString(s));
}
static StringNode* saveString(ResourceManager& resources, const char* s,
size_t n) {
return resources.saveString(adaptString(s, n));
}
TEST_CASE("ResourceManager::saveString()") {
ResourceManager resources;
SECTION("Duplicates different strings") {
auto a = saveString(resources, "hello");
auto b = saveString(resources, "world");
REQUIRE(+a->data != +b->data);
REQUIRE(a->length == 5);
REQUIRE(b->length == 5);
REQUIRE(a->references == 1);
REQUIRE(b->references == 1);
REQUIRE(resources.size() == sizeofString("hello") + sizeofString("world"));
}
SECTION("Deduplicates identical strings") {
auto a = saveString(resources, "hello");
auto b = saveString(resources, "hello");
REQUIRE(a == b);
REQUIRE(a->length == 5);
REQUIRE(a->references == 2);
REQUIRE(resources.size() == sizeofString("hello"));
}
SECTION("Deduplicates identical strings that contain NUL") {
auto a = saveString(resources, "hello\0world", 11);
auto b = saveString(resources, "hello\0world", 11);
REQUIRE(a == b);
REQUIRE(a->length == 11);
REQUIRE(a->references == 2);
REQUIRE(resources.size() == sizeofString("hello world"));
}
SECTION("Don't stop on first NUL") {
auto a = saveString(resources, "hello");
auto b = saveString(resources, "hello\0world", 11);
REQUIRE(a != b);
REQUIRE(a->length == 5);
REQUIRE(b->length == 11);
REQUIRE(a->references == 1);
REQUIRE(b->references == 1);
REQUIRE(resources.size() ==
sizeofString("hello") + sizeofString("hello world"));
}
SECTION("Returns NULL when allocation fails") {
ResourceManager pool2(FailingAllocator::instance());
REQUIRE(saveString(pool2, "a") == nullptr);
}
}

View File

@@ -0,0 +1,57 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
#include <catch.hpp>
#include "Allocators.hpp"
using namespace ArduinoJson::detail;
TEST_CASE("ResourceManager::shrinkToFit()") {
SpyingAllocator spyingAllocator;
ResourceManager resources(&spyingAllocator);
SECTION("empty") {
resources.shrinkToFit();
REQUIRE(spyingAllocator.log() == AllocatorLog{});
}
SECTION("only one pool") {
resources.allocSlot();
resources.shrinkToFit();
REQUIRE(spyingAllocator.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeof(VariantSlot)),
});
}
SECTION("more pools than initial count") {
for (size_t i = 0;
i < ARDUINOJSON_POOL_CAPACITY * ARDUINOJSON_INITIAL_POOL_COUNT + 1;
i++)
resources.allocSlot();
REQUIRE(spyingAllocator.log() ==
AllocatorLog{
Allocate(sizeofPool()) * ARDUINOJSON_INITIAL_POOL_COUNT,
Allocate(sizeofPoolList(ARDUINOJSON_INITIAL_POOL_COUNT * 2)),
Allocate(sizeofPool()),
});
spyingAllocator.clearLog();
resources.shrinkToFit();
REQUIRE(spyingAllocator.log() ==
AllocatorLog{
Reallocate(sizeofPool(), sizeof(VariantSlot)),
Reallocate(sizeofPoolList(ARDUINOJSON_INITIAL_POOL_COUNT * 2),
sizeofPoolList(ARDUINOJSON_INITIAL_POOL_COUNT + 1)),
});
}
}

View File

@@ -0,0 +1,31 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
#include <catch.hpp>
#include "Allocators.hpp"
using namespace ArduinoJson::detail;
TEST_CASE("ResourceManager::size()") {
TimebombAllocator timebomb(0);
ResourceManager resources(&timebomb);
SECTION("Initial size is 0") {
REQUIRE(0 == resources.size());
}
SECTION("Doesn't grow when allocation of second pool fails") {
timebomb.setCountdown(1);
for (size_t i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
resources.allocSlot();
size_t size = resources.size();
resources.allocSlot();
REQUIRE(size == resources.size());
}
}

View File

@@ -0,0 +1,96 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson/Memory/Alignment.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
#include <catch.hpp>
#include "Allocators.hpp"
using namespace ArduinoJson::detail;
static void fullPreallocatedPools(ResourceManager& resources) {
for (int i = 0;
i < ARDUINOJSON_INITIAL_POOL_COUNT * ARDUINOJSON_POOL_CAPACITY; i++)
resources.allocSlot();
}
TEST_CASE("ResourceManager::swap()") {
SECTION("Both using preallocated pool list") {
SpyingAllocator spy;
ResourceManager a(&spy);
ResourceManager b(&spy);
auto a1 = a.allocSlot();
auto b1 = b.allocSlot();
swap(a, b);
REQUIRE(a1->data() == b.getSlot(a1.id())->data());
REQUIRE(b1->data() == a.getSlot(b1.id())->data());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()) * 2,
});
}
SECTION("Only left using preallocated pool list") {
SpyingAllocator spy;
ResourceManager a(&spy);
ResourceManager b(&spy);
fullPreallocatedPools(b);
auto a1 = a.allocSlot();
auto b1 = b.allocSlot();
swap(a, b);
REQUIRE(a1->data() == b.getSlot(a1.id())->data());
REQUIRE(b1->data() == a.getSlot(b1.id())->data());
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofPool()) * (ARDUINOJSON_INITIAL_POOL_COUNT + 1),
Allocate(sizeofPoolList(ARDUINOJSON_INITIAL_POOL_COUNT * 2)),
Allocate(sizeofPool()),
});
}
SECTION("Only right using preallocated pool list") {
SpyingAllocator spy;
ResourceManager a(&spy);
fullPreallocatedPools(a);
ResourceManager b(&spy);
auto a1 = a.allocSlot();
auto b1 = b.allocSlot();
swap(a, b);
REQUIRE(a1->data() == b.getSlot(a1.id())->data());
REQUIRE(b1->data() == a.getSlot(b1.id())->data());
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofPool()) * ARDUINOJSON_INITIAL_POOL_COUNT,
Allocate(sizeofPoolList(ARDUINOJSON_INITIAL_POOL_COUNT * 2)),
Allocate(sizeofPool()) * 2,
});
}
SECTION("None is using preallocated pool list") {
SpyingAllocator spy;
ResourceManager a(&spy);
fullPreallocatedPools(a);
ResourceManager b(&spy);
fullPreallocatedPools(b);
auto a1 = a.allocSlot();
auto b1 = b.allocSlot();
swap(a, b);
REQUIRE(a1->data() == b.getSlot(a1.id())->data());
REQUIRE(b1->data() == a.getSlot(b1.id())->data());
}
}