Use singly-linked list to reduce memory usage

This commit is contained in:
Benoit Blanchon
2018-12-01 12:05:51 +01:00
parent aaf0d5c3c5
commit 1d942cdf41
8 changed files with 97 additions and 71 deletions

View File

@ -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();
} }

View File

@ -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 {

View File

@ -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) {

View File

@ -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;
} }

View File

@ -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());
} }
} }

View File

@ -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) {

View File

@ -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) {

View File

@ -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;
} }
}; };