From db9258bcd7048e43f15d0160c9a583d7460ce2a8 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Mon, 20 Mar 2023 14:49:08 +0100 Subject: [PATCH] Remove `BasicJsonDocument` --- CHANGELOG.md | 1 + extras/tests/JsonDocument/CMakeLists.txt | 2 +- .../{BasicJsonDocument.cpp => allocator.cpp} | 30 +-- extras/tests/JsonDocument/shrinkToFit.cpp | 14 +- .../Document/BasicJsonDocument.hpp | 172 ------------------ .../Document/DynamicJsonDocument.hpp | 163 ++++++++++++++++- 6 files changed, 178 insertions(+), 204 deletions(-) rename extras/tests/JsonDocument/{BasicJsonDocument.cpp => allocator.cpp} (85%) delete mode 100644 src/ArduinoJson/Document/BasicJsonDocument.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index d9c1f9f6..57c9dddd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,5 +4,6 @@ ArduinoJson: change log HEAD ---- +* Remove `BasicJsonDocument` * Remove `StaticJsonDocument` * Add abstract `Allocator` class diff --git a/extras/tests/JsonDocument/CMakeLists.txt b/extras/tests/JsonDocument/CMakeLists.txt index a4f6db44..878e6cb2 100644 --- a/extras/tests/JsonDocument/CMakeLists.txt +++ b/extras/tests/JsonDocument/CMakeLists.txt @@ -4,7 +4,7 @@ add_executable(JsonDocumentTests add.cpp - BasicJsonDocument.cpp + allocator.cpp cast.cpp compare.cpp containsKey.cpp diff --git a/extras/tests/JsonDocument/BasicJsonDocument.cpp b/extras/tests/JsonDocument/allocator.cpp similarity index 85% rename from extras/tests/JsonDocument/BasicJsonDocument.cpp rename to extras/tests/JsonDocument/allocator.cpp index 7711f648..7a61d91c 100644 --- a/extras/tests/JsonDocument/BasicJsonDocument.cpp +++ b/extras/tests/JsonDocument/allocator.cpp @@ -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() == "The size of this string is 32!!"); REQUIRE(doc2.as() == "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() == "The size of this string is 32!!"); REQUIRE(doc1.as() == "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}"); diff --git a/extras/tests/JsonDocument/shrinkToFit.cpp b/extras/tests/JsonDocument/shrinkToFit.cpp index bc2cb674..d3ac7be5 100644 --- a/extras/tests/JsonDocument/shrinkToFit.cpp +++ b/extras/tests/JsonDocument/shrinkToFit.cpp @@ -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\"]}"); -} diff --git a/src/ArduinoJson/Document/BasicJsonDocument.hpp b/src/ArduinoJson/Document/BasicJsonDocument.hpp deleted file mode 100644 index b87b687d..00000000 --- a/src/ArduinoJson/Document/BasicJsonDocument.hpp +++ /dev/null @@ -1,172 +0,0 @@ -// ArduinoJson - https://arduinojson.org -// Copyright © 2014-2023, Benoit BLANCHON -// MIT License - -#pragma once - -#include -#include - -#include // 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 - BasicJsonDocument(const T& src, - typename detail::enable_if< - detail::is_same::value || - detail::is_same::value || - detail::is_same::value || - detail::is_same::value || - detail::is_same::value || - detail::is_same::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 - 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(new_ptr) - static_cast(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(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 diff --git a/src/ArduinoJson/Document/DynamicJsonDocument.hpp b/src/ArduinoJson/Document/DynamicJsonDocument.hpp index 550503fc..7c24544d 100644 --- a/src/ArduinoJson/Document/DynamicJsonDocument.hpp +++ b/src/ArduinoJson/Document/DynamicJsonDocument.hpp @@ -4,12 +4,169 @@ #pragma once -#include +#include +#include + +#include // 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 + DynamicJsonDocument( + const T& src, typename detail::enable_if< + detail::is_same::value || + detail::is_same::value || + detail::is_same::value || + detail::is_same::value || + detail::is_same::value || + detail::is_same::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 + 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(new_ptr) - static_cast(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(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