Remove BasicJsonDocument

This commit is contained in:
Benoit Blanchon
2023-03-20 14:49:08 +01:00
parent 24aaab6e3e
commit db9258bcd7
6 changed files with 178 additions and 204 deletions

View File

@ -4,5 +4,6 @@ ArduinoJson: change log
HEAD
----
* Remove `BasicJsonDocument`
* Remove `StaticJsonDocument`
* Add abstract `Allocator` class

View File

@ -4,7 +4,7 @@
add_executable(JsonDocumentTests
add.cpp
BasicJsonDocument.cpp
allocator.cpp
cast.cpp
compare.cpp
containsKey.cpp

View File

@ -60,21 +60,21 @@ class ControllableAllocator : public Allocator {
bool _enabled;
};
TEST_CASE("BasicJsonDocument") {
TEST_CASE("DynamicJsonDocument's allocator") {
SpyingAllocator spyingAllocator;
ControllableAllocator controllableAllocator;
SECTION("Construct/Destruct") {
{ BasicJsonDocument doc(4096, &spyingAllocator); }
{ DynamicJsonDocument doc(4096, &spyingAllocator); }
REQUIRE(spyingAllocator.log() == "A4096F");
}
SECTION("Copy construct") {
{
BasicJsonDocument doc1(4096, &spyingAllocator);
DynamicJsonDocument doc1(4096, &spyingAllocator);
doc1.set(std::string("The size of this string is 32!!"));
BasicJsonDocument doc2(doc1);
DynamicJsonDocument doc2(doc1);
REQUIRE(doc1.as<std::string>() == "The size of this string is 32!!");
REQUIRE(doc2.as<std::string>() == "The size of this string is 32!!");
@ -85,10 +85,10 @@ TEST_CASE("BasicJsonDocument") {
SECTION("Move construct") {
{
BasicJsonDocument doc1(4096, &spyingAllocator);
DynamicJsonDocument doc1(4096, &spyingAllocator);
doc1.set(std::string("The size of this string is 32!!"));
BasicJsonDocument doc2(std::move(doc1));
DynamicJsonDocument doc2(std::move(doc1));
REQUIRE(doc2.as<std::string>() == "The size of this string is 32!!");
REQUIRE(doc1.as<std::string>() == "null");
@ -100,9 +100,9 @@ TEST_CASE("BasicJsonDocument") {
SECTION("Copy assign larger") {
{
BasicJsonDocument doc1(4096, &spyingAllocator);
DynamicJsonDocument doc1(4096, &spyingAllocator);
doc1.set(std::string("The size of this string is 32!!"));
BasicJsonDocument doc2(8, &spyingAllocator);
DynamicJsonDocument doc2(8, &spyingAllocator);
doc2 = doc1;
@ -115,9 +115,9 @@ TEST_CASE("BasicJsonDocument") {
SECTION("Copy assign smaller") {
{
BasicJsonDocument doc1(1024, &spyingAllocator);
DynamicJsonDocument doc1(1024, &spyingAllocator);
doc1.set(std::string("The size of this string is 32!!"));
BasicJsonDocument doc2(4096, &spyingAllocator);
DynamicJsonDocument doc2(4096, &spyingAllocator);
doc2 = doc1;
@ -130,9 +130,9 @@ TEST_CASE("BasicJsonDocument") {
SECTION("Copy assign same size") {
{
BasicJsonDocument doc1(1024, &spyingAllocator);
DynamicJsonDocument doc1(1024, &spyingAllocator);
doc1.set(std::string("The size of this string is 32!!"));
BasicJsonDocument doc2(1024, &spyingAllocator);
DynamicJsonDocument doc2(1024, &spyingAllocator);
doc2 = doc1;
@ -145,9 +145,9 @@ TEST_CASE("BasicJsonDocument") {
SECTION("Move assign") {
{
BasicJsonDocument doc1(4096, &spyingAllocator);
DynamicJsonDocument doc1(4096, &spyingAllocator);
doc1.set(std::string("The size of this string is 32!!"));
BasicJsonDocument doc2(8, &spyingAllocator);
DynamicJsonDocument doc2(8, &spyingAllocator);
doc2 = std::move(doc1);
@ -160,7 +160,7 @@ TEST_CASE("BasicJsonDocument") {
}
SECTION("garbageCollect()") {
BasicJsonDocument doc(4096, &controllableAllocator);
DynamicJsonDocument doc(4096, &controllableAllocator);
SECTION("when allocation succeeds") {
deserializeJson(doc, "{\"blanket\":1,\"dancing\":2}");

View File

@ -58,7 +58,7 @@ void testShrinkToFit(DynamicJsonDocument& doc, std::string expected_json,
}
}
TEST_CASE("BasicJsonDocument::shrinkToFit()") {
TEST_CASE("DynamicJsonDocument::shrinkToFit()") {
ArmoredAllocator armoredAllocator;
DynamicJsonDocument doc(4096, &armoredAllocator);
@ -138,15 +138,3 @@ TEST_CASE("BasicJsonDocument::shrinkToFit()") {
REQUIRE(doc[0] == "?");
}
}
TEST_CASE("DynamicJsonDocument::shrinkToFit()") {
DynamicJsonDocument doc(4096);
deserializeJson(doc, "{\"hello\":[\"world\"]");
doc.shrinkToFit();
std::string json;
serializeJson(doc, json);
REQUIRE(json == "{\"hello\":[\"world\"]}");
}

View File

@ -1,172 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Document/JsonDocument.hpp>
#include <ArduinoJson/Memory/Allocator.hpp>
#include <stdlib.h> // malloc, free
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Helper to implement the "base-from-member" idiom
// (we need to store the allocator before constructing JsonDocument)
class AllocatorOwner {
public:
AllocatorOwner(Allocator* allocator) : _allocator(allocator) {}
void* allocate(size_t size) {
return _allocator->allocate(size);
}
void deallocate(void* ptr) {
if (ptr)
_allocator->deallocate(ptr);
}
void* reallocate(void* ptr, size_t new_size) {
return _allocator->reallocate(ptr, new_size);
}
Allocator* allocator() {
return _allocator;
}
private:
Allocator* _allocator;
};
// A JsonDocument that uses the provided allocator to allocate its memory pool.
// https://arduinojson.org/v6/api/basicjsondocument/
class BasicJsonDocument : AllocatorOwner, public JsonDocument {
public:
explicit BasicJsonDocument(
size_t capa, Allocator* alloc = detail::DefaultAllocator::instance())
: AllocatorOwner(alloc), JsonDocument(allocPool(capa)) {}
// Copy-constructor
BasicJsonDocument(const BasicJsonDocument& src)
: AllocatorOwner(src), JsonDocument() {
copyAssignFrom(src);
}
// Move-constructor
BasicJsonDocument(BasicJsonDocument&& src) : AllocatorOwner(src) {
moveAssignFrom(src);
}
BasicJsonDocument(const JsonDocument& src)
: AllocatorOwner(detail::DefaultAllocator::instance()) {
copyAssignFrom(src);
}
// Construct from variant, array, or object
template <typename T>
BasicJsonDocument(const T& src,
typename detail::enable_if<
detail::is_same<T, JsonVariant>::value ||
detail::is_same<T, JsonVariantConst>::value ||
detail::is_same<T, JsonArray>::value ||
detail::is_same<T, JsonArrayConst>::value ||
detail::is_same<T, JsonObject>::value ||
detail::is_same<T, JsonObjectConst>::value>::type* = 0)
: BasicJsonDocument(src.memoryUsage()) {
set(src);
}
// disambiguate
BasicJsonDocument(JsonVariant src)
: AllocatorOwner(detail::DefaultAllocator::instance()),
JsonDocument(allocPool(src.memoryUsage())) {
set(src);
}
~BasicJsonDocument() {
freePool();
}
BasicJsonDocument& operator=(const BasicJsonDocument& src) {
copyAssignFrom(src);
return *this;
}
BasicJsonDocument& operator=(BasicJsonDocument&& src) {
moveAssignFrom(src);
return *this;
}
template <typename T>
BasicJsonDocument& operator=(const T& src) {
size_t requiredSize = src.memoryUsage();
if (requiredSize > capacity())
reallocPool(requiredSize);
set(src);
return *this;
}
// Reduces the capacity of the memory pool to match the current usage.
// https://arduinojson.org/v6/api/basicjsondocument/shrinktofit/
void shrinkToFit() {
ptrdiff_t bytes_reclaimed = _pool.squash();
if (bytes_reclaimed == 0)
return;
void* old_ptr = _pool.buffer();
void* new_ptr = this->reallocate(old_ptr, _pool.capacity());
ptrdiff_t ptr_offset =
static_cast<char*>(new_ptr) - static_cast<char*>(old_ptr);
_pool.movePointers(ptr_offset);
_data.movePointers(ptr_offset, ptr_offset - bytes_reclaimed);
}
// Reclaims the memory leaked when removing and replacing values.
// https://arduinojson.org/v6/api/jsondocument/garbagecollect/
bool garbageCollect() {
// make a temporary clone and move assign
BasicJsonDocument tmp(*this);
if (!tmp.capacity())
return false;
tmp.set(*this);
moveAssignFrom(tmp);
return true;
}
using AllocatorOwner::allocator;
private:
detail::MemoryPool allocPool(size_t requiredSize) {
size_t capa = detail::addPadding(requiredSize);
return {reinterpret_cast<char*>(this->allocate(capa)), capa};
}
void reallocPool(size_t requiredSize) {
size_t capa = detail::addPadding(requiredSize);
if (capa == _pool.capacity())
return;
freePool();
replacePool(allocPool(detail::addPadding(requiredSize)));
}
void freePool() {
this->deallocate(getPool()->buffer());
}
void copyAssignFrom(const JsonDocument& src) {
reallocPool(src.capacity());
set(src);
}
void moveAssignFrom(BasicJsonDocument& src) {
freePool();
_data = src._data;
_pool = src._pool;
src._data.setNull();
src._pool = {0, 0};
}
};
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@ -4,12 +4,169 @@
#pragma once
#include <ArduinoJson/Document/BasicJsonDocument.hpp>
#include <ArduinoJson/Document/JsonDocument.hpp>
#include <ArduinoJson/Memory/Allocator.hpp>
#include <stdlib.h> // malloc, free
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// A JsonDocument with a memory pool in the heap.
// Helper to implement the "base-from-member" idiom
// (we need to store the allocator before constructing JsonDocument)
class AllocatorOwner {
public:
AllocatorOwner(Allocator* allocator) : _allocator(allocator) {}
void* allocate(size_t size) {
return _allocator->allocate(size);
}
void deallocate(void* ptr) {
if (ptr)
_allocator->deallocate(ptr);
}
void* reallocate(void* ptr, size_t new_size) {
return _allocator->reallocate(ptr, new_size);
}
Allocator* allocator() {
return _allocator;
}
private:
Allocator* _allocator;
};
// A JsonDocument that uses the provided allocator to allocate its memory pool.
// https://arduinojson.org/v6/api/dynamicjsondocument/
typedef BasicJsonDocument DynamicJsonDocument;
class DynamicJsonDocument : AllocatorOwner, public JsonDocument {
public:
explicit DynamicJsonDocument(
size_t capa, Allocator* alloc = detail::DefaultAllocator::instance())
: AllocatorOwner(alloc), JsonDocument(allocPool(capa)) {}
// Copy-constructor
DynamicJsonDocument(const DynamicJsonDocument& src)
: AllocatorOwner(src), JsonDocument() {
copyAssignFrom(src);
}
// Move-constructor
DynamicJsonDocument(DynamicJsonDocument&& src) : AllocatorOwner(src) {
moveAssignFrom(src);
}
DynamicJsonDocument(const JsonDocument& src)
: AllocatorOwner(detail::DefaultAllocator::instance()) {
copyAssignFrom(src);
}
// Construct from variant, array, or object
template <typename T>
DynamicJsonDocument(
const T& src, typename detail::enable_if<
detail::is_same<T, JsonVariant>::value ||
detail::is_same<T, JsonVariantConst>::value ||
detail::is_same<T, JsonArray>::value ||
detail::is_same<T, JsonArrayConst>::value ||
detail::is_same<T, JsonObject>::value ||
detail::is_same<T, JsonObjectConst>::value>::type* = 0)
: DynamicJsonDocument(src.memoryUsage()) {
set(src);
}
// disambiguate
DynamicJsonDocument(JsonVariant src)
: AllocatorOwner(detail::DefaultAllocator::instance()),
JsonDocument(allocPool(src.memoryUsage())) {
set(src);
}
~DynamicJsonDocument() {
freePool();
}
DynamicJsonDocument& operator=(const DynamicJsonDocument& src) {
copyAssignFrom(src);
return *this;
}
DynamicJsonDocument& operator=(DynamicJsonDocument&& src) {
moveAssignFrom(src);
return *this;
}
template <typename T>
DynamicJsonDocument& operator=(const T& src) {
size_t requiredSize = src.memoryUsage();
if (requiredSize > capacity())
reallocPool(requiredSize);
set(src);
return *this;
}
// Reduces the capacity of the memory pool to match the current usage.
// https://arduinojson.org/v6/api/dynamicjsondocument/shrinktofit/
void shrinkToFit() {
ptrdiff_t bytes_reclaimed = _pool.squash();
if (bytes_reclaimed == 0)
return;
void* old_ptr = _pool.buffer();
void* new_ptr = this->reallocate(old_ptr, _pool.capacity());
ptrdiff_t ptr_offset =
static_cast<char*>(new_ptr) - static_cast<char*>(old_ptr);
_pool.movePointers(ptr_offset);
_data.movePointers(ptr_offset, ptr_offset - bytes_reclaimed);
}
// Reclaims the memory leaked when removing and replacing values.
// https://arduinojson.org/v6/api/jsondocument/garbagecollect/
bool garbageCollect() {
// make a temporary clone and move assign
DynamicJsonDocument tmp(*this);
if (!tmp.capacity())
return false;
tmp.set(*this);
moveAssignFrom(tmp);
return true;
}
using AllocatorOwner::allocator;
private:
detail::MemoryPool allocPool(size_t requiredSize) {
size_t capa = detail::addPadding(requiredSize);
return {reinterpret_cast<char*>(this->allocate(capa)), capa};
}
void reallocPool(size_t requiredSize) {
size_t capa = detail::addPadding(requiredSize);
if (capa == _pool.capacity())
return;
freePool();
replacePool(allocPool(detail::addPadding(requiredSize)));
}
void freePool() {
this->deallocate(getPool()->buffer());
}
void copyAssignFrom(const JsonDocument& src) {
reallocPool(src.capacity());
set(src);
}
void moveAssignFrom(DynamicJsonDocument& src) {
freePool();
_data = src._data;
_pool = src._pool;
src._data.setNull();
src._pool = {0, 0};
}
};
ARDUINOJSON_END_PUBLIC_NAMESPACE