mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-07-21 22:42:25 +02:00
Use singly-linked list to reduce memory usage
This commit is contained in:
@ -15,20 +15,17 @@ inline VariantData* arrayAdd(ArrayData* arr, MemoryPool* pool) {
|
|||||||
VariantSlot* slot = pool->allocVariant();
|
VariantSlot* slot = pool->allocVariant();
|
||||||
if (!slot) return 0;
|
if (!slot) return 0;
|
||||||
|
|
||||||
slot->next = 0;
|
slot->init();
|
||||||
slot->value.type = JSON_NULL;
|
|
||||||
slot->value.keyIsOwned = false;
|
|
||||||
|
|
||||||
if (arr->tail) {
|
if (arr->tail) {
|
||||||
slot->attachTo(arr->tail);
|
slot->attachTo(arr->tail);
|
||||||
arr->tail = slot;
|
arr->tail = slot;
|
||||||
} else {
|
} else {
|
||||||
slot->prev = 0;
|
|
||||||
arr->head = slot;
|
arr->head = slot;
|
||||||
arr->tail = slot;
|
arr->tail = slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
return &slot->value;
|
return slot->getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline VariantSlot* arrayGetSlot(const ArrayData* arr, size_t index) {
|
inline VariantSlot* arrayGetSlot(const ArrayData* arr, size_t index) {
|
||||||
@ -38,20 +35,20 @@ inline VariantSlot* arrayGetSlot(const ArrayData* arr, size_t index) {
|
|||||||
|
|
||||||
inline VariantData* arrayGet(const ArrayData* arr, size_t index) {
|
inline VariantData* arrayGet(const ArrayData* arr, size_t index) {
|
||||||
VariantSlot* slot = arrayGetSlot(arr, index);
|
VariantSlot* slot = arrayGetSlot(arr, index);
|
||||||
return slot ? &slot->value : 0;
|
return slot ? slot->getData() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void arrayRemove(ArrayData* arr, VariantSlot* slot) {
|
inline void arrayRemove(ArrayData* arr, VariantSlot* slot) {
|
||||||
if (!arr || !slot) return;
|
if (!arr || !slot) return;
|
||||||
|
|
||||||
if (slot->prev)
|
VariantSlot* prev = slot->getPrev(arr->head);
|
||||||
slot->getPrev()->setNext(slot->getNext());
|
VariantSlot* next = slot->getNext();
|
||||||
|
|
||||||
|
if (prev)
|
||||||
|
prev->setNext(next);
|
||||||
else
|
else
|
||||||
arr->head = slot->getNext();
|
arr->head = next;
|
||||||
if (slot->next)
|
if (!next) arr->tail = prev;
|
||||||
slot->getNext()->setPrev(slot->getPrev());
|
|
||||||
else
|
|
||||||
arr->tail = slot->getPrev();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void arrayRemove(ArrayData* arr, size_t index) {
|
inline void arrayRemove(ArrayData* arr, size_t index) {
|
||||||
@ -70,7 +67,7 @@ inline bool arrayCopy(ArrayData* dst, const ArrayData* src, MemoryPool* pool) {
|
|||||||
if (!dst || !src) return false;
|
if (!dst || !src) return false;
|
||||||
arrayClear(dst);
|
arrayClear(dst);
|
||||||
for (VariantSlot* s = src->head; s; s = s->getNext()) {
|
for (VariantSlot* s = src->head; s; s = s->getNext()) {
|
||||||
if (!variantCopy(arrayAdd(dst, pool), &s->value, pool)) return false;
|
if (!variantCopy(arrayAdd(dst, pool), s->getData(), pool)) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -85,7 +82,7 @@ inline bool arrayEquals(const ArrayData* a1, const ArrayData* a2) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
if (s1 == s2) return true;
|
if (s1 == s2) return true;
|
||||||
if (!s1 || !s2) return false;
|
if (!s1 || !s2) return false;
|
||||||
if (!variantEquals(&s1->value, &s2->value)) return false;
|
if (!variantEquals(s1->getData(), s2->getData())) return false;
|
||||||
s1 = s1->getNext();
|
s1 = s1->getNext();
|
||||||
s2 = s2->getNext();
|
s2 = s2->getNext();
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,10 @@ class ArrayIterator {
|
|||||||
: _memoryPool(memoryPool), _slot(slot) {}
|
: _memoryPool(memoryPool), _slot(slot) {}
|
||||||
|
|
||||||
VariantRef operator*() const {
|
VariantRef operator*() const {
|
||||||
return VariantRef(_memoryPool, &_slot->value);
|
return VariantRef(_memoryPool, _slot->getData());
|
||||||
}
|
}
|
||||||
VariantPtr operator->() {
|
VariantPtr operator->() {
|
||||||
return VariantPtr(_memoryPool, &_slot->value);
|
return VariantPtr(_memoryPool, _slot->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const ArrayIterator &other) const {
|
bool operator==(const ArrayIterator &other) const {
|
||||||
@ -88,10 +88,10 @@ class ArrayConstRefIterator {
|
|||||||
explicit ArrayConstRefIterator(const VariantSlot *slot) : _slot(slot) {}
|
explicit ArrayConstRefIterator(const VariantSlot *slot) : _slot(slot) {}
|
||||||
|
|
||||||
VariantConstRef operator*() const {
|
VariantConstRef operator*() const {
|
||||||
return VariantConstRef(&_slot->value);
|
return VariantConstRef(_slot->getData());
|
||||||
}
|
}
|
||||||
VariantConstPtr operator->() {
|
VariantConstPtr operator->() {
|
||||||
return VariantConstPtr(&_slot->value);
|
return VariantConstPtr(_slot->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const ArrayConstRefIterator &other) const {
|
bool operator==(const ArrayConstRefIterator &other) const {
|
||||||
|
@ -19,7 +19,7 @@ class Key {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isNull() const {
|
bool isNull() const {
|
||||||
return _slot == 0 || _slot->key == 0;
|
return _slot == 0 || _slot->key() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator==(Key lhs, const char* rhs) {
|
friend bool operator==(Key lhs, const char* rhs) {
|
||||||
|
@ -31,20 +31,18 @@ inline VariantData* objectAdd(ObjectData* obj, TKey key, MemoryPool* pool) {
|
|||||||
VariantSlot* slot = pool->allocVariant();
|
VariantSlot* slot = pool->allocVariant();
|
||||||
if (!slot) return 0;
|
if (!slot) return 0;
|
||||||
|
|
||||||
slot->next = 0;
|
slot->init();
|
||||||
slot->value.type = JSON_NULL;
|
|
||||||
|
|
||||||
if (obj->tail) {
|
if (obj->tail) {
|
||||||
slot->attachTo(obj->tail);
|
slot->attachTo(obj->tail);
|
||||||
obj->tail = slot;
|
obj->tail = slot;
|
||||||
} else {
|
} else {
|
||||||
slot->prev = 0;
|
|
||||||
obj->head = slot;
|
obj->head = slot;
|
||||||
obj->tail = slot;
|
obj->tail = slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!slotSetKey(slot, key, pool)) return 0;
|
if (!slotSetKey(slot, key, pool)) return 0;
|
||||||
return &slot->value;
|
return slot->getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey>
|
template <typename TKey>
|
||||||
@ -56,7 +54,7 @@ inline VariantData* objectSet(ObjectData* obj, TKey key, MemoryPool* pool) {
|
|||||||
|
|
||||||
// search a matching key
|
// search a matching key
|
||||||
VariantSlot* slot = objectFindSlot(obj, key);
|
VariantSlot* slot = objectFindSlot(obj, key);
|
||||||
if (slot) return &slot->value;
|
if (slot) return slot->getData();
|
||||||
|
|
||||||
return objectAdd(obj, key, pool);
|
return objectAdd(obj, key, pool);
|
||||||
}
|
}
|
||||||
@ -64,7 +62,7 @@ inline VariantData* objectSet(ObjectData* obj, TKey key, MemoryPool* pool) {
|
|||||||
template <typename TKey>
|
template <typename TKey>
|
||||||
inline VariantData* objectGet(const ObjectData* obj, TKey key) {
|
inline VariantData* objectGet(const ObjectData* obj, TKey key) {
|
||||||
VariantSlot* slot = objectFindSlot(obj, key);
|
VariantSlot* slot = objectFindSlot(obj, key);
|
||||||
return slot ? &slot->value : 0;
|
return slot ? slot->getData() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void objectClear(ObjectData* obj) {
|
inline void objectClear(ObjectData* obj) {
|
||||||
@ -76,16 +74,13 @@ inline void objectClear(ObjectData* obj) {
|
|||||||
inline void objectRemove(ObjectData* obj, VariantSlot* slot) {
|
inline void objectRemove(ObjectData* obj, VariantSlot* slot) {
|
||||||
if (!obj) return;
|
if (!obj) return;
|
||||||
if (!slot) return;
|
if (!slot) return;
|
||||||
VariantSlot* prev = slot->getPrev();
|
VariantSlot* prev = slot->getPrev(obj->head);
|
||||||
VariantSlot* next = slot->getNext();
|
VariantSlot* next = slot->getNext();
|
||||||
if (prev)
|
if (prev)
|
||||||
prev->setNext(next);
|
prev->setNext(next);
|
||||||
else
|
else
|
||||||
obj->head = next;
|
obj->head = next;
|
||||||
if (next)
|
if (!next) obj->tail = prev;
|
||||||
next->setPrev(prev);
|
|
||||||
else
|
|
||||||
obj->tail = prev;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t objectSize(const ObjectData* obj) {
|
inline size_t objectSize(const ObjectData* obj) {
|
||||||
@ -101,11 +96,11 @@ inline bool objectCopy(ObjectData* dst, const ObjectData* src,
|
|||||||
objectClear(dst);
|
objectClear(dst);
|
||||||
for (VariantSlot* s = src->head; s; s = s->getNext()) {
|
for (VariantSlot* s = src->head; s; s = s->getNext()) {
|
||||||
VariantData* var;
|
VariantData* var;
|
||||||
if (s->value.keyIsOwned)
|
if (s->ownsKey())
|
||||||
var = objectAdd(dst, ZeroTerminatedRamString(s->key), pool);
|
var = objectAdd(dst, ZeroTerminatedRamString(s->key()), pool);
|
||||||
else
|
else
|
||||||
var = objectAdd(dst, ZeroTerminatedRamStringConst(s->key), pool);
|
var = objectAdd(dst, ZeroTerminatedRamStringConst(s->key()), pool);
|
||||||
if (!variantCopy(var, &s->value, pool)) return false;
|
if (!variantCopy(var, s->getData(), pool)) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -115,7 +110,7 @@ inline bool objectEquals(const ObjectData* o1, const ObjectData* o2) {
|
|||||||
if (!o1 || !o2) return false;
|
if (!o1 || !o2) return false;
|
||||||
|
|
||||||
for (VariantSlot* s = o1->head; s; s = s->getNext()) {
|
for (VariantSlot* s = o1->head; s; s = s->getNext()) {
|
||||||
VariantData* v1 = &s->value;
|
VariantData* v1 = s->getData();
|
||||||
VariantData* v2 = objectGet(o2, makeString(slotGetKey(s)));
|
VariantData* v2 = objectGet(o2, makeString(slotGetKey(s)));
|
||||||
if (!variantEquals(v1, v2)) return false;
|
if (!variantEquals(v1, v2)) return false;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ class Pair {
|
|||||||
public:
|
public:
|
||||||
Pair(MemoryPool* memoryPool, VariantSlot* slot) : _key(slot) {
|
Pair(MemoryPool* memoryPool, VariantSlot* slot) : _key(slot) {
|
||||||
if (slot) {
|
if (slot) {
|
||||||
_value = VariantRef(memoryPool, &slot->value);
|
_value = VariantRef(memoryPool, slot->getData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ class PairConst {
|
|||||||
public:
|
public:
|
||||||
PairConst(const VariantSlot* slot) : _key(slot) {
|
PairConst(const VariantSlot* slot) : _key(slot) {
|
||||||
if (slot) {
|
if (slot) {
|
||||||
_value = VariantConstRef(&slot->value);
|
_value = VariantConstRef(slot->getData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,26 +15,23 @@ template <typename TKey>
|
|||||||
inline bool slotSetKey(VariantSlot* var, TKey key, MemoryPool* pool) {
|
inline bool slotSetKey(VariantSlot* var, TKey key, MemoryPool* pool) {
|
||||||
char* dup = key.save(pool);
|
char* dup = key.save(pool);
|
||||||
if (!dup) return false;
|
if (!dup) return false;
|
||||||
var->key = dup;
|
var->setKey(dup, true);
|
||||||
var->value.keyIsOwned = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool slotSetKey(VariantSlot* var, ZeroTerminatedRamStringConst key,
|
inline bool slotSetKey(VariantSlot* var, ZeroTerminatedRamStringConst key,
|
||||||
MemoryPool*) {
|
MemoryPool*) {
|
||||||
var->key = key.c_str();
|
var->setKey(key.c_str(), false);
|
||||||
var->value.keyIsOwned = false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool slotSetKey(VariantSlot* var, StringInMemoryPool key, MemoryPool*) {
|
inline bool slotSetKey(VariantSlot* var, StringInMemoryPool key, MemoryPool*) {
|
||||||
var->key = key.c_str();
|
var->setKey(key.c_str(), true);
|
||||||
var->value.keyIsOwned = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const char* slotGetKey(const VariantSlot* var) {
|
inline const char* slotGetKey(const VariantSlot* var) {
|
||||||
return var->key;
|
return var->key();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t slotSize(const VariantSlot* var) {
|
inline size_t slotSize(const VariantSlot* var) {
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
namespace ARDUINOJSON_NAMESPACE {
|
namespace ARDUINOJSON_NAMESPACE {
|
||||||
|
|
||||||
|
class VariantSlot;
|
||||||
|
|
||||||
enum VariantType {
|
enum VariantType {
|
||||||
JSON_NULL,
|
JSON_NULL,
|
||||||
JSON_LINKED_RAW,
|
JSON_LINKED_RAW,
|
||||||
@ -26,13 +28,13 @@ enum VariantType {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct ObjectData {
|
struct ObjectData {
|
||||||
struct VariantSlot *head;
|
VariantSlot *head;
|
||||||
struct VariantSlot *tail;
|
VariantSlot *tail;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ArrayData {
|
struct ArrayData {
|
||||||
struct VariantSlot *head;
|
VariantSlot *head;
|
||||||
struct VariantSlot *tail;
|
VariantSlot *tail;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RawData {
|
struct RawData {
|
||||||
@ -56,9 +58,9 @@ union VariantContent {
|
|||||||
|
|
||||||
// this struct must be a POD type to prevent error calling offsetof on clang
|
// this struct must be a POD type to prevent error calling offsetof on clang
|
||||||
struct VariantData {
|
struct VariantData {
|
||||||
|
VariantContent content;
|
||||||
bool keyIsOwned : 1;
|
bool keyIsOwned : 1;
|
||||||
VariantType type : 7;
|
VariantType type : 7;
|
||||||
VariantContent content;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline VariantData *getVariantData(ArrayData *arr) {
|
inline VariantData *getVariantData(ArrayData *arr) {
|
||||||
|
@ -11,16 +11,33 @@ namespace ARDUINOJSON_NAMESPACE {
|
|||||||
|
|
||||||
typedef conditional<sizeof(void*) <= 2, int8_t, int16_t>::type VariantSlotDiff;
|
typedef conditional<sizeof(void*) <= 2, int8_t, int16_t>::type VariantSlotDiff;
|
||||||
|
|
||||||
struct VariantSlot {
|
class VariantSlot {
|
||||||
VariantData value;
|
// CAUTION: same layout as VariantData
|
||||||
VariantSlotDiff next;
|
// we cannot use composition because it adds padding
|
||||||
VariantSlotDiff prev;
|
// (+20% on ESP8266 for example)
|
||||||
const char* key;
|
VariantContent _content;
|
||||||
|
bool _keyIsOwned : 1;
|
||||||
|
VariantType _type : 7;
|
||||||
|
VariantSlotDiff _next;
|
||||||
|
const char* _key;
|
||||||
|
|
||||||
// Must be a POD! so no constructor, nor destructor, nor virtual
|
public:
|
||||||
|
// Must be a POD!
|
||||||
|
// - no constructor
|
||||||
|
// - no destructor
|
||||||
|
// - no virtual
|
||||||
|
// - no inheritance
|
||||||
|
|
||||||
|
VariantData* getData() {
|
||||||
|
return reinterpret_cast<VariantData*>(&_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
const VariantData* getData() const {
|
||||||
|
return reinterpret_cast<const VariantData*>(&_content);
|
||||||
|
}
|
||||||
|
|
||||||
VariantSlot* getNext() {
|
VariantSlot* getNext() {
|
||||||
return next ? this + next : 0;
|
return _next ? this + _next : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VariantSlot* getNext() const {
|
const VariantSlot* getNext() const {
|
||||||
@ -30,32 +47,50 @@ struct VariantSlot {
|
|||||||
VariantSlot* getNext(size_t distance) {
|
VariantSlot* getNext(size_t distance) {
|
||||||
VariantSlot* slot = this;
|
VariantSlot* slot = this;
|
||||||
while (distance--) {
|
while (distance--) {
|
||||||
if (!slot->next) return 0;
|
if (!slot->_next) return 0;
|
||||||
slot += slot->next;
|
slot += slot->_next;
|
||||||
}
|
}
|
||||||
return slot;
|
return slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VariantSlot* getPrev(VariantSlot* head) {
|
||||||
|
while (head) {
|
||||||
|
VariantSlot* nxt = head->getNext();
|
||||||
|
if (nxt == this) return head;
|
||||||
|
head = nxt;
|
||||||
|
}
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
const VariantSlot* getNext(size_t distance) const {
|
const VariantSlot* getNext(size_t distance) const {
|
||||||
return const_cast<VariantSlot*>(this)->getNext(distance);
|
return const_cast<VariantSlot*>(this)->getNext(distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
VariantSlot* getPrev() {
|
|
||||||
return prev ? this + prev : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setNext(VariantSlot* slot) {
|
void setNext(VariantSlot* slot) {
|
||||||
this->next = VariantSlotDiff(slot ? slot - this : 0);
|
_next = VariantSlotDiff(slot ? slot - this : 0);
|
||||||
}
|
|
||||||
|
|
||||||
void setPrev(VariantSlot* slot) {
|
|
||||||
this->prev = VariantSlotDiff(slot ? slot - this : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void attachTo(VariantSlot* tail) {
|
void attachTo(VariantSlot* tail) {
|
||||||
VariantSlotDiff offset = VariantSlotDiff(tail - this);
|
tail->_next = VariantSlotDiff(this - tail);
|
||||||
this->prev = offset;
|
}
|
||||||
tail->next = VariantSlotDiff(-offset);
|
|
||||||
|
void setKey(const char* k, bool owned) {
|
||||||
|
_keyIsOwned = owned;
|
||||||
|
_key = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* key() const {
|
||||||
|
return _key;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ownsKey() const {
|
||||||
|
return _keyIsOwned;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
_next = 0;
|
||||||
|
_type = JSON_NULL;
|
||||||
|
_keyIsOwned = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user