Store offset between slots to reduce memory usage

This commit is contained in:
Benoit Blanchon
2018-11-30 14:26:15 +01:00
parent 8ff48dde73
commit 04e8acd844
12 changed files with 118 additions and 49 deletions

View File

@ -6,6 +6,7 @@ HEAD
* Removed the automatic expansion of `DynamicJsonDocument`, it now has a fixed capacity. * Removed the automatic expansion of `DynamicJsonDocument`, it now has a fixed capacity.
* Restored the monotonic allocator because the code was getting too big * Restored the monotonic allocator because the code was getting too big
* Reduced the memory usage
v6.6.0-beta (2018-11-13) v6.6.0-beta (2018-11-13)
----------- -----------

View File

@ -2,6 +2,10 @@
# Copyright Benoit Blanchon 2014-2018 # Copyright Benoit Blanchon 2014-2018
# MIT License # MIT License
if(MSVC)
add_compile_options(-D_CRT_SECURE_NO_WARNINGS)
endif()
add_executable(msgpack_fuzzer add_executable(msgpack_fuzzer
msgpack_fuzzer.cpp msgpack_fuzzer.cpp
fuzzer_main.cpp fuzzer_main.cpp

View File

@ -17,10 +17,10 @@ inline JsonVariantData* arrayAdd(JsonArrayData* arr, MemoryPool* pool) {
slot->next = 0; slot->next = 0;
slot->value.type = JSON_NULL; slot->value.type = JSON_NULL;
slot->value.keyIsOwned = false;
if (arr->tail) { if (arr->tail) {
slot->prev = arr->tail; slot->attachTo(arr->tail);
arr->tail->next = slot;
arr->tail = slot; arr->tail = slot;
} else { } else {
slot->prev = 0; slot->prev = 0;
@ -28,13 +28,12 @@ inline JsonVariantData* arrayAdd(JsonArrayData* arr, MemoryPool* pool) {
arr->tail = slot; arr->tail = slot;
} }
slot->value.keyIsOwned = false;
return &slot->value; return &slot->value;
} }
inline VariantSlot* arrayGetSlot(const JsonArrayData* arr, size_t index) { inline VariantSlot* arrayGetSlot(const JsonArrayData* arr, size_t index) {
if (!arr) return 0; if (!arr) return 0;
return slotAdvance(arr->head, index); return arr->head->getNext(index);
} }
inline JsonVariantData* arrayGet(const JsonArrayData* arr, size_t index) { inline JsonVariantData* arrayGet(const JsonArrayData* arr, size_t index) {
@ -46,13 +45,13 @@ inline void arrayRemove(JsonArrayData* arr, VariantSlot* slot) {
if (!arr || !slot) return; if (!arr || !slot) return;
if (slot->prev) if (slot->prev)
slot->prev->next = slot->next; slot->getPrev()->setNext(slot->getNext());
else else
arr->head = slot->next; arr->head = slot->getNext();
if (slot->next) if (slot->next)
slot->next->prev = slot->prev; slot->getNext()->setPrev(slot->getPrev());
else else
arr->tail = slot->prev; arr->tail = slot->getPrev();
} }
inline void arrayRemove(JsonArrayData* arr, size_t index) { inline void arrayRemove(JsonArrayData* arr, size_t index) {
@ -71,7 +70,7 @@ inline bool arrayCopy(JsonArrayData* dst, const JsonArrayData* src,
MemoryPool* pool) { MemoryPool* pool) {
if (!dst || !src) return false; if (!dst || !src) return false;
arrayClear(dst); arrayClear(dst);
for (VariantSlot* s = src->head; s; s = s->next) { 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->value, pool)) return false;
} }
return true; return true;
@ -88,8 +87,8 @@ inline bool arrayEquals(const JsonArrayData* a1, const JsonArrayData* a2) {
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->value, &s2->value)) return false;
s1 = s1->next; s1 = s1->getNext();
s2 = s2->next; s2 = s2->getNext();
} }
} }

View File

@ -16,7 +16,7 @@ inline VariantSlot* objectFindSlot(const JsonObjectData* obj, TKey key) {
VariantSlot* slot = obj->head; VariantSlot* slot = obj->head;
while (slot) { while (slot) {
if (key.equals(slotGetKey(slot))) break; if (key.equals(slotGetKey(slot))) break;
slot = slot->next; slot = slot->getNext();
} }
return slot; return slot;
} }
@ -36,8 +36,7 @@ inline JsonVariantData* objectAdd(JsonObjectData* obj, TKey key,
slot->value.type = JSON_NULL; slot->value.type = JSON_NULL;
if (obj->tail) { if (obj->tail) {
slot->prev = obj->tail; slot->attachTo(obj->tail);
obj->tail->next = slot;
obj->tail = slot; obj->tail = slot;
} else { } else {
slot->prev = 0; slot->prev = 0;
@ -79,14 +78,16 @@ inline void objectClear(JsonObjectData* obj) {
inline void objectRemove(JsonObjectData* obj, VariantSlot* slot) { inline void objectRemove(JsonObjectData* obj, VariantSlot* slot) {
if (!obj) return; if (!obj) return;
if (!slot) return; if (!slot) return;
if (slot->prev) VariantSlot* prev = slot->getPrev();
slot->prev->next = slot->next; VariantSlot* next = slot->getNext();
if (prev)
prev->setNext(next);
else else
obj->head = slot->next; obj->head = next;
if (slot->next) if (next)
slot->next->prev = slot->prev; next->setPrev(prev);
else else
obj->tail = slot->prev; obj->tail = prev;
} }
inline size_t objectSize(const JsonObjectData* obj) { inline size_t objectSize(const JsonObjectData* obj) {
@ -100,7 +101,7 @@ inline bool objectCopy(JsonObjectData* dst, const JsonObjectData* src,
MemoryPool* pool) { MemoryPool* pool) {
if (!dst || !src) return false; if (!dst || !src) return false;
objectClear(dst); objectClear(dst);
for (VariantSlot* s = src->head; s; s = s->next) { for (VariantSlot* s = src->head; s; s = s->getNext()) {
JsonVariantData* var; JsonVariantData* var;
if (s->value.keyIsOwned) if (s->value.keyIsOwned)
var = objectAdd(dst, ZeroTerminatedRamString(s->key), pool); var = objectAdd(dst, ZeroTerminatedRamString(s->key), pool);
@ -115,7 +116,7 @@ inline bool objectEquals(const JsonObjectData* o1, const JsonObjectData* o2) {
if (o1 == o2) return true; if (o1 == o2) return true;
if (!o1 || !o2) return false; if (!o1 || !o2) return false;
for (VariantSlot* s = o1->head; s; s = s->next) { for (VariantSlot* s = o1->head; s; s = s->getNext()) {
JsonVariantData* v1 = &s->value; JsonVariantData* v1 = &s->value;
JsonVariantData* v2 = objectGet(o2, makeString(slotGetKey(s))); JsonVariantData* v2 = objectGet(o2, makeString(slotGetKey(s)));
if (!variantEquals(v1, v2)) return false; if (!variantEquals(v1, v2)) return false;

View File

@ -37,27 +37,11 @@ inline const char* slotGetKey(const VariantSlot* var) {
return var->key; return var->key;
} }
inline const VariantSlot* slotAdvance(const VariantSlot* var, size_t distance) {
while (distance && var) {
var = var->next;
distance--;
}
return var;
}
inline VariantSlot* slotAdvance(VariantSlot* var, size_t distance) {
while (distance && var) {
var = var->next;
distance--;
}
return var;
}
inline size_t slotSize(const VariantSlot* var) { inline size_t slotSize(const VariantSlot* var) {
size_t n = 0; size_t n = 0;
while (var) { while (var) {
n++; n++;
var = var->next; var = var->getNext();
} }
return n; return n;
} }

View File

@ -48,12 +48,12 @@ class JsonArrayIterator {
} }
JsonArrayIterator &operator++() { JsonArrayIterator &operator++() {
_slot = _slot->next; _slot = _slot->getNext();
return *this; return *this;
} }
JsonArrayIterator &operator+=(size_t distance) { JsonArrayIterator &operator+=(size_t distance) {
_slot = slotAdvance(_slot, distance); _slot = _slot->getNext(distance);
return *this; return *this;
} }
@ -103,12 +103,12 @@ class JsonArrayConstIterator {
} }
JsonArrayConstIterator &operator++() { JsonArrayConstIterator &operator++() {
_slot = _slot->next; _slot = _slot->getNext();
return *this; return *this;
} }
JsonArrayConstIterator &operator+=(size_t distance) { JsonArrayConstIterator &operator+=(size_t distance) {
_slot = slotAdvance(_slot, distance); _slot = _slot->getNext(distance);
return *this; return *this;
} }

View File

@ -49,12 +49,12 @@ class JsonObjectIterator {
} }
JsonObjectIterator &operator++() { JsonObjectIterator &operator++() {
if (_slot) _slot = _slot->next; _slot = _slot->getNext();
return *this; return *this;
} }
JsonObjectIterator &operator+=(size_t distance) { JsonObjectIterator &operator+=(size_t distance) {
_slot = slotAdvance(_slot, distance); _slot = _slot->getNext(distance);
return *this; return *this;
} }
@ -105,12 +105,12 @@ class JsonObjectConstIterator {
} }
JsonObjectConstIterator &operator++() { JsonObjectConstIterator &operator++() {
if (_slot) _slot = _slot->next; _slot = _slot->getNext();
return *this; return *this;
} }
JsonObjectConstIterator &operator+=(size_t distance) { JsonObjectConstIterator &operator+=(size_t distance) {
_slot = slotAdvance(_slot, distance); _slot = _slot->getNext(distance);
return *this; return *this;
} }

View File

@ -5,14 +5,58 @@
#pragma once #pragma once
#include "../Data/JsonVariantData.hpp" #include "../Data/JsonVariantData.hpp"
#include "../Polyfills/type_traits.hpp"
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
typedef conditional<sizeof(void*) <= 2, int8_t, int16_t>::type VariantSlotDiff;
struct VariantSlot { struct VariantSlot {
JsonVariantData value; JsonVariantData value;
struct VariantSlot* next; VariantSlotDiff next;
struct VariantSlot* prev; VariantSlotDiff prev;
const char* key; const char* key;
// Must be a POD! so no constructor, nor destructor, nor virtual
VariantSlot* getNext() {
return next ? this + next : 0;
}
const VariantSlot* getNext() const {
return const_cast<VariantSlot*>(this)->getNext();
}
VariantSlot* getNext(size_t distance) {
VariantSlot* slot = this;
while (distance--) {
if (!slot->next) return 0;
slot += slot->next;
}
return slot;
}
const VariantSlot* getNext(size_t distance) const {
return const_cast<VariantSlot*>(this)->getNext(distance);
}
VariantSlot* getPrev() {
return prev ? this + prev : 0;
}
void setNext(VariantSlot* slot) {
this->next = VariantSlotDiff(slot ? slot - this : 0);
}
void setPrev(VariantSlot* slot) {
this->prev = VariantSlotDiff(slot ? slot - this : 0);
}
void attachTo(VariantSlot* tail) {
VariantSlotDiff offset = VariantSlotDiff(tail - this);
this->prev = offset;
tail->next = VariantSlotDiff(-offset);
}
}; };
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "type_traits/conditional.hpp"
#include "type_traits/enable_if.hpp" #include "type_traits/enable_if.hpp"
#include "type_traits/integral_constant.hpp" #include "type_traits/integral_constant.hpp"
#include "type_traits/is_array.hpp" #include "type_traits/is_array.hpp"

View File

@ -0,0 +1,18 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
namespace ARDUINOJSON_NAMESPACE {
template <bool Condition, class TrueType, class FalseType>
struct conditional {
typedef TrueType type;
};
template <class TrueType, class FalseType>
struct conditional<false, TrueType, FalseType> {
typedef FalseType type;
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -8,6 +8,7 @@ add_executable(JsonArrayTests
copyTo.cpp copyTo.cpp
createNested.cpp createNested.cpp
equals.cpp equals.cpp
get.cpp
isNull.cpp isNull.cpp
iterator.cpp iterator.cpp
remove.cpp remove.cpp

16
test/JsonArray/get.cpp Normal file
View File

@ -0,0 +1,16 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonArray::get()") {
DynamicJsonDocument doc;
deserializeJson(doc, "[1,2,3]");
JsonArray array = doc.as<JsonArray>();
SECTION("Overflow") {
REQUIRE(array.get(3).isNull());
}
}