forked from bblanchon/ArduinoJson
Decouple VariantData
from MemoryPool
This commit is contained in:
@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
#include "Allocators.hpp"
|
||||||
|
|
||||||
using ArduinoJson::detail::sizeofArray;
|
using ArduinoJson::detail::sizeofArray;
|
||||||
using ArduinoJson::detail::sizeofObject;
|
using ArduinoJson::detail::sizeofObject;
|
||||||
using ArduinoJson::detail::sizeofString;
|
using ArduinoJson::detail::sizeofString;
|
||||||
@ -388,3 +390,19 @@ TEST_CASE("Deduplicate keys") {
|
|||||||
CHECK(key1 == key2);
|
CHECK(key1 == key2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("MemberProxy under memory constraints") {
|
||||||
|
ControllableAllocator allocator;
|
||||||
|
JsonDocument doc(4096, &allocator);
|
||||||
|
|
||||||
|
SECTION("key allocation fails") {
|
||||||
|
allocator.disable();
|
||||||
|
|
||||||
|
doc[std::string("hello")] = "world";
|
||||||
|
|
||||||
|
REQUIRE(doc.is<JsonObject>());
|
||||||
|
REQUIRE(doc.size() == 0);
|
||||||
|
REQUIRE(doc.memoryUsage() == sizeofObject(1));
|
||||||
|
REQUIRE(doc.overflowed() == true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,12 +4,15 @@
|
|||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
#include "Allocators.hpp"
|
||||||
|
|
||||||
using ArduinoJson::detail::sizeofString;
|
using ArduinoJson::detail::sizeofString;
|
||||||
|
|
||||||
TEST_CASE("JsonVariant::set(JsonVariant)") {
|
TEST_CASE("JsonVariant::set(JsonVariant)") {
|
||||||
JsonDocument doc1(4096);
|
ControllableAllocator allocator;
|
||||||
JsonDocument doc2(4096);
|
SpyingAllocator spyingAllocator(&allocator);
|
||||||
|
JsonDocument doc1(4096, &spyingAllocator);
|
||||||
|
JsonDocument doc2(4096, &spyingAllocator);
|
||||||
JsonVariant var1 = doc1.to<JsonVariant>();
|
JsonVariant var1 = doc1.to<JsonVariant>();
|
||||||
JsonVariant var2 = doc2.to<JsonVariant>();
|
JsonVariant var2 = doc2.to<JsonVariant>();
|
||||||
|
|
||||||
@ -37,53 +40,103 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
|
|||||||
|
|
||||||
SECTION("stores const char* by reference") {
|
SECTION("stores const char* by reference") {
|
||||||
var1.set("hello!!");
|
var1.set("hello!!");
|
||||||
|
spyingAllocator.clearLog();
|
||||||
|
|
||||||
var2.set(var1);
|
var2.set(var1);
|
||||||
|
|
||||||
REQUIRE(doc1.memoryUsage() == 0);
|
REQUIRE(doc1.memoryUsage() == 0);
|
||||||
REQUIRE(doc2.memoryUsage() == 0);
|
REQUIRE(doc2.memoryUsage() == 0);
|
||||||
|
REQUIRE(spyingAllocator.log() == AllocatorLog());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("stores char* by copy") {
|
SECTION("stores char* by copy") {
|
||||||
char str[] = "hello!!";
|
char str[] = "hello!!";
|
||||||
|
|
||||||
var1.set(str);
|
var1.set(str);
|
||||||
|
spyingAllocator.clearLog();
|
||||||
|
|
||||||
var2.set(var1);
|
var2.set(var1);
|
||||||
|
|
||||||
REQUIRE(doc1.memoryUsage() == sizeofString(7));
|
REQUIRE(doc1.memoryUsage() == sizeofString(7));
|
||||||
REQUIRE(doc2.memoryUsage() == sizeofString(7));
|
REQUIRE(doc2.memoryUsage() == sizeofString(7));
|
||||||
|
REQUIRE(spyingAllocator.log() ==
|
||||||
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString((7))));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("fails gracefully if string allocation fails") {
|
||||||
|
char str[] = "hello!!";
|
||||||
|
var1.set(str);
|
||||||
|
allocator.disable();
|
||||||
|
spyingAllocator.clearLog();
|
||||||
|
|
||||||
|
var2.set(var1);
|
||||||
|
|
||||||
|
REQUIRE(doc1.memoryUsage() == sizeofString(7));
|
||||||
|
REQUIRE(doc2.memoryUsage() == 0);
|
||||||
|
REQUIRE(doc2.overflowed() == true);
|
||||||
|
REQUIRE(spyingAllocator.log() ==
|
||||||
|
AllocatorLog() << AllocatorLog::AllocateFail(sizeofString((7))));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("stores std::string by copy") {
|
SECTION("stores std::string by copy") {
|
||||||
var1.set(std::string("hello!!"));
|
var1.set(std::string("hello!!"));
|
||||||
|
spyingAllocator.clearLog();
|
||||||
|
|
||||||
var2.set(var1);
|
var2.set(var1);
|
||||||
|
|
||||||
REQUIRE(doc1.memoryUsage() == sizeofString(7));
|
REQUIRE(doc1.memoryUsage() == sizeofString(7));
|
||||||
REQUIRE(doc2.memoryUsage() == sizeofString(7));
|
REQUIRE(doc2.memoryUsage() == sizeofString(7));
|
||||||
|
REQUIRE(spyingAllocator.log() ==
|
||||||
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString((7))));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("stores Serialized<const char*> by reference") {
|
SECTION("stores Serialized<const char*> by reference") {
|
||||||
var1.set(serialized("hello!!", 8));
|
var1.set(serialized("hello!!", 8));
|
||||||
|
spyingAllocator.clearLog();
|
||||||
|
|
||||||
var2.set(var1);
|
var2.set(var1);
|
||||||
|
|
||||||
REQUIRE(doc1.memoryUsage() == 0);
|
REQUIRE(doc1.memoryUsage() == 0);
|
||||||
REQUIRE(doc2.memoryUsage() == 0);
|
REQUIRE(doc2.memoryUsage() == 0);
|
||||||
|
REQUIRE(spyingAllocator.log() == AllocatorLog());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("stores Serialized<char*> by copy") {
|
SECTION("stores Serialized<char*> by copy") {
|
||||||
char str[] = "hello!!";
|
char str[] = "hello!!";
|
||||||
var1.set(serialized(str, 7));
|
var1.set(serialized(str, 7));
|
||||||
|
spyingAllocator.clearLog();
|
||||||
|
|
||||||
var2.set(var1);
|
var2.set(var1);
|
||||||
|
|
||||||
REQUIRE(doc1.memoryUsage() == sizeofString(7));
|
REQUIRE(doc1.memoryUsage() == sizeofString(7));
|
||||||
REQUIRE(doc2.memoryUsage() == sizeofString(7));
|
REQUIRE(doc2.memoryUsage() == sizeofString(7));
|
||||||
|
REQUIRE(spyingAllocator.log() ==
|
||||||
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString((7))));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("stores Serialized<std::string> by copy") {
|
SECTION("stores Serialized<std::string> by copy") {
|
||||||
var1.set(serialized(std::string("hello!!")));
|
var1.set(serialized(std::string("hello!!")));
|
||||||
|
spyingAllocator.clearLog();
|
||||||
|
|
||||||
var2.set(var1);
|
var2.set(var1);
|
||||||
|
|
||||||
REQUIRE(doc1.memoryUsage() == sizeofString(7));
|
REQUIRE(doc1.memoryUsage() == sizeofString(7));
|
||||||
REQUIRE(doc2.memoryUsage() == sizeofString(7));
|
REQUIRE(doc2.memoryUsage() == sizeofString(7));
|
||||||
|
REQUIRE(spyingAllocator.log() ==
|
||||||
|
AllocatorLog() << AllocatorLog::Allocate(sizeofString((7))));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("fails gracefully if raw string allocation fails") {
|
||||||
|
var1.set(serialized(std::string("hello!!")));
|
||||||
|
allocator.disable();
|
||||||
|
spyingAllocator.clearLog();
|
||||||
|
|
||||||
|
var2.set(var1);
|
||||||
|
|
||||||
|
REQUIRE(doc1.memoryUsage() == sizeofString(7));
|
||||||
|
REQUIRE(doc2.memoryUsage() == 0);
|
||||||
|
REQUIRE(doc2.overflowed() == true);
|
||||||
|
REQUIRE(spyingAllocator.log() ==
|
||||||
|
AllocatorLog() << AllocatorLog::AllocateFail(sizeofString((7))));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("destination is unbound") {
|
SECTION("destination is unbound") {
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <ArduinoJson/Array/ElementProxy.hpp>
|
#include <ArduinoJson/Array/ElementProxy.hpp>
|
||||||
#include <ArduinoJson/Array/JsonArrayConst.hpp>
|
#include <ArduinoJson/Array/JsonArrayConst.hpp>
|
||||||
|
#include <ArduinoJson/Collection/CollectionFunctions.hpp>
|
||||||
|
|
||||||
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
|
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
|
||||||
|
|
||||||
@ -43,9 +44,7 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
|
|||||||
// Returns a reference to the new element.
|
// Returns a reference to the new element.
|
||||||
// https://arduinojson.org/v6/api/jsonarray/add/
|
// https://arduinojson.org/v6/api/jsonarray/add/
|
||||||
JsonVariant add() const {
|
JsonVariant add() const {
|
||||||
if (!_data)
|
return JsonVariant(_pool, collectionAddElement(_data, _pool));
|
||||||
return JsonVariant();
|
|
||||||
return JsonVariant(_pool, _data->addElement(_pool));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Appends a value to the array.
|
// Appends a value to the array.
|
||||||
@ -79,9 +78,7 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
|
|||||||
// Copies an array.
|
// Copies an array.
|
||||||
// https://arduinojson.org/v6/api/jsonarray/set/
|
// https://arduinojson.org/v6/api/jsonarray/set/
|
||||||
FORCE_INLINE bool set(JsonArrayConst src) const {
|
FORCE_INLINE bool set(JsonArrayConst src) const {
|
||||||
if (!_data || !src._data)
|
return collectionCopy(_data, src._data, _pool);
|
||||||
return false;
|
|
||||||
return _data->copyFrom(*src._data, _pool);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compares the content of two arrays.
|
// Compares the content of two arrays.
|
||||||
@ -93,27 +90,21 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
|
|||||||
// ⚠️ Doesn't release the memory associated with the removed element.
|
// ⚠️ Doesn't release the memory associated with the removed element.
|
||||||
// https://arduinojson.org/v6/api/jsonarray/remove/
|
// https://arduinojson.org/v6/api/jsonarray/remove/
|
||||||
FORCE_INLINE void remove(iterator it) const {
|
FORCE_INLINE void remove(iterator it) const {
|
||||||
if (!_data)
|
collectionRemove(_data, it._slot, _pool);
|
||||||
return;
|
|
||||||
_data->removeSlot(it._slot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes the element at the specified index.
|
// Removes the element at the specified index.
|
||||||
// ⚠️ Doesn't release the memory associated with the removed element.
|
// ⚠️ Doesn't release the memory associated with the removed element.
|
||||||
// https://arduinojson.org/v6/api/jsonarray/remove/
|
// https://arduinojson.org/v6/api/jsonarray/remove/
|
||||||
FORCE_INLINE void remove(size_t index) const {
|
FORCE_INLINE void remove(size_t index) const {
|
||||||
if (!_data)
|
collectionRemoveElement(_data, index, _pool);
|
||||||
return;
|
|
||||||
_data->removeElement(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes all the elements of the array.
|
// Removes all the elements of the array.
|
||||||
// ⚠️ Doesn't release the memory associated with the removed elements.
|
// ⚠️ Doesn't release the memory associated with the removed elements.
|
||||||
// https://arduinojson.org/v6/api/jsonarray/clear/
|
// https://arduinojson.org/v6/api/jsonarray/clear/
|
||||||
void clear() const {
|
void clear() const {
|
||||||
if (!_data)
|
collectionClear(_data, _pool);
|
||||||
return;
|
|
||||||
_data->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets or sets the element at the specified index.
|
// Gets or sets the element at the specified index.
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||||
|
|
||||||
class MemoryPool;
|
|
||||||
class VariantData;
|
class VariantData;
|
||||||
class VariantSlot;
|
class VariantSlot;
|
||||||
|
|
||||||
@ -28,30 +27,13 @@ class CollectionData {
|
|||||||
|
|
||||||
// Array only
|
// Array only
|
||||||
|
|
||||||
VariantData* addElement(MemoryPool* pool);
|
|
||||||
|
|
||||||
VariantData* getElement(size_t index) const;
|
VariantData* getElement(size_t index) const;
|
||||||
|
|
||||||
VariantData* getOrAddElement(size_t index, MemoryPool* pool);
|
|
||||||
|
|
||||||
void removeElement(size_t index);
|
|
||||||
|
|
||||||
// Object only
|
// Object only
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
|
||||||
VariantData* addMember(TAdaptedString key, MemoryPool* pool);
|
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
VariantData* getMember(TAdaptedString key) const;
|
VariantData* getMember(TAdaptedString key) const;
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
|
||||||
VariantData* getOrAddMember(TAdaptedString key, MemoryPool* pool);
|
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
|
||||||
void removeMember(TAdaptedString key) {
|
|
||||||
removeSlot(getSlot(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
bool containsKey(const TAdaptedString& key) const;
|
bool containsKey(const TAdaptedString& key) const;
|
||||||
|
|
||||||
@ -61,23 +43,21 @@ class CollectionData {
|
|||||||
size_t memoryUsage() const;
|
size_t memoryUsage() const;
|
||||||
size_t size() const;
|
size_t size() const;
|
||||||
|
|
||||||
VariantSlot* addSlot(MemoryPool*);
|
void addSlot(VariantSlot*);
|
||||||
void removeSlot(VariantSlot* slot);
|
void removeSlot(VariantSlot* slot);
|
||||||
|
|
||||||
bool copyFrom(const CollectionData& src, MemoryPool* pool);
|
|
||||||
|
|
||||||
VariantSlot* head() const {
|
VariantSlot* head() const {
|
||||||
return _head;
|
return _head;
|
||||||
}
|
}
|
||||||
|
|
||||||
void movePointers(ptrdiff_t variantDistance);
|
void movePointers(ptrdiff_t variantDistance);
|
||||||
|
|
||||||
private:
|
|
||||||
VariantSlot* getSlot(size_t index) const;
|
VariantSlot* getSlot(size_t index) const;
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
VariantSlot* getSlot(TAdaptedString key) const;
|
VariantSlot* getSlot(TAdaptedString key) const;
|
||||||
|
|
||||||
|
private:
|
||||||
VariantSlot* getPreviousSlot(VariantSlot*) const;
|
VariantSlot* getPreviousSlot(VariantSlot*) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
93
src/ArduinoJson/Collection/CollectionFunctions.hpp
Normal file
93
src/ArduinoJson/Collection/CollectionFunctions.hpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// ArduinoJson - https://arduinojson.org
|
||||||
|
// Copyright © 2014-2023, Benoit BLANCHON
|
||||||
|
// MIT License
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ArduinoJson/Collection/CollectionData.hpp>
|
||||||
|
|
||||||
|
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||||
|
|
||||||
|
inline VariantData* collectionAddElement(CollectionData* array,
|
||||||
|
MemoryPool* pool) {
|
||||||
|
if (!array)
|
||||||
|
return nullptr;
|
||||||
|
auto slot = pool->allocVariant();
|
||||||
|
if (!slot)
|
||||||
|
return nullptr;
|
||||||
|
array->addSlot(slot);
|
||||||
|
return slot->data();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TAdaptedString>
|
||||||
|
inline VariantData* collectionAddMember(CollectionData* obj, TAdaptedString key,
|
||||||
|
MemoryPool* pool) {
|
||||||
|
ARDUINOJSON_ASSERT(!key.isNull());
|
||||||
|
ARDUINOJSON_ASSERT(obj != nullptr);
|
||||||
|
if (!obj)
|
||||||
|
return nullptr;
|
||||||
|
auto slot = pool->allocVariant();
|
||||||
|
if (!slot)
|
||||||
|
return nullptr;
|
||||||
|
auto storedKey = storeString(pool, key);
|
||||||
|
if (!storedKey)
|
||||||
|
return nullptr;
|
||||||
|
slot->setKey(storedKey);
|
||||||
|
obj->addSlot(slot);
|
||||||
|
return slot->data();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void collectionClear(CollectionData* c, MemoryPool* pool) {
|
||||||
|
if (!c)
|
||||||
|
return;
|
||||||
|
for (auto slot = c->head(); slot; slot = slot->next())
|
||||||
|
slotRelease(slot, pool);
|
||||||
|
c->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool collectionCopy(CollectionData* dst, const CollectionData* src,
|
||||||
|
MemoryPool* pool) {
|
||||||
|
if (!dst || !src)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
collectionClear(dst, pool);
|
||||||
|
|
||||||
|
for (VariantSlot* s = src->head(); s; s = s->next()) {
|
||||||
|
VariantData* var;
|
||||||
|
if (s->key() != 0) {
|
||||||
|
JsonString key(s->key(),
|
||||||
|
s->ownsKey() ? JsonString::Copied : JsonString::Linked);
|
||||||
|
var = collectionAddMember(dst, adaptString(key), pool);
|
||||||
|
} else {
|
||||||
|
var = collectionAddElement(dst, pool);
|
||||||
|
}
|
||||||
|
if (!variantCopyFrom(var, s->data(), pool))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void collectionRemove(CollectionData* data, VariantSlot* slot,
|
||||||
|
MemoryPool* pool) {
|
||||||
|
if (!data || !slot)
|
||||||
|
return;
|
||||||
|
data->removeSlot(slot);
|
||||||
|
slotRelease(slot, pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void collectionRemoveElement(CollectionData* array, size_t index,
|
||||||
|
MemoryPool* pool) {
|
||||||
|
if (!array)
|
||||||
|
return;
|
||||||
|
collectionRemove(array, array->getSlot(index), pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TAdaptedString>
|
||||||
|
inline void collectionRemoveMember(CollectionData* obj, TAdaptedString key,
|
||||||
|
MemoryPool* pool) {
|
||||||
|
if (!obj)
|
||||||
|
return;
|
||||||
|
collectionRemove(obj, obj->getSlot(key), pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
@ -11,41 +11,16 @@
|
|||||||
|
|
||||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||||
|
|
||||||
inline VariantSlot* CollectionData::addSlot(MemoryPool* pool) {
|
inline void CollectionData::addSlot(VariantSlot* slot) {
|
||||||
VariantSlot* slot = pool->allocVariant();
|
ARDUINOJSON_ASSERT(slot != nullptr);
|
||||||
if (!slot)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (_tail) {
|
if (_tail) {
|
||||||
ARDUINOJSON_ASSERT(pool->owns(_tail)); // Can't alter a linked array/object
|
|
||||||
_tail->setNextNotNull(slot);
|
_tail->setNextNotNull(slot);
|
||||||
_tail = slot;
|
_tail = slot;
|
||||||
} else {
|
} else {
|
||||||
_head = slot;
|
_head = slot;
|
||||||
_tail = slot;
|
_tail = slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
slot->clear();
|
|
||||||
return slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline VariantData* CollectionData::addElement(MemoryPool* pool) {
|
|
||||||
return slotData(addSlot(pool));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
|
||||||
inline VariantData* CollectionData::addMember(TAdaptedString key,
|
|
||||||
MemoryPool* pool) {
|
|
||||||
VariantSlot* slot = addSlot(pool);
|
|
||||||
if (!slot)
|
|
||||||
return 0;
|
|
||||||
auto storedKey = storeString(pool, key);
|
|
||||||
if (!storedKey) {
|
|
||||||
removeSlot(slot);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
slot->setKey(storedKey);
|
|
||||||
return slot->data();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void CollectionData::clear() {
|
inline void CollectionData::clear() {
|
||||||
@ -58,26 +33,6 @@ inline bool CollectionData::containsKey(const TAdaptedString& key) const {
|
|||||||
return getSlot(key) != 0;
|
return getSlot(key) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool CollectionData::copyFrom(const CollectionData& src,
|
|
||||||
MemoryPool* pool) {
|
|
||||||
clear();
|
|
||||||
for (VariantSlot* s = src._head; s; s = s->next()) {
|
|
||||||
VariantData* var;
|
|
||||||
if (s->key() != 0) {
|
|
||||||
JsonString key(s->key(),
|
|
||||||
s->ownsKey() ? JsonString::Copied : JsonString::Linked);
|
|
||||||
var = addMember(adaptString(key), pool);
|
|
||||||
} else {
|
|
||||||
var = addElement(pool);
|
|
||||||
}
|
|
||||||
if (!var)
|
|
||||||
return false;
|
|
||||||
if (!var->copyFrom(*s->data(), pool))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
inline VariantSlot* CollectionData::getSlot(TAdaptedString key) const {
|
inline VariantSlot* CollectionData::getSlot(TAdaptedString key) const {
|
||||||
if (key.isNull())
|
if (key.isNull())
|
||||||
@ -114,42 +69,11 @@ inline VariantData* CollectionData::getMember(TAdaptedString key) const {
|
|||||||
return slot ? slot->data() : 0;
|
return slot ? slot->data() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
|
||||||
inline VariantData* CollectionData::getOrAddMember(TAdaptedString key,
|
|
||||||
MemoryPool* pool) {
|
|
||||||
// ignore null key
|
|
||||||
if (key.isNull())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// search a matching key
|
|
||||||
VariantSlot* slot = getSlot(key);
|
|
||||||
if (slot)
|
|
||||||
return slot->data();
|
|
||||||
|
|
||||||
return addMember(key, pool);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline VariantData* CollectionData::getElement(size_t index) const {
|
inline VariantData* CollectionData::getElement(size_t index) const {
|
||||||
VariantSlot* slot = getSlot(index);
|
VariantSlot* slot = getSlot(index);
|
||||||
return slot ? slot->data() : 0;
|
return slot ? slot->data() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline VariantData* CollectionData::getOrAddElement(size_t index,
|
|
||||||
MemoryPool* pool) {
|
|
||||||
VariantSlot* slot = _head;
|
|
||||||
while (slot && index > 0) {
|
|
||||||
slot = slot->next();
|
|
||||||
index--;
|
|
||||||
}
|
|
||||||
if (!slot)
|
|
||||||
index++;
|
|
||||||
while (index > 0) {
|
|
||||||
slot = addSlot(pool);
|
|
||||||
index--;
|
|
||||||
}
|
|
||||||
return slotData(slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void CollectionData::removeSlot(VariantSlot* slot) {
|
inline void CollectionData::removeSlot(VariantSlot* slot) {
|
||||||
if (!slot)
|
if (!slot)
|
||||||
return;
|
return;
|
||||||
@ -163,10 +87,6 @@ inline void CollectionData::removeSlot(VariantSlot* slot) {
|
|||||||
_tail = prev;
|
_tail = prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void CollectionData::removeElement(size_t index) {
|
|
||||||
removeSlot(getSlot(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t CollectionData::memoryUsage() const {
|
inline size_t CollectionData::memoryUsage() const {
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
for (VariantSlot* s = _head; s; s = s->next()) {
|
for (VariantSlot* s = _head; s; s = s->next()) {
|
||||||
|
@ -119,7 +119,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
|||||||
// https://arduinojson.org/v6/api/jsondocument/clear/
|
// https://arduinojson.org/v6/api/jsondocument/clear/
|
||||||
void clear() {
|
void clear() {
|
||||||
_pool.clear();
|
_pool.clear();
|
||||||
_data.setNull();
|
_data.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the root is of the specified type.
|
// Returns true if the root is of the specified type.
|
||||||
@ -297,7 +297,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
|||||||
// Returns a reference to the new element.
|
// Returns a reference to the new element.
|
||||||
// https://arduinojson.org/v6/api/jsondocument/add/
|
// https://arduinojson.org/v6/api/jsondocument/add/
|
||||||
FORCE_INLINE JsonVariant add() {
|
FORCE_INLINE JsonVariant add() {
|
||||||
return JsonVariant(&_pool, _data.addElement(&_pool));
|
return JsonVariant(&_pool, variantAddElement(&_data, &_pool));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Appends a value to the root array.
|
// Appends a value to the root array.
|
||||||
@ -318,7 +318,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
|||||||
// ⚠️ Doesn't release the memory associated with the removed element.
|
// ⚠️ Doesn't release the memory associated with the removed element.
|
||||||
// https://arduinojson.org/v6/api/jsondocument/remove/
|
// https://arduinojson.org/v6/api/jsondocument/remove/
|
||||||
FORCE_INLINE void remove(size_t index) {
|
FORCE_INLINE void remove(size_t index) {
|
||||||
_data.remove(index);
|
variantRemoveElement(getData(), index, getPool());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes a member of the root object.
|
// Removes a member of the root object.
|
||||||
@ -327,7 +327,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
|||||||
template <typename TChar>
|
template <typename TChar>
|
||||||
FORCE_INLINE typename detail::enable_if<detail::IsString<TChar*>::value>::type
|
FORCE_INLINE typename detail::enable_if<detail::IsString<TChar*>::value>::type
|
||||||
remove(TChar* key) {
|
remove(TChar* key) {
|
||||||
_data.remove(detail::adaptString(key));
|
variantRemoveMember(getData(), detail::adaptString(key), getPool());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes a member of the root object.
|
// Removes a member of the root object.
|
||||||
@ -337,7 +337,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
|||||||
FORCE_INLINE
|
FORCE_INLINE
|
||||||
typename detail::enable_if<detail::IsString<TString>::value>::type
|
typename detail::enable_if<detail::IsString<TString>::value>::type
|
||||||
remove(const TString& key) {
|
remove(const TString& key) {
|
||||||
_data.remove(detail::adaptString(key));
|
variantRemoveMember(getData(), detail::adaptString(key), getPool());
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE operator JsonVariant() {
|
FORCE_INLINE operator JsonVariant() {
|
||||||
@ -364,7 +364,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
|||||||
|
|
||||||
void moveAssignFrom(JsonDocument& src) {
|
void moveAssignFrom(JsonDocument& src) {
|
||||||
_data = src._data;
|
_data = src._data;
|
||||||
src._data.setNull();
|
src._data.reset();
|
||||||
_pool = move(src._pool);
|
_pool = move(src._pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ class JsonDeserializer {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
if (memberFilter.allow()) {
|
if (memberFilter.allow()) {
|
||||||
// Allocate slot in array
|
// Allocate slot in array
|
||||||
VariantData* value = array.addElement(_pool);
|
VariantData* value = collectionAddElement(&array, _pool);
|
||||||
if (!value)
|
if (!value)
|
||||||
return DeserializationError::NoMemory;
|
return DeserializationError::NoMemory;
|
||||||
|
|
||||||
@ -280,11 +280,12 @@ class JsonDeserializer {
|
|||||||
key = _stringStorage.save();
|
key = _stringStorage.save();
|
||||||
|
|
||||||
// Allocate slot in object
|
// Allocate slot in object
|
||||||
VariantSlot* slot = object.addSlot(_pool);
|
VariantSlot* slot = _pool->allocVariant();
|
||||||
if (!slot)
|
if (!slot)
|
||||||
return DeserializationError::NoMemory;
|
return DeserializationError::NoMemory;
|
||||||
|
|
||||||
slot->setKey(key);
|
slot->setKey(key);
|
||||||
|
object.addSlot(slot);
|
||||||
|
|
||||||
variant = slot->data();
|
variant = slot->data();
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,10 @@ class MemoryPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VariantSlot* allocVariant() {
|
VariantSlot* allocVariant() {
|
||||||
return allocRight<VariantSlot>();
|
auto slot = allocRight<VariantSlot>();
|
||||||
|
if (slot)
|
||||||
|
slot->clear();
|
||||||
|
return slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
|
@ -435,7 +435,7 @@ class MsgPackDeserializer {
|
|||||||
|
|
||||||
if (memberFilter.allow()) {
|
if (memberFilter.allow()) {
|
||||||
ARDUINOJSON_ASSERT(array != 0);
|
ARDUINOJSON_ASSERT(array != 0);
|
||||||
value = array->addElement(_pool);
|
value = collectionAddElement(array, _pool);
|
||||||
if (!value)
|
if (!value)
|
||||||
return DeserializationError::NoMemory;
|
return DeserializationError::NoMemory;
|
||||||
} else {
|
} else {
|
||||||
@ -496,11 +496,12 @@ class MsgPackDeserializer {
|
|||||||
// Save key in memory pool.
|
// Save key in memory pool.
|
||||||
key = _stringStorage.save();
|
key = _stringStorage.save();
|
||||||
|
|
||||||
VariantSlot* slot = object->addSlot(_pool);
|
VariantSlot* slot = _pool->allocVariant();
|
||||||
if (!slot)
|
if (!slot)
|
||||||
return DeserializationError::NoMemory;
|
return DeserializationError::NoMemory;
|
||||||
|
|
||||||
slot->setKey(key);
|
slot->setKey(key);
|
||||||
|
object->addSlot(slot);
|
||||||
|
|
||||||
member = slot->data();
|
member = slot->data();
|
||||||
} else {
|
} else {
|
||||||
|
@ -87,17 +87,13 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
|||||||
// ⚠️ Doesn't release the memory associated with the removed members.
|
// ⚠️ Doesn't release the memory associated with the removed members.
|
||||||
// https://arduinojson.org/v6/api/jsonobject/clear/
|
// https://arduinojson.org/v6/api/jsonobject/clear/
|
||||||
void clear() const {
|
void clear() const {
|
||||||
if (!_data)
|
collectionClear(_data, _pool);
|
||||||
return;
|
|
||||||
_data->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copies an object.
|
// Copies an object.
|
||||||
// https://arduinojson.org/v6/api/jsonobject/set/
|
// https://arduinojson.org/v6/api/jsonobject/set/
|
||||||
FORCE_INLINE bool set(JsonObjectConst src) {
|
FORCE_INLINE bool set(JsonObjectConst src) {
|
||||||
if (!_data || !src._data)
|
return collectionCopy(_data, src._data, _pool);
|
||||||
return false;
|
|
||||||
return _data->copyFrom(*src._data, _pool);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compares the content of two objects.
|
// Compares the content of two objects.
|
||||||
@ -129,9 +125,7 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
|||||||
// ⚠️ Doesn't release the memory associated with the removed member.
|
// ⚠️ Doesn't release the memory associated with the removed member.
|
||||||
// https://arduinojson.org/v6/api/jsonobject/remove/
|
// https://arduinojson.org/v6/api/jsonobject/remove/
|
||||||
FORCE_INLINE void remove(iterator it) const {
|
FORCE_INLINE void remove(iterator it) const {
|
||||||
if (!_data)
|
collectionRemove(_data, it._slot, _pool);
|
||||||
return;
|
|
||||||
_data->removeSlot(it._slot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes the member with the specified key.
|
// Removes the member with the specified key.
|
||||||
@ -139,7 +133,7 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
|||||||
// https://arduinojson.org/v6/api/jsonobject/remove/
|
// https://arduinojson.org/v6/api/jsonobject/remove/
|
||||||
template <typename TString>
|
template <typename TString>
|
||||||
FORCE_INLINE void remove(const TString& key) const {
|
FORCE_INLINE void remove(const TString& key) const {
|
||||||
removeMember(detail::adaptString(key));
|
collectionRemoveMember(_data, detail::adaptString(key), _pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes the member with the specified key.
|
// Removes the member with the specified key.
|
||||||
@ -147,7 +141,7 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
|||||||
// https://arduinojson.org/v6/api/jsonobject/remove/
|
// https://arduinojson.org/v6/api/jsonobject/remove/
|
||||||
template <typename TChar>
|
template <typename TChar>
|
||||||
FORCE_INLINE void remove(TChar* key) const {
|
FORCE_INLINE void remove(TChar* key) const {
|
||||||
removeMember(detail::adaptString(key));
|
collectionRemoveMember(_data, detail::adaptString(key), _pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the object contains the specified key.
|
// Returns true if the object contains the specified key.
|
||||||
@ -214,9 +208,7 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
|||||||
|
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
void removeMember(TAdaptedString key) const {
|
void removeMember(TAdaptedString key) const {
|
||||||
if (!_data)
|
collectionRemove(_data, _data->getSlot(key), _pool);
|
||||||
return;
|
|
||||||
_data->removeMember(key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
detail::CollectionData* _data;
|
detail::CollectionData* _data;
|
||||||
|
@ -42,10 +42,8 @@ struct Converter<
|
|||||||
!detail::is_same<char, T>::value>::type>
|
!detail::is_same<char, T>::value>::type>
|
||||||
: private detail::VariantAttorney {
|
: private detail::VariantAttorney {
|
||||||
static void toJson(T src, JsonVariant dst) {
|
static void toJson(T src, JsonVariant dst) {
|
||||||
auto data = getData(dst);
|
|
||||||
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
|
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
|
||||||
if (data)
|
variantSetInteger(getData(dst), src, getPool(dst));
|
||||||
data->setInteger(src);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static T fromJson(JsonVariantConst src) {
|
static T fromJson(JsonVariantConst src) {
|
||||||
@ -81,9 +79,7 @@ struct Converter<T, typename detail::enable_if<detail::is_enum<T>::value>::type>
|
|||||||
template <>
|
template <>
|
||||||
struct Converter<bool> : private detail::VariantAttorney {
|
struct Converter<bool> : private detail::VariantAttorney {
|
||||||
static void toJson(bool src, JsonVariant dst) {
|
static void toJson(bool src, JsonVariant dst) {
|
||||||
auto data = getData(dst);
|
variantSetBoolean(getData(dst), src, getPool(dst));
|
||||||
if (data)
|
|
||||||
data->setBoolean(src);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool fromJson(JsonVariantConst src) {
|
static bool fromJson(JsonVariantConst src) {
|
||||||
@ -102,9 +98,7 @@ struct Converter<
|
|||||||
T, typename detail::enable_if<detail::is_floating_point<T>::value>::type>
|
T, typename detail::enable_if<detail::is_floating_point<T>::value>::type>
|
||||||
: private detail::VariantAttorney {
|
: private detail::VariantAttorney {
|
||||||
static void toJson(T src, JsonVariant dst) {
|
static void toJson(T src, JsonVariant dst) {
|
||||||
auto data = getData(dst);
|
variantSetFloat(getData(dst), static_cast<JsonFloat>(src), getPool(dst));
|
||||||
if (data)
|
|
||||||
data->setFloat(static_cast<JsonFloat>(src));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static T fromJson(JsonVariantConst src) {
|
static T fromJson(JsonVariantConst src) {
|
||||||
@ -165,9 +159,7 @@ template <>
|
|||||||
struct Converter<SerializedValue<const char*>>
|
struct Converter<SerializedValue<const char*>>
|
||||||
: private detail::VariantAttorney {
|
: private detail::VariantAttorney {
|
||||||
static void toJson(SerializedValue<const char*> src, JsonVariant dst) {
|
static void toJson(SerializedValue<const char*> src, JsonVariant dst) {
|
||||||
auto data = getData(dst);
|
variantSetLinkedRaw(getData(dst), src, getPool(dst));
|
||||||
if (data)
|
|
||||||
data->setLinkedRaw(src);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -180,17 +172,14 @@ struct Converter<
|
|||||||
typename detail::enable_if<!detail::is_same<const char*, T>::value>::type>
|
typename detail::enable_if<!detail::is_same<const char*, T>::value>::type>
|
||||||
: private detail::VariantAttorney {
|
: private detail::VariantAttorney {
|
||||||
static void toJson(SerializedValue<T> src, JsonVariant dst) {
|
static void toJson(SerializedValue<T> src, JsonVariant dst) {
|
||||||
auto data = getData(dst);
|
variantSetOwnedRaw(getData(dst), src, getPool(dst));
|
||||||
auto pool = getPool(dst);
|
|
||||||
if (data)
|
|
||||||
data->storeOwnedRaw(src, pool);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct Converter<decltype(nullptr)> : private detail::VariantAttorney {
|
struct Converter<decltype(nullptr)> : private detail::VariantAttorney {
|
||||||
static void toJson(decltype(nullptr), JsonVariant dst) {
|
static void toJson(decltype(nullptr), JsonVariant dst) {
|
||||||
variantSetNull(getData(dst));
|
variantSetNull(getData(dst), getPool(dst));
|
||||||
}
|
}
|
||||||
static decltype(nullptr) fromJson(JsonVariantConst) {
|
static decltype(nullptr) fromJson(JsonVariantConst) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -21,4 +21,10 @@ inline size_t slotSize(const VariantSlot* var) {
|
|||||||
inline VariantData* slotData(VariantSlot* slot) {
|
inline VariantData* slotData(VariantSlot* slot) {
|
||||||
return reinterpret_cast<VariantData*>(slot);
|
return reinterpret_cast<VariantData*>(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void slotRelease(const VariantSlot* slot, MemoryPool* pool) {
|
||||||
|
ARDUINOJSON_ASSERT(slot != nullptr);
|
||||||
|
variantRelease(slot->data(), pool);
|
||||||
|
}
|
||||||
|
|
||||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
|
||||||
#include <ArduinoJson/Misc/SerializedValue.hpp>
|
#include <ArduinoJson/Misc/SerializedValue.hpp>
|
||||||
#include <ArduinoJson/Numbers/convertNumber.hpp>
|
#include <ArduinoJson/Numbers/convertNumber.hpp>
|
||||||
#include <ArduinoJson/Strings/JsonString.hpp>
|
#include <ArduinoJson/Strings/JsonString.hpp>
|
||||||
@ -20,6 +19,10 @@ class VariantData {
|
|||||||
public:
|
public:
|
||||||
VariantData() : _flags(VALUE_IS_NULL) {}
|
VariantData() : _flags(VALUE_IS_NULL) {}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
_flags = VALUE_IS_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void operator=(const VariantData& src) {
|
void operator=(const VariantData& src) {
|
||||||
_content = src._content;
|
_content = src._content;
|
||||||
_flags = uint8_t((_flags & OWNED_KEY_BIT) | (src._flags & ~OWNED_KEY_BIT));
|
_flags = uint8_t((_flags & OWNED_KEY_BIT) | (src._flags & ~OWNED_KEY_BIT));
|
||||||
@ -68,9 +71,17 @@ class VariantData {
|
|||||||
T asFloat() const;
|
T asFloat() const;
|
||||||
|
|
||||||
JsonString asString() const;
|
JsonString asString() const;
|
||||||
|
JsonString asRaw() const;
|
||||||
|
|
||||||
bool asBoolean() const;
|
bool asBoolean() const;
|
||||||
|
|
||||||
|
const char* getOwnedString() const {
|
||||||
|
if (_flags & OWNED_VALUE_BIT)
|
||||||
|
return _content.asString.data;
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
CollectionData* asArray() {
|
CollectionData* asArray() {
|
||||||
return isArray() ? &_content.asCollection : 0;
|
return isArray() ? &_content.asCollection : 0;
|
||||||
}
|
}
|
||||||
@ -91,8 +102,6 @@ class VariantData {
|
|||||||
return const_cast<VariantData*>(this)->asObject();
|
return const_cast<VariantData*>(this)->asObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool copyFrom(const VariantData& src, MemoryPool* pool);
|
|
||||||
|
|
||||||
bool isArray() const {
|
bool isArray() const {
|
||||||
return (_flags & VALUE_IS_ARRAY) != 0;
|
return (_flags & VALUE_IS_ARRAY) != 0;
|
||||||
}
|
}
|
||||||
@ -139,17 +148,6 @@ class VariantData {
|
|||||||
return !isFloat();
|
return !isFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(size_t index) {
|
|
||||||
if (isArray())
|
|
||||||
_content.asCollection.removeElement(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
|
||||||
void remove(TAdaptedString key) {
|
|
||||||
if (isObject())
|
|
||||||
_content.asCollection.removeMember(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBoolean(bool value) {
|
void setBoolean(bool value) {
|
||||||
setType(VALUE_IS_BOOLEAN);
|
setType(VALUE_IS_BOOLEAN);
|
||||||
_content.asBoolean = value;
|
_content.asBoolean = value;
|
||||||
@ -160,28 +158,16 @@ class VariantData {
|
|||||||
_content.asFloat = value;
|
_content.asFloat = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLinkedRaw(SerializedValue<const char*> value) {
|
void setLinkedRaw(const char* data, size_t n) {
|
||||||
if (value.data()) {
|
setType(VALUE_IS_LINKED_RAW);
|
||||||
setType(VALUE_IS_LINKED_RAW);
|
_content.asString.data = data;
|
||||||
_content.asString.data = value.data();
|
_content.asString.size = n;
|
||||||
_content.asString.size = value.size();
|
|
||||||
} else {
|
|
||||||
setType(VALUE_IS_NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
void setOwnedRaw(const char* data, size_t n) {
|
||||||
bool storeOwnedRaw(SerializedValue<T> value, MemoryPool* pool) {
|
setType(VALUE_IS_OWNED_RAW);
|
||||||
const char* dup = pool->saveString(adaptString(value.data(), value.size()));
|
_content.asString.data = data;
|
||||||
if (dup) {
|
_content.asString.size = n;
|
||||||
setType(VALUE_IS_OWNED_RAW);
|
|
||||||
_content.asString.data = dup;
|
|
||||||
_content.asString.size = value.size();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
setType(VALUE_IS_NULL);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -239,42 +225,17 @@ class VariantData {
|
|||||||
return isCollection() ? _content.asCollection.size() : 0;
|
return isCollection() ? _content.asCollection.size() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
VariantData* addElement(MemoryPool* pool) {
|
|
||||||
if (isNull())
|
|
||||||
toArray();
|
|
||||||
if (!isArray())
|
|
||||||
return 0;
|
|
||||||
return _content.asCollection.addElement(pool);
|
|
||||||
}
|
|
||||||
|
|
||||||
VariantData* getElement(size_t index) const {
|
VariantData* getElement(size_t index) const {
|
||||||
const CollectionData* col = asArray();
|
const CollectionData* col = asArray();
|
||||||
return col ? col->getElement(index) : 0;
|
return col ? col->getElement(index) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
VariantData* getOrAddElement(size_t index, MemoryPool* pool) {
|
|
||||||
if (isNull())
|
|
||||||
toArray();
|
|
||||||
if (!isArray())
|
|
||||||
return 0;
|
|
||||||
return _content.asCollection.getOrAddElement(index, pool);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
VariantData* getMember(TAdaptedString key) const {
|
VariantData* getMember(TAdaptedString key) const {
|
||||||
const CollectionData* col = asObject();
|
const CollectionData* col = asObject();
|
||||||
return col ? col->getMember(key) : 0;
|
return col ? col->getMember(key) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
|
||||||
VariantData* getOrAddMember(TAdaptedString key, MemoryPool* pool) {
|
|
||||||
if (isNull())
|
|
||||||
toObject();
|
|
||||||
if (!isObject())
|
|
||||||
return 0;
|
|
||||||
return _content.asCollection.getOrAddMember(key, pool);
|
|
||||||
}
|
|
||||||
|
|
||||||
void movePointers(ptrdiff_t variantDistance) {
|
void movePointers(ptrdiff_t variantDistance) {
|
||||||
if (_flags & COLLECTION_MASK)
|
if (_flags & COLLECTION_MASK)
|
||||||
_content.asCollection.movePointers(variantDistance);
|
_content.asCollection.movePointers(variantDistance);
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||||
#include <ArduinoJson/Polyfills/attributes.hpp>
|
#include <ArduinoJson/Polyfills/attributes.hpp>
|
||||||
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
||||||
#include <ArduinoJson/Variant/VariantData.hpp>
|
#include <ArduinoJson/Variant/VariantData.hpp>
|
||||||
@ -11,6 +12,16 @@
|
|||||||
|
|
||||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||||
|
|
||||||
|
void slotRelease(const VariantSlot* slot, MemoryPool* pool);
|
||||||
|
bool collectionCopy(CollectionData* dst, const CollectionData* src,
|
||||||
|
MemoryPool* pool);
|
||||||
|
VariantData* collectionAddElement(CollectionData* array, MemoryPool* pool);
|
||||||
|
void collectionRemoveElement(CollectionData* data, size_t index,
|
||||||
|
MemoryPool* pool);
|
||||||
|
template <typename TAdaptedString>
|
||||||
|
void collectionRemoveMember(CollectionData* data, TAdaptedString key,
|
||||||
|
MemoryPool* pool);
|
||||||
|
|
||||||
template <typename TVisitor>
|
template <typename TVisitor>
|
||||||
inline typename TVisitor::result_type variantAccept(const VariantData* var,
|
inline typename TVisitor::result_type variantAccept(const VariantData* var,
|
||||||
TVisitor& visitor) {
|
TVisitor& visitor) {
|
||||||
@ -20,6 +31,15 @@ inline typename TVisitor::result_type variantAccept(const VariantData* var,
|
|||||||
return visitor.visitNull();
|
return visitor.visitNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void variantRelease(const VariantData* var, MemoryPool* pool) {
|
||||||
|
ARDUINOJSON_ASSERT(var != nullptr);
|
||||||
|
auto c = var->asCollection();
|
||||||
|
if (c) {
|
||||||
|
for (auto slot = c->head(); slot; slot = slot->next())
|
||||||
|
slotRelease(slot, pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline bool variantCopyFrom(VariantData* dst, const VariantData* src,
|
inline bool variantCopyFrom(VariantData* dst, const VariantData* src,
|
||||||
MemoryPool* pool) {
|
MemoryPool* pool) {
|
||||||
if (!dst)
|
if (!dst)
|
||||||
@ -28,20 +48,69 @@ inline bool variantCopyFrom(VariantData* dst, const VariantData* src,
|
|||||||
dst->setNull();
|
dst->setNull();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return dst->copyFrom(*src, pool);
|
switch (src->type()) {
|
||||||
|
case VALUE_IS_ARRAY:
|
||||||
|
return collectionCopy(&dst->toArray(), src->asArray(), pool);
|
||||||
|
case VALUE_IS_OBJECT:
|
||||||
|
return collectionCopy(&dst->toObject(), src->asObject(), pool);
|
||||||
|
case VALUE_IS_OWNED_STRING: {
|
||||||
|
auto str = adaptString(src->asString());
|
||||||
|
auto dup = storeString(pool, str, StringStoragePolicy::Copy());
|
||||||
|
if (!dup)
|
||||||
|
return false;
|
||||||
|
dst->setString(dup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case VALUE_IS_OWNED_RAW: {
|
||||||
|
auto str = adaptString(src->asRaw());
|
||||||
|
auto dup = storeString(pool, str, StringStoragePolicy::Copy());
|
||||||
|
if (!dup)
|
||||||
|
return false;
|
||||||
|
dst->setOwnedRaw(dup.c_str(), str.size());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
*dst = *src;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void variantSetNull(VariantData* var) {
|
inline void variantSetNull(VariantData* var, MemoryPool* pool) {
|
||||||
if (!var)
|
if (!var)
|
||||||
return;
|
return;
|
||||||
|
variantRelease(var, pool);
|
||||||
var->setNull();
|
var->setNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void variantSetBoolean(VariantData* var, bool value, MemoryPool* pool) {
|
||||||
|
if (!var)
|
||||||
|
return;
|
||||||
|
variantRelease(var, pool);
|
||||||
|
var->setBoolean(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void variantSetFloat(VariantData* var, JsonFloat value,
|
||||||
|
MemoryPool* pool) {
|
||||||
|
if (!var)
|
||||||
|
return;
|
||||||
|
variantRelease(var, pool);
|
||||||
|
var->setFloat(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void variantSetInteger(VariantData* var, T value, MemoryPool* pool) {
|
||||||
|
if (!var)
|
||||||
|
return;
|
||||||
|
variantRelease(var, pool);
|
||||||
|
var->setInteger(value);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
inline void variantSetString(VariantData* var, TAdaptedString value,
|
inline void variantSetString(VariantData* var, TAdaptedString value,
|
||||||
MemoryPool* pool) {
|
MemoryPool* pool) {
|
||||||
if (!var)
|
if (!var)
|
||||||
return;
|
return;
|
||||||
|
variantRelease(var, pool);
|
||||||
JsonString str = storeString(pool, value);
|
JsonString str = storeString(pool, value);
|
||||||
if (str)
|
if (str)
|
||||||
var->setString(str);
|
var->setString(str);
|
||||||
@ -49,19 +118,46 @@ inline void variantSetString(VariantData* var, TAdaptedString value,
|
|||||||
var->setNull();
|
var->setNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void variantSetOwnedRaw(VariantData* var, SerializedValue<T> value,
|
||||||
|
MemoryPool* pool) {
|
||||||
|
if (!var)
|
||||||
|
return;
|
||||||
|
variantRelease(var, pool);
|
||||||
|
const char* dup = pool->saveString(adaptString(value.data(), value.size()));
|
||||||
|
if (dup)
|
||||||
|
var->setOwnedRaw(dup, value.size());
|
||||||
|
else
|
||||||
|
var->setNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void variantSetLinkedRaw(VariantData* var,
|
||||||
|
SerializedValue<const char*> value,
|
||||||
|
MemoryPool* pool) {
|
||||||
|
if (!var)
|
||||||
|
return;
|
||||||
|
variantRelease(var, pool);
|
||||||
|
if (value.data())
|
||||||
|
var->setLinkedRaw(value.data(), value.size());
|
||||||
|
else
|
||||||
|
var->setNull();
|
||||||
|
}
|
||||||
|
|
||||||
inline size_t variantSize(const VariantData* var) {
|
inline size_t variantSize(const VariantData* var) {
|
||||||
return var != 0 ? var->size() : 0;
|
return var != 0 ? var->size() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline CollectionData* variantToArray(VariantData* var) {
|
inline CollectionData* variantToArray(VariantData* var, MemoryPool* pool) {
|
||||||
if (!var)
|
if (!var)
|
||||||
return 0;
|
return 0;
|
||||||
|
variantRelease(var, pool);
|
||||||
return &var->toArray();
|
return &var->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline CollectionData* variantToObject(VariantData* var) {
|
inline CollectionData* variantToObject(VariantData* var, MemoryPool* pool) {
|
||||||
if (!var)
|
if (!var)
|
||||||
return 0;
|
return 0;
|
||||||
|
variantRelease(var, pool);
|
||||||
return &var->toObject();
|
return &var->toObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,15 +165,43 @@ inline VariantData* variantGetElement(const VariantData* var, size_t index) {
|
|||||||
return var != 0 ? var->getElement(index) : 0;
|
return var != 0 ? var->getElement(index) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline NO_INLINE VariantData* variantAddElement(VariantData* var,
|
inline VariantData* variantAddElement(VariantData* var, MemoryPool* pool) {
|
||||||
MemoryPool* pool) {
|
if (!var)
|
||||||
return var != 0 ? var->addElement(pool) : 0;
|
return nullptr;
|
||||||
|
auto array = var->isNull() ? &var->toArray() : var->asArray();
|
||||||
|
return collectionAddElement(array, pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline NO_INLINE VariantData* variantGetOrAddElement(VariantData* var,
|
inline NO_INLINE VariantData* variantGetOrAddElement(VariantData* var,
|
||||||
size_t index,
|
size_t index,
|
||||||
MemoryPool* pool) {
|
MemoryPool* pool) {
|
||||||
return var != 0 ? var->getOrAddElement(index, pool) : 0;
|
if (!var)
|
||||||
|
return nullptr;
|
||||||
|
auto array = var->isNull() ? &var->toArray() : var->asArray();
|
||||||
|
if (!array)
|
||||||
|
return nullptr;
|
||||||
|
VariantSlot* slot = array->head();
|
||||||
|
while (slot && index > 0) {
|
||||||
|
slot = slot->next();
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
if (!slot)
|
||||||
|
index++;
|
||||||
|
while (index > 0) {
|
||||||
|
slot = pool->allocVariant();
|
||||||
|
if (!slot)
|
||||||
|
return nullptr;
|
||||||
|
array->addSlot(slot);
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
return slot->data();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void variantRemoveElement(VariantData* var, size_t index,
|
||||||
|
MemoryPool* pool) {
|
||||||
|
if (!var)
|
||||||
|
return;
|
||||||
|
collectionRemoveElement(var->asArray(), index, pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
@ -90,9 +214,23 @@ VariantData* variantGetMember(const VariantData* var, TAdaptedString key) {
|
|||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
VariantData* variantGetOrAddMember(VariantData* var, TAdaptedString key,
|
VariantData* variantGetOrAddMember(VariantData* var, TAdaptedString key,
|
||||||
MemoryPool* pool) {
|
MemoryPool* pool) {
|
||||||
|
if (!var || key.isNull())
|
||||||
|
return nullptr;
|
||||||
|
auto obj = var->isNull() ? &var->toObject() : var->asObject();
|
||||||
|
if (!obj)
|
||||||
|
return nullptr;
|
||||||
|
auto slot = obj->getSlot(key);
|
||||||
|
if (slot)
|
||||||
|
return slot->data();
|
||||||
|
return collectionAddMember(obj, key, pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TAdaptedString>
|
||||||
|
void variantRemoveMember(VariantData* var, TAdaptedString key,
|
||||||
|
MemoryPool* pool) {
|
||||||
if (!var)
|
if (!var)
|
||||||
return 0;
|
return;
|
||||||
return var->getOrAddMember(key, pool);
|
collectionRemoveMember(var->asObject(), key, pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool variantIsNull(const VariantData* var) {
|
inline bool variantIsNull(const VariantData* var) {
|
||||||
|
@ -83,26 +83,16 @@ inline JsonString VariantData::asString() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool VariantData::copyFrom(const VariantData& src, MemoryPool* pool) {
|
inline JsonString VariantData::asRaw() const {
|
||||||
switch (src.type()) {
|
switch (type()) {
|
||||||
case VALUE_IS_ARRAY:
|
case VALUE_IS_LINKED_RAW:
|
||||||
return toArray().copyFrom(src._content.asCollection, pool);
|
return JsonString(_content.asString.data, _content.asString.size,
|
||||||
case VALUE_IS_OBJECT:
|
JsonString::Linked);
|
||||||
return toObject().copyFrom(src._content.asCollection, pool);
|
|
||||||
case VALUE_IS_OWNED_STRING: {
|
|
||||||
auto str = storeString(pool, adaptString(src.asString()),
|
|
||||||
StringStoragePolicy::Copy());
|
|
||||||
setString(str);
|
|
||||||
return !str.isNull();
|
|
||||||
}
|
|
||||||
case VALUE_IS_OWNED_RAW:
|
case VALUE_IS_OWNED_RAW:
|
||||||
return storeOwnedRaw(
|
return JsonString(_content.asString.data, _content.asString.size,
|
||||||
serialized(src._content.asString.data, src._content.asString.size),
|
JsonString::Copied);
|
||||||
pool);
|
|
||||||
default:
|
default:
|
||||||
setType(src.type());
|
return JsonString();
|
||||||
_content = src._content;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,21 +116,21 @@ template <typename TDerived>
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
inline typename enable_if<is_same<T, JsonArray>::value, JsonArray>::type
|
inline typename enable_if<is_same<T, JsonArray>::value, JsonArray>::type
|
||||||
VariantRefBase<TDerived>::to() const {
|
VariantRefBase<TDerived>::to() const {
|
||||||
return JsonArray(getPool(), variantToArray(getOrCreateData()));
|
return JsonArray(getPool(), variantToArray(getOrCreateData(), getPool()));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TDerived>
|
template <typename TDerived>
|
||||||
template <typename T>
|
template <typename T>
|
||||||
typename enable_if<is_same<T, JsonObject>::value, JsonObject>::type
|
typename enable_if<is_same<T, JsonObject>::value, JsonObject>::type
|
||||||
VariantRefBase<TDerived>::to() const {
|
VariantRefBase<TDerived>::to() const {
|
||||||
return JsonObject(getPool(), variantToObject(getOrCreateData()));
|
return JsonObject(getPool(), variantToObject(getOrCreateData(), getPool()));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TDerived>
|
template <typename TDerived>
|
||||||
template <typename T>
|
template <typename T>
|
||||||
typename enable_if<is_same<T, JsonVariant>::value, JsonVariant>::type
|
typename enable_if<is_same<T, JsonVariant>::value, JsonVariant>::type
|
||||||
VariantRefBase<TDerived>::to() const {
|
VariantRefBase<TDerived>::to() const {
|
||||||
variantSetNull(getOrCreateData());
|
variantSetNull(getOrCreateData(), getPool());
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class VariantRefBase : public VariantTag {
|
|||||||
// ⚠️ Doesn't release the memory associated with the previous value.
|
// ⚠️ Doesn't release the memory associated with the previous value.
|
||||||
// https://arduinojson.org/v6/api/jsonvariant/clear/
|
// https://arduinojson.org/v6/api/jsonvariant/clear/
|
||||||
FORCE_INLINE void clear() const {
|
FORCE_INLINE void clear() const {
|
||||||
variantSetNull(getData());
|
variantSetNull(getOrCreateData(), getPool());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the value is null or the reference is unbound.
|
// Returns true if the value is null or the reference is unbound.
|
||||||
@ -112,11 +112,10 @@ class VariantRefBase : public VariantTag {
|
|||||||
VariantData* data = getOrCreateData();
|
VariantData* data = getOrCreateData();
|
||||||
if (!data)
|
if (!data)
|
||||||
return;
|
return;
|
||||||
|
variantSetNull(data, getPool());
|
||||||
const VariantData* targetData = VariantAttorney::getData(target);
|
const VariantData* targetData = VariantAttorney::getData(target);
|
||||||
if (targetData)
|
if (targetData)
|
||||||
*data = *targetData;
|
*data = *targetData;
|
||||||
else
|
|
||||||
data->setNull();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copies the specified value.
|
// Copies the specified value.
|
||||||
@ -179,9 +178,7 @@ class VariantRefBase : public VariantTag {
|
|||||||
// ⚠️ Doesn't release the memory associated with the removed element.
|
// ⚠️ Doesn't release the memory associated with the removed element.
|
||||||
// https://arduinojson.org/v6/api/jsonvariant/remove/
|
// https://arduinojson.org/v6/api/jsonvariant/remove/
|
||||||
FORCE_INLINE void remove(size_t index) const {
|
FORCE_INLINE void remove(size_t index) const {
|
||||||
VariantData* data = getData();
|
variantRemoveElement(getData(), index, getPool());
|
||||||
if (data)
|
|
||||||
data->remove(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes a member of the object.
|
// Removes a member of the object.
|
||||||
@ -190,9 +187,7 @@ class VariantRefBase : public VariantTag {
|
|||||||
template <typename TChar>
|
template <typename TChar>
|
||||||
FORCE_INLINE typename enable_if<IsString<TChar*>::value>::type remove(
|
FORCE_INLINE typename enable_if<IsString<TChar*>::value>::type remove(
|
||||||
TChar* key) const {
|
TChar* key) const {
|
||||||
VariantData* data = getData();
|
variantRemoveMember(getData(), adaptString(key), getPool());
|
||||||
if (data)
|
|
||||||
data->remove(adaptString(key));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes a member of the object.
|
// Removes a member of the object.
|
||||||
@ -201,9 +196,7 @@ class VariantRefBase : public VariantTag {
|
|||||||
template <typename TString>
|
template <typename TString>
|
||||||
FORCE_INLINE typename enable_if<IsString<TString>::value>::type remove(
|
FORCE_INLINE typename enable_if<IsString<TString>::value>::type remove(
|
||||||
const TString& key) const {
|
const TString& key) const {
|
||||||
VariantData* data = getData();
|
variantRemoveMember(getData(), adaptString(key), getPool());
|
||||||
if (data)
|
|
||||||
data->remove(adaptString(key));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates an array and appends it to the array.
|
// Creates an array and appends it to the array.
|
||||||
|
Reference in New Issue
Block a user