mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-07-17 04:22:18 +02:00
JsonArray::remove() and JsonObject::remove() now release the memory of the variant
This commit is contained in:
@ -8,6 +8,7 @@ HEAD
|
||||
* Removed `JsonObject::is<T>(k)` and `JsonObject::set(k,v)`
|
||||
* Replaced `T JsonArray::get<T>(i)` with `JsonVariant JsonArray::get(i)`
|
||||
* Replaced `T JsonObject::get<T>(k)` with `JsonVariant JsonObject::get(k)`
|
||||
* `JsonArray::remove()` and `JsonObject::remove()` now release the memory of the variant
|
||||
|
||||
v6.5.0-beta (2018-10-13)
|
||||
-----------
|
||||
|
@ -13,10 +13,11 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
inline JsonVariantData* arrayAdd(JsonArrayData* arr, MemoryPool* pool) {
|
||||
if (!arr) return 0;
|
||||
|
||||
Slot* slot = new (pool) Slot();
|
||||
Slot* slot = pool->allocSlot();
|
||||
if (!slot) return 0;
|
||||
|
||||
slot->next = 0;
|
||||
slot->value.type = JSON_NULL;
|
||||
|
||||
if (arr->tail) {
|
||||
slot->prev = arr->tail;
|
||||
@ -41,7 +42,7 @@ inline JsonVariantData* arrayGet(const JsonArrayData* arr, size_t index) {
|
||||
return slot ? &slot->value : 0;
|
||||
}
|
||||
|
||||
inline void arrayRemove(JsonArrayData* arr, Slot* slot) {
|
||||
inline void arrayRemove(JsonArrayData* arr, Slot* slot, MemoryPool* pool) {
|
||||
if (!arr || !slot) return;
|
||||
|
||||
if (slot->prev)
|
||||
@ -52,10 +53,12 @@ inline void arrayRemove(JsonArrayData* arr, Slot* slot) {
|
||||
slot->next->prev = slot->prev;
|
||||
else
|
||||
arr->tail = slot->prev;
|
||||
|
||||
slotFree(slot, pool);
|
||||
}
|
||||
|
||||
inline void arrayRemove(JsonArrayData* arr, size_t index) {
|
||||
arrayRemove(arr, arrayGetSlot(arr, index));
|
||||
inline void arrayRemove(JsonArrayData* arr, size_t index, MemoryPool* pool) {
|
||||
arrayRemove(arr, arrayGetSlot(arr, index), pool);
|
||||
}
|
||||
|
||||
inline void arrayClear(JsonArrayData* arr) {
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Memory/MemoryPool.hpp"
|
||||
#include "JsonVariantData.hpp"
|
||||
#include "SlotFunctions.hpp"
|
||||
|
||||
@ -28,10 +29,11 @@ inline bool objectContainsKey(const JsonObjectData* obj, const TKey& key) {
|
||||
template <typename TKey>
|
||||
inline JsonVariantData* objectAdd(JsonObjectData* obj, TKey key,
|
||||
MemoryPool* pool) {
|
||||
Slot* slot = new (pool) Slot();
|
||||
Slot* slot = pool->allocSlot();
|
||||
if (!slot) return 0;
|
||||
|
||||
slot->next = 0;
|
||||
slot->value.type = JSON_NULL;
|
||||
|
||||
if (obj->tail) {
|
||||
slot->prev = obj->tail;
|
||||
@ -74,7 +76,7 @@ inline void objectClear(JsonObjectData* obj) {
|
||||
obj->tail = 0;
|
||||
}
|
||||
|
||||
inline void objectRemove(JsonObjectData* obj, Slot* slot) {
|
||||
inline void objectRemove(JsonObjectData* obj, Slot* slot, MemoryPool* pool) {
|
||||
if (!obj) return;
|
||||
if (!slot) return;
|
||||
if (slot->prev)
|
||||
@ -85,6 +87,8 @@ inline void objectRemove(JsonObjectData* obj, Slot* slot) {
|
||||
slot->next->prev = slot->prev;
|
||||
else
|
||||
obj->tail = slot->prev;
|
||||
|
||||
slotFree(slot, pool);
|
||||
}
|
||||
|
||||
inline size_t objectSize(const JsonObjectData* obj) {
|
||||
|
@ -4,12 +4,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Memory/AllocableInMemoryPool.hpp"
|
||||
#include "JsonVariantData.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
struct Slot : AllocableInMemoryPool {
|
||||
struct Slot {
|
||||
JsonVariantData value;
|
||||
struct Slot* next;
|
||||
struct Slot* prev;
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Memory/MemoryPool.hpp"
|
||||
#include "../Strings/StringTypes.hpp"
|
||||
#include "JsonVariantData.hpp"
|
||||
#include "Slot.hpp"
|
||||
@ -56,4 +57,15 @@ inline size_t slotSize(const Slot* slot) {
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
inline void slotFree(Slot* slot, MemoryPool* pool) {
|
||||
const JsonVariantData& v = slot->value;
|
||||
if (v.type == JSON_ARRAY || v.type == JSON_OBJECT) {
|
||||
for (Slot* s = v.content.asObject.head; s; s = s->next) {
|
||||
slotFree(s, pool);
|
||||
}
|
||||
}
|
||||
|
||||
pool->freeSlot(slot);
|
||||
}
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -197,12 +197,12 @@ class JsonArray : public JsonArrayProxy<JsonArrayData>, public Visitable {
|
||||
|
||||
// Removes element at specified position.
|
||||
FORCE_INLINE void remove(iterator it) const {
|
||||
arrayRemove(_data, it.internal());
|
||||
arrayRemove(_data, it.internal(), _memoryPool);
|
||||
}
|
||||
|
||||
// Removes element at specified index.
|
||||
FORCE_INLINE void remove(size_t index) const {
|
||||
arrayRemove(_data, index);
|
||||
arrayRemove(_data, index, _memoryPool);
|
||||
}
|
||||
|
||||
template <typename Visitor>
|
||||
|
@ -227,7 +227,7 @@ class JsonObject : public JsonObjectProxy<JsonObjectData>, public Visitable {
|
||||
}
|
||||
|
||||
FORCE_INLINE void remove(iterator it) const {
|
||||
objectRemove(_data, it.internal());
|
||||
objectRemove(_data, it.internal(), _memoryPool);
|
||||
}
|
||||
|
||||
// Removes the specified key and the associated value.
|
||||
@ -278,7 +278,7 @@ class JsonObject : public JsonObjectProxy<JsonObjectData>, public Visitable {
|
||||
|
||||
template <typename TStringRef>
|
||||
FORCE_INLINE void remove_impl(TStringRef key) const {
|
||||
objectRemove(_data, objectFindSlot(_data, key));
|
||||
objectRemove(_data, objectFindSlot(_data, key), _memoryPool);
|
||||
}
|
||||
|
||||
MemoryPool* _memoryPool;
|
||||
|
@ -1,19 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MemoryPool.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class AllocableInMemoryPool {
|
||||
public:
|
||||
void *operator new(size_t n, MemoryPool *memoryPool) NOEXCEPT {
|
||||
return memoryPool->alloc(n);
|
||||
}
|
||||
|
||||
void operator delete(void *, MemoryPool *)NOEXCEPT {}
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -58,7 +58,7 @@ class DynamicMemoryPoolBase : public MemoryPool {
|
||||
}
|
||||
|
||||
// Gets the number of bytes occupied in the memoryPool
|
||||
size_t size() const {
|
||||
virtual size_t allocated_bytes() const {
|
||||
size_t total = 0;
|
||||
for (const Block* b = _head; b; b = b->next) total += b->size;
|
||||
return total;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "../Configuration.hpp"
|
||||
#include "../Data/Slot.hpp"
|
||||
#include "../Polyfills/attributes.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
@ -23,12 +24,36 @@ class MemoryPool {
|
||||
|
||||
virtual char *realloc(char *oldPtr, size_t oldSize, size_t newSize) = 0;
|
||||
|
||||
Slot *allocSlot() {
|
||||
if (_freeSlots) {
|
||||
Slot *s = _freeSlots;
|
||||
_freeSlots = s->next;
|
||||
return s;
|
||||
}
|
||||
return reinterpret_cast<Slot *>(alloc(sizeof(Slot)));
|
||||
}
|
||||
|
||||
void freeSlot(Slot *slot) {
|
||||
slot->next = _freeSlots;
|
||||
_freeSlots = slot;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
size_t result = allocated_bytes();
|
||||
for (Slot *s = _freeSlots; s; s = s->next) result -= sizeof(Slot);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected:
|
||||
MemoryPool() : _freeSlots(0) {}
|
||||
|
||||
// CAUTION: NO VIRTUAL DESTRUCTOR!
|
||||
// If we add a virtual constructor the Arduino compiler will add malloc()
|
||||
// and free() to the binary, adding 706 useless bytes.
|
||||
~MemoryPool() {}
|
||||
|
||||
virtual size_t allocated_bytes() const = 0;
|
||||
|
||||
// Preserve aligment if necessary
|
||||
static FORCE_INLINE size_t round_size_up(size_t bytes) {
|
||||
#if ARDUINOJSON_ENABLE_ALIGNMENT
|
||||
@ -38,5 +63,8 @@ class MemoryPool {
|
||||
return bytes;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
Slot *_freeSlots;
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -18,11 +18,6 @@ class StaticMemoryPoolBase : public MemoryPool {
|
||||
return _capacity;
|
||||
}
|
||||
|
||||
// Gets the current usage of the memoryPool in bytes
|
||||
size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
// Allocates the specified amount of bytes in the memoryPool
|
||||
virtual char* alloc(size_t bytes) {
|
||||
alignNextAlloc();
|
||||
@ -53,6 +48,11 @@ class StaticMemoryPoolBase : public MemoryPool {
|
||||
|
||||
~StaticMemoryPoolBase() {}
|
||||
|
||||
// Gets the current usage of the memoryPool in bytes
|
||||
virtual size_t allocated_bytes() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
private:
|
||||
void alignNextAlloc() {
|
||||
_size = round_size_up(_size);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
add_executable(DynamicMemoryPoolTests
|
||||
alloc.cpp
|
||||
allocSlot.cpp
|
||||
no_memory.cpp
|
||||
size.cpp
|
||||
startString.cpp
|
||||
|
27
test/DynamicMemoryPool/allocSlot.cpp
Normal file
27
test/DynamicMemoryPool/allocSlot.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
@ -26,4 +26,23 @@ TEST_CASE("DynamicMemoryPool::size()") {
|
||||
memoryPool.clear();
|
||||
REQUIRE(0 == memoryPool.size());
|
||||
}
|
||||
|
||||
SECTION("Increases after allocSlot()") {
|
||||
memoryPool.allocSlot();
|
||||
REQUIRE(sizeof(Slot) == memoryPool.size());
|
||||
|
||||
memoryPool.allocSlot();
|
||||
REQUIRE(2 * sizeof(Slot) == memoryPool.size());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -92,4 +92,66 @@ TEST_CASE("DynamicJsonDocument") {
|
||||
REQUIRE(json == "{\"hello\":\"world\"}");
|
||||
REQUIRE(ddoc.nestingLimit == 42);
|
||||
}
|
||||
|
||||
SECTION("memoryUsage()") {
|
||||
typedef ARDUINOJSON_NAMESPACE::Slot Slot;
|
||||
|
||||
SECTION("Increases after adding value to array") {
|
||||
JsonArray arr = doc.to<JsonArray>();
|
||||
|
||||
arr.add(42);
|
||||
REQUIRE(sizeof(Slot) == doc.memoryUsage());
|
||||
arr.add(43);
|
||||
REQUIRE(2 * sizeof(Slot) == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("Decreases after remove value from array") {
|
||||
JsonArray arr = doc.to<JsonArray>();
|
||||
arr.add(42);
|
||||
arr.add(43);
|
||||
|
||||
arr.remove(1);
|
||||
REQUIRE(sizeof(Slot) == doc.memoryUsage());
|
||||
arr.remove(0);
|
||||
REQUIRE(0 == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("Increases after adding value to object") {
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
|
||||
obj["a"] = 1;
|
||||
REQUIRE(sizeof(Slot) == doc.memoryUsage());
|
||||
obj["b"] = 2;
|
||||
REQUIRE(2 * sizeof(Slot) == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("Decreases after removing value from object") {
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj["a"] = 1;
|
||||
obj["b"] = 2;
|
||||
|
||||
obj.remove("a");
|
||||
REQUIRE(sizeof(Slot) == doc.memoryUsage());
|
||||
obj.remove("b");
|
||||
REQUIRE(0 == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("Decreases after removing nested object from array") {
|
||||
JsonArray arr = doc.to<JsonArray>();
|
||||
JsonObject obj = arr.createNestedObject();
|
||||
obj["hello"] = "world";
|
||||
|
||||
arr.remove(0);
|
||||
REQUIRE(0 == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("Decreases after removing nested array from object") {
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
JsonArray arr = obj.createNestedArray("hello");
|
||||
arr.add("world");
|
||||
|
||||
obj.remove("hello");
|
||||
REQUIRE(0 == doc.memoryUsage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user