forked from bblanchon/ArduinoJson
Store object members with two slots: one for the key and one for the value
This commit is contained in:
@ -1,6 +1,11 @@
|
|||||||
ArduinoJson: change log
|
ArduinoJson: change log
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
|
HEAD
|
||||||
|
----
|
||||||
|
|
||||||
|
* Store object members with two slots: one for the key and one for the value
|
||||||
|
|
||||||
v7.1.0 (2024-06-27)
|
v7.1.0 (2024-06-27)
|
||||||
------
|
------
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
|
|||||||
|
|
||||||
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
|
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
|
||||||
|
|
||||||
static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 8,
|
static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 6,
|
||||||
"sizeof(VariantSlot)");
|
"sizeof(VariantSlot)");
|
||||||
|
|
||||||
void setup() {}
|
void setup() {}
|
||||||
|
@ -8,7 +8,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
|
|||||||
|
|
||||||
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
|
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
|
||||||
|
|
||||||
static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 16,
|
static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 12,
|
||||||
"sizeof(VariantSlot)");
|
"sizeof(VariantSlot)");
|
||||||
|
|
||||||
int main() {}
|
int main() {}
|
||||||
|
@ -8,7 +8,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
|
|||||||
|
|
||||||
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
|
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
|
||||||
|
|
||||||
static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 24,
|
static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 16,
|
||||||
"sizeof(VariantSlot)");
|
"sizeof(VariantSlot)");
|
||||||
|
|
||||||
int main() {}
|
int main() {}
|
||||||
|
@ -8,7 +8,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
|
|||||||
|
|
||||||
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
|
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
|
||||||
|
|
||||||
static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 24,
|
static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 16,
|
||||||
"sizeof(VariantSlot)");
|
"sizeof(VariantSlot)");
|
||||||
|
|
||||||
int main() {}
|
int main() {}
|
||||||
|
@ -68,6 +68,12 @@ TEST_CASE("JsonArray::remove()") {
|
|||||||
REQUIRE(array[1] == 2);
|
REQUIRE(array[1] == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("remove end()") {
|
||||||
|
array.remove(array.end());
|
||||||
|
|
||||||
|
REQUIRE(3 == array.size());
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("In a loop") {
|
SECTION("In a loop") {
|
||||||
for (JsonArray::iterator it = array.begin(); it != array.end(); ++it) {
|
for (JsonArray::iterator it = array.begin(); it != array.end(); ++it) {
|
||||||
if (*it == 2)
|
if (*it == 2)
|
||||||
|
@ -308,12 +308,12 @@ TEST_CASE("deserialize JSON object") {
|
|||||||
REQUIRE(doc["a"] == 2);
|
REQUIRE(doc["a"] == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("NUL in keys") { // we don't support NULs in keys
|
SECTION("NUL in keys") {
|
||||||
DeserializationError err =
|
DeserializationError err =
|
||||||
deserializeJson(doc, "{\"x\\u0000a\":1,\"x\\u0000b\":2}");
|
deserializeJson(doc, "{\"x\\u0000a\":1,\"x\\u0000b\":2}");
|
||||||
|
|
||||||
REQUIRE(err == DeserializationError::Ok);
|
REQUIRE(err == DeserializationError::Ok);
|
||||||
REQUIRE(doc.as<std::string>() == "{\"x\":2}");
|
REQUIRE(doc.as<std::string>() == "{\"x\\u0000a\":1,\"x\\u0000b\":2}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,12 +345,12 @@ TEST_CASE("Deduplicate keys") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("MemberProxy under memory constraints") {
|
TEST_CASE("MemberProxy under memory constraints") {
|
||||||
KillswitchAllocator killswitch;
|
TimebombAllocator timebomb(1);
|
||||||
SpyingAllocator spy(&killswitch);
|
SpyingAllocator spy(&timebomb);
|
||||||
JsonDocument doc(&spy);
|
JsonDocument doc(&spy);
|
||||||
|
|
||||||
SECTION("key allocation fails") {
|
SECTION("key slot allocation fails") {
|
||||||
killswitch.on();
|
timebomb.setCountdown(0);
|
||||||
|
|
||||||
doc["hello"_s] = "world";
|
doc["hello"_s] = "world";
|
||||||
|
|
||||||
@ -361,4 +361,36 @@ TEST_CASE("MemberProxy under memory constraints") {
|
|||||||
AllocateFail(sizeofPool()),
|
AllocateFail(sizeofPool()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("value slot allocation fails") {
|
||||||
|
timebomb.setCountdown(1);
|
||||||
|
|
||||||
|
// fill the pool entirely, but leave one slot for the key
|
||||||
|
doc["foo"][ARDUINOJSON_POOL_CAPACITY - 4] = 1;
|
||||||
|
REQUIRE(doc.overflowed() == false);
|
||||||
|
|
||||||
|
doc["hello"_s] = "world";
|
||||||
|
|
||||||
|
REQUIRE(doc.is<JsonObject>());
|
||||||
|
REQUIRE(doc.size() == 1);
|
||||||
|
REQUIRE(doc.overflowed() == true);
|
||||||
|
REQUIRE(spy.log() == AllocatorLog{
|
||||||
|
Allocate(sizeofPool()),
|
||||||
|
AllocateFail(sizeofPool()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("key string allocation fails") {
|
||||||
|
timebomb.setCountdown(1);
|
||||||
|
|
||||||
|
doc["hello"_s] = "world";
|
||||||
|
|
||||||
|
REQUIRE(doc.is<JsonObject>());
|
||||||
|
REQUIRE(doc.size() == 0);
|
||||||
|
REQUIRE(doc.overflowed() == true);
|
||||||
|
REQUIRE(spy.log() == AllocatorLog{
|
||||||
|
Allocate(sizeofPool()),
|
||||||
|
AllocateFail(sizeofString("hello")),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
|||||||
AllocatorLog{
|
AllocatorLog{
|
||||||
Allocate(sizeofPool()),
|
Allocate(sizeofPool()),
|
||||||
Allocate(sizeofString("abcdefg")),
|
Allocate(sizeofString("abcdefg")),
|
||||||
Reallocate(sizeofPool(), sizeofPool(1)),
|
Reallocate(sizeofPool(), sizeofPool(2)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,7 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
|||||||
|
|
||||||
class ArrayData : public CollectionData {
|
class ArrayData : public CollectionData {
|
||||||
public:
|
public:
|
||||||
VariantData* addElement(ResourceManager* resources) {
|
VariantData* addElement(ResourceManager* resources);
|
||||||
return addSlot(resources).data();
|
|
||||||
}
|
|
||||||
|
|
||||||
static VariantData* addElement(ArrayData* array, ResourceManager* resources) {
|
static VariantData* addElement(ArrayData* array, ResourceManager* resources) {
|
||||||
if (!array)
|
if (!array)
|
||||||
@ -51,6 +49,16 @@ class ArrayData : public CollectionData {
|
|||||||
array->removeElement(index, resources);
|
array->removeElement(index, resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void remove(iterator it, ResourceManager* resources) {
|
||||||
|
CollectionData::removeOne(it, resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove(ArrayData* array, iterator it,
|
||||||
|
ResourceManager* resources) {
|
||||||
|
if (array)
|
||||||
|
return array->remove(it, resources);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
iterator at(size_t index, const ResourceManager* resources) const;
|
iterator at(size_t index, const ResourceManager* resources) const;
|
||||||
};
|
};
|
||||||
|
@ -19,6 +19,14 @@ inline ArrayData::iterator ArrayData::at(
|
|||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline VariantData* ArrayData::addElement(ResourceManager* resources) {
|
||||||
|
auto slot = resources->allocSlot();
|
||||||
|
if (!slot)
|
||||||
|
return nullptr;
|
||||||
|
CollectionData::appendOne(slot, resources);
|
||||||
|
return slot->data();
|
||||||
|
}
|
||||||
|
|
||||||
inline VariantData* ArrayData::getOrAddElement(size_t index,
|
inline VariantData* ArrayData::getOrAddElement(size_t index,
|
||||||
ResourceManager* resources) {
|
ResourceManager* resources) {
|
||||||
auto it = createIterator(resources);
|
auto it = createIterator(resources);
|
||||||
@ -58,7 +66,7 @@ inline bool ArrayData::addValue(T&& value, ResourceManager* resources) {
|
|||||||
resources->freeSlot(slot);
|
resources->freeSlot(slot);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
addSlot(slot, resources);
|
CollectionData::appendOne(slot, resources);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,12 +49,6 @@ class CollectionIterator {
|
|||||||
return *data();
|
return *data();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* key() const;
|
|
||||||
bool ownsKey() const;
|
|
||||||
|
|
||||||
void setKey(StringNode*);
|
|
||||||
void setKey(const char*);
|
|
||||||
|
|
||||||
VariantData* data() {
|
VariantData* data() {
|
||||||
return reinterpret_cast<VariantData*>(slot_);
|
return reinterpret_cast<VariantData*>(slot_);
|
||||||
}
|
}
|
||||||
@ -99,22 +93,17 @@ class CollectionData {
|
|||||||
collection->clear(resources);
|
collection->clear(resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(iterator it, ResourceManager* resources);
|
|
||||||
|
|
||||||
static void remove(CollectionData* collection, iterator it,
|
|
||||||
ResourceManager* resources) {
|
|
||||||
if (collection)
|
|
||||||
return collection->remove(it, resources);
|
|
||||||
}
|
|
||||||
|
|
||||||
SlotId head() const {
|
SlotId head() const {
|
||||||
return head_;
|
return head_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addSlot(SlotWithId slot, ResourceManager* resources);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
iterator addSlot(ResourceManager*);
|
void appendOne(SlotWithId slot, const ResourceManager* resources);
|
||||||
|
void appendPair(SlotWithId key, SlotWithId value,
|
||||||
|
const ResourceManager* resources);
|
||||||
|
|
||||||
|
void removeOne(iterator it, ResourceManager* resources);
|
||||||
|
void removePair(iterator it, ResourceManager* resources);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SlotWithId getPreviousSlot(VariantSlot*, const ResourceManager*) const;
|
SlotWithId getPreviousSlot(VariantSlot*, const ResourceManager*) const;
|
||||||
|
@ -17,28 +17,6 @@ inline CollectionIterator::CollectionIterator(VariantSlot* slot, SlotId slotId)
|
|||||||
nextId_ = slot_ ? slot_->next() : NULL_SLOT;
|
nextId_ = slot_ ? slot_->next() : NULL_SLOT;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const char* CollectionIterator::key() const {
|
|
||||||
ARDUINOJSON_ASSERT(slot_ != nullptr);
|
|
||||||
return slot_->key();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void CollectionIterator::setKey(const char* s) {
|
|
||||||
ARDUINOJSON_ASSERT(slot_ != nullptr);
|
|
||||||
ARDUINOJSON_ASSERT(s != nullptr);
|
|
||||||
return slot_->setKey(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void CollectionIterator::setKey(StringNode* s) {
|
|
||||||
ARDUINOJSON_ASSERT(slot_ != nullptr);
|
|
||||||
ARDUINOJSON_ASSERT(s != nullptr);
|
|
||||||
return slot_->setKey(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool CollectionIterator::ownsKey() const {
|
|
||||||
ARDUINOJSON_ASSERT(slot_ != nullptr);
|
|
||||||
return slot_->ownsKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void CollectionIterator::next(const ResourceManager* resources) {
|
inline void CollectionIterator::next(const ResourceManager* resources) {
|
||||||
ARDUINOJSON_ASSERT(currentId_ != NULL_SLOT);
|
ARDUINOJSON_ASSERT(currentId_ != NULL_SLOT);
|
||||||
slot_ = resources->getSlot(nextId_);
|
slot_ = resources->getSlot(nextId_);
|
||||||
@ -47,11 +25,8 @@ inline void CollectionIterator::next(const ResourceManager* resources) {
|
|||||||
nextId_ = slot_->next();
|
nextId_ = slot_->next();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline CollectionData::iterator CollectionData::addSlot(
|
inline void CollectionData::appendOne(SlotWithId slot,
|
||||||
ResourceManager* resources) {
|
const ResourceManager* resources) {
|
||||||
auto slot = resources->allocSlot();
|
|
||||||
if (!slot)
|
|
||||||
return {};
|
|
||||||
if (tail_ != NULL_SLOT) {
|
if (tail_ != NULL_SLOT) {
|
||||||
auto tail = resources->getSlot(tail_);
|
auto tail = resources->getSlot(tail_);
|
||||||
tail->setNext(slot.id());
|
tail->setNext(slot.id());
|
||||||
@ -60,18 +35,19 @@ inline CollectionData::iterator CollectionData::addSlot(
|
|||||||
head_ = slot.id();
|
head_ = slot.id();
|
||||||
tail_ = slot.id();
|
tail_ = slot.id();
|
||||||
}
|
}
|
||||||
return iterator(slot, slot.id());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void CollectionData::addSlot(SlotWithId slot,
|
inline void CollectionData::appendPair(SlotWithId key, SlotWithId value,
|
||||||
ResourceManager* resources) {
|
const ResourceManager* resources) {
|
||||||
|
key->setNext(value.id());
|
||||||
|
|
||||||
if (tail_ != NULL_SLOT) {
|
if (tail_ != NULL_SLOT) {
|
||||||
auto tail = resources->getSlot(tail_);
|
auto tail = resources->getSlot(tail_);
|
||||||
tail->setNext(slot.id());
|
tail->setNext(key.id());
|
||||||
tail_ = slot.id();
|
tail_ = value.id();
|
||||||
} else {
|
} else {
|
||||||
head_ = slot.id();
|
head_ = key.id();
|
||||||
tail_ = slot.id();
|
tail_ = value.id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,14 +71,14 @@ inline SlotWithId CollectionData::getPreviousSlot(
|
|||||||
while (currentId != NULL_SLOT) {
|
while (currentId != NULL_SLOT) {
|
||||||
auto currentSlot = resources->getSlot(currentId);
|
auto currentSlot = resources->getSlot(currentId);
|
||||||
if (currentSlot == target)
|
if (currentSlot == target)
|
||||||
return prev;
|
break;
|
||||||
prev = SlotWithId(currentSlot, currentId);
|
prev = SlotWithId(currentSlot, currentId);
|
||||||
currentId = currentSlot->next();
|
currentId = currentSlot->next();
|
||||||
}
|
}
|
||||||
return SlotWithId();
|
return prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void CollectionData::remove(iterator it, ResourceManager* resources) {
|
inline void CollectionData::removeOne(iterator it, ResourceManager* resources) {
|
||||||
if (it.done())
|
if (it.done())
|
||||||
return;
|
return;
|
||||||
auto curr = it.slot_;
|
auto curr = it.slot_;
|
||||||
@ -117,6 +93,24 @@ inline void CollectionData::remove(iterator it, ResourceManager* resources) {
|
|||||||
resources->freeSlot({it.slot_, it.currentId_});
|
resources->freeSlot({it.slot_, it.currentId_});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void CollectionData::removePair(ObjectData::iterator it,
|
||||||
|
ResourceManager* resources) {
|
||||||
|
if (it.done())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto keySlot = it.slot_;
|
||||||
|
|
||||||
|
auto valueId = it.nextId_;
|
||||||
|
auto valueSlot = resources->getSlot(valueId);
|
||||||
|
|
||||||
|
// remove value slot
|
||||||
|
keySlot->setNext(valueSlot->next());
|
||||||
|
resources->freeSlot({valueSlot, valueId});
|
||||||
|
|
||||||
|
// remove key slot
|
||||||
|
removeOne(it, resources);
|
||||||
|
}
|
||||||
|
|
||||||
inline size_t CollectionData::nesting(const ResourceManager* resources) const {
|
inline size_t CollectionData::nesting(const ResourceManager* resources) const {
|
||||||
size_t maxChildNesting = 0;
|
size_t maxChildNesting = 0;
|
||||||
for (auto it = createIterator(resources); !it.done(); it.next(resources)) {
|
for (auto it = createIterator(resources); !it.done(); it.next(resources)) {
|
||||||
|
@ -44,17 +44,18 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
|
|||||||
|
|
||||||
auto slotId = object.head();
|
auto slotId = object.head();
|
||||||
|
|
||||||
|
bool isKey = true;
|
||||||
|
|
||||||
while (slotId != NULL_SLOT) {
|
while (slotId != NULL_SLOT) {
|
||||||
auto slot = resources_->getSlot(slotId);
|
auto slot = resources_->getSlot(slotId);
|
||||||
|
|
||||||
formatter_.writeString(slot->key());
|
|
||||||
write(':');
|
|
||||||
slot->data()->accept(*this);
|
slot->data()->accept(*this);
|
||||||
|
|
||||||
slotId = slot->next();
|
slotId = slot->next();
|
||||||
|
|
||||||
if (slotId != NULL_SLOT)
|
if (slotId != NULL_SLOT)
|
||||||
write(',');
|
write(isKey ? ':' : ',');
|
||||||
|
|
||||||
|
isKey = !isKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
write('}');
|
write('}');
|
||||||
|
@ -45,14 +45,17 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
|
|||||||
if (!it.done()) {
|
if (!it.done()) {
|
||||||
base::write("{\r\n");
|
base::write("{\r\n");
|
||||||
nesting_++;
|
nesting_++;
|
||||||
|
bool isKey = true;
|
||||||
while (!it.done()) {
|
while (!it.done()) {
|
||||||
indent();
|
if (isKey)
|
||||||
base::visit(it.key());
|
indent();
|
||||||
base::write(": ");
|
|
||||||
it->accept(*this);
|
it->accept(*this);
|
||||||
|
|
||||||
it.next(base::resources_);
|
it.next(base::resources_);
|
||||||
base::write(it.done() ? "\r\n" : ",\r\n");
|
if (isKey)
|
||||||
|
base::write(": ");
|
||||||
|
else
|
||||||
|
base::write(it.done() ? "\r\n" : ",\r\n");
|
||||||
|
isKey = !isKey;
|
||||||
}
|
}
|
||||||
nesting_--;
|
nesting_--;
|
||||||
indent();
|
indent();
|
||||||
|
@ -11,8 +11,6 @@
|
|||||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||||
|
|
||||||
inline void ResourceManager::freeSlot(SlotWithId slot) {
|
inline void ResourceManager::freeSlot(SlotWithId slot) {
|
||||||
if (slot->ownsKey())
|
|
||||||
dereferenceString(slot->key());
|
|
||||||
slot->data()->setNull(this);
|
slot->data()->setNull(this);
|
||||||
variantPools_.freeSlot(slot);
|
variantPools_.freeSlot(slot);
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,6 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
|
|||||||
auto slotId = object.head();
|
auto slotId = object.head();
|
||||||
while (slotId != NULL_SLOT) {
|
while (slotId != NULL_SLOT) {
|
||||||
auto slot = resources_->getSlot(slotId);
|
auto slot = resources_->getSlot(slotId);
|
||||||
visit(slot->key());
|
|
||||||
slot->data()->accept(*this);
|
slot->data()->accept(*this);
|
||||||
slotId = slot->next();
|
slotId = slot->next();
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,8 @@ class JsonObjectIterator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
JsonObjectIterator& operator++() {
|
JsonObjectIterator& operator++() {
|
||||||
iterator_.next(resources_);
|
iterator_.next(resources_); // key
|
||||||
|
iterator_.next(resources_); // value
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +70,8 @@ class JsonObjectConstIterator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
JsonObjectConstIterator& operator++() {
|
JsonObjectConstIterator& operator++() {
|
||||||
iterator_.next(resources_);
|
iterator_.next(resources_); // key
|
||||||
|
iterator_.next(resources_); // value
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,27 +16,27 @@ class JsonPair {
|
|||||||
public:
|
public:
|
||||||
// INTERNAL USE ONLY
|
// INTERNAL USE ONLY
|
||||||
JsonPair(detail::ObjectData::iterator iterator,
|
JsonPair(detail::ObjectData::iterator iterator,
|
||||||
detail::ResourceManager* resources)
|
detail::ResourceManager* resources) {
|
||||||
: iterator_(iterator), resources_(resources) {}
|
if (!iterator.done()) {
|
||||||
|
key_ = iterator->asString();
|
||||||
|
iterator.next(resources);
|
||||||
|
value_ = JsonVariant(iterator.data(), resources);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the key.
|
// Returns the key.
|
||||||
JsonString key() const {
|
JsonString key() const {
|
||||||
if (!iterator_.done())
|
return key_;
|
||||||
return JsonString(iterator_.key(), iterator_.ownsKey()
|
|
||||||
? JsonString::Copied
|
|
||||||
: JsonString::Linked);
|
|
||||||
else
|
|
||||||
return JsonString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the value.
|
// Returns the value.
|
||||||
JsonVariant value() {
|
JsonVariant value() {
|
||||||
return JsonVariant(iterator_.data(), resources_);
|
return value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
detail::ObjectData::iterator iterator_;
|
JsonString key_;
|
||||||
detail::ResourceManager* resources_;
|
JsonVariant value_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A read-only key-value pair.
|
// A read-only key-value pair.
|
||||||
@ -44,27 +44,27 @@ class JsonPair {
|
|||||||
class JsonPairConst {
|
class JsonPairConst {
|
||||||
public:
|
public:
|
||||||
JsonPairConst(detail::ObjectData::iterator iterator,
|
JsonPairConst(detail::ObjectData::iterator iterator,
|
||||||
const detail::ResourceManager* resources)
|
const detail::ResourceManager* resources) {
|
||||||
: iterator_(iterator), resources_(resources) {}
|
if (!iterator.done()) {
|
||||||
|
key_ = iterator->asString();
|
||||||
|
iterator.next(resources);
|
||||||
|
value_ = JsonVariantConst(iterator.data(), resources);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the key.
|
// Returns the key.
|
||||||
JsonString key() const {
|
JsonString key() const {
|
||||||
if (!iterator_.done())
|
return key_;
|
||||||
return JsonString(iterator_.key(), iterator_.ownsKey()
|
|
||||||
? JsonString::Copied
|
|
||||||
: JsonString::Linked);
|
|
||||||
else
|
|
||||||
return JsonString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the value.
|
// Returns the value.
|
||||||
JsonVariantConst value() const {
|
JsonVariantConst value() const {
|
||||||
return JsonVariantConst(iterator_.data(), resources_);
|
return value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
detail::ObjectData::iterator iterator_;
|
JsonString key_;
|
||||||
const detail::ResourceManager* resources_;
|
JsonVariantConst value_;
|
||||||
};
|
};
|
||||||
|
|
||||||
ARDUINOJSON_END_PUBLIC_NAMESPACE
|
ARDUINOJSON_END_PUBLIC_NAMESPACE
|
||||||
|
@ -10,34 +10,8 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
|||||||
|
|
||||||
class ObjectData : public CollectionData {
|
class ObjectData : public CollectionData {
|
||||||
public:
|
public:
|
||||||
VariantData* addMember(StringNode* key, ResourceManager* resources) {
|
template <typename TAdaptedString> // also works with StringNode*
|
||||||
ARDUINOJSON_ASSERT(key != nullptr);
|
VariantData* addMember(TAdaptedString key, ResourceManager* resources);
|
||||||
auto it = addSlot(resources);
|
|
||||||
if (it.done())
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
it.setKey(key);
|
|
||||||
return it.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
|
||||||
VariantData* addMember(TAdaptedString key, ResourceManager* resources) {
|
|
||||||
ARDUINOJSON_ASSERT(!key.isNull());
|
|
||||||
if (key.isLinked()) {
|
|
||||||
auto it = addSlot(resources);
|
|
||||||
if (!it.done())
|
|
||||||
it.setKey(key.data());
|
|
||||||
return it.data();
|
|
||||||
} else {
|
|
||||||
auto storedKey = resources->saveString(key);
|
|
||||||
if (!storedKey)
|
|
||||||
return nullptr;
|
|
||||||
auto it = addSlot(resources);
|
|
||||||
if (!it.done())
|
|
||||||
it.setKey(storedKey);
|
|
||||||
return it.data();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources);
|
VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources);
|
||||||
@ -65,6 +39,27 @@ class ObjectData : public CollectionData {
|
|||||||
obj->removeMember(key, resources);
|
obj->removeMember(key, resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void remove(iterator it, ResourceManager* resources) {
|
||||||
|
CollectionData::removePair(it, resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove(ObjectData* obj, ObjectData::iterator it,
|
||||||
|
ResourceManager* resources) {
|
||||||
|
if (!obj)
|
||||||
|
return;
|
||||||
|
obj->remove(it, resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size(const ResourceManager* resources) const {
|
||||||
|
return CollectionData::size(resources) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t size(const ObjectData* obj, const ResourceManager* resources) {
|
||||||
|
if (!obj)
|
||||||
|
return 0;
|
||||||
|
return obj->size(resources);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
iterator findKey(TAdaptedString key, const ResourceManager* resources) const;
|
iterator findKey(TAdaptedString key, const ResourceManager* resources) const;
|
||||||
|
@ -12,15 +12,19 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
|||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
inline VariantData* ObjectData::getMember(
|
inline VariantData* ObjectData::getMember(
|
||||||
TAdaptedString key, const ResourceManager* resources) const {
|
TAdaptedString key, const ResourceManager* resources) const {
|
||||||
return findKey(key, resources).data();
|
auto it = findKey(key, resources);
|
||||||
|
if (it.done())
|
||||||
|
return nullptr;
|
||||||
|
it.next(resources);
|
||||||
|
return it.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
VariantData* ObjectData::getOrAddMember(TAdaptedString key,
|
VariantData* ObjectData::getOrAddMember(TAdaptedString key,
|
||||||
ResourceManager* resources) {
|
ResourceManager* resources) {
|
||||||
auto it = findKey(key, resources);
|
auto data = getMember(key, resources);
|
||||||
if (!it.done())
|
if (data)
|
||||||
return it.data();
|
return data;
|
||||||
return addMember(key, resources);
|
return addMember(key, resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,9 +33,11 @@ inline ObjectData::iterator ObjectData::findKey(
|
|||||||
TAdaptedString key, const ResourceManager* resources) const {
|
TAdaptedString key, const ResourceManager* resources) const {
|
||||||
if (key.isNull())
|
if (key.isNull())
|
||||||
return iterator();
|
return iterator();
|
||||||
|
bool isKey = true;
|
||||||
for (auto it = createIterator(resources); !it.done(); it.next(resources)) {
|
for (auto it = createIterator(resources); !it.done(); it.next(resources)) {
|
||||||
if (stringEquals(key, adaptString(it.key())))
|
if (isKey && stringEquals(key, adaptString(it->asString())))
|
||||||
return it;
|
return it;
|
||||||
|
isKey = !isKey;
|
||||||
}
|
}
|
||||||
return iterator();
|
return iterator();
|
||||||
}
|
}
|
||||||
@ -42,4 +48,23 @@ inline void ObjectData::removeMember(TAdaptedString key,
|
|||||||
remove(findKey(key, resources), resources);
|
remove(findKey(key, resources), resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename TAdaptedString>
|
||||||
|
inline VariantData* ObjectData::addMember(TAdaptedString key,
|
||||||
|
ResourceManager* resources) {
|
||||||
|
auto keySlot = resources->allocSlot();
|
||||||
|
if (!keySlot)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto valueSlot = resources->allocSlot();
|
||||||
|
if (!valueSlot)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!keySlot->data()->setString(key, resources))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
CollectionData::appendPair(keySlot, valueSlot, resources);
|
||||||
|
|
||||||
|
return valueSlot->data();
|
||||||
|
}
|
||||||
|
|
||||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||||
|
@ -14,8 +14,6 @@
|
|||||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
VALUE_MASK = 0x7F,
|
|
||||||
|
|
||||||
OWNED_VALUE_BIT = 0x01,
|
OWNED_VALUE_BIT = 0x01,
|
||||||
VALUE_IS_NULL = 0,
|
VALUE_IS_NULL = 0,
|
||||||
VALUE_IS_RAW_STRING = 0x03,
|
VALUE_IS_RAW_STRING = 0x03,
|
||||||
@ -34,8 +32,6 @@ enum {
|
|||||||
COLLECTION_MASK = 0x60,
|
COLLECTION_MASK = 0x60,
|
||||||
VALUE_IS_OBJECT = 0x20,
|
VALUE_IS_OBJECT = 0x20,
|
||||||
VALUE_IS_ARRAY = 0x40,
|
VALUE_IS_ARRAY = 0x40,
|
||||||
|
|
||||||
OWNED_KEY_BIT = 0x80
|
|
||||||
};
|
};
|
||||||
|
|
||||||
union VariantContent {
|
union VariantContent {
|
||||||
|
@ -410,20 +410,29 @@ class VariantData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
void setString(TAdaptedString value, ResourceManager* resources) {
|
bool setString(TAdaptedString value, ResourceManager* resources) {
|
||||||
setNull(resources);
|
setNull(resources);
|
||||||
|
|
||||||
if (value.isNull())
|
if (value.isNull())
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
if (value.isLinked()) {
|
if (value.isLinked()) {
|
||||||
setLinkedString(value.data());
|
setLinkedString(value.data());
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto dup = resources->saveString(value);
|
auto dup = resources->saveString(value);
|
||||||
if (dup)
|
if (dup) {
|
||||||
setOwnedString(dup);
|
setOwnedString(dup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setString(StringNode* s, ResourceManager*) {
|
||||||
|
setOwnedString(s);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
@ -447,7 +456,13 @@ class VariantData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t size(const ResourceManager* resources) const {
|
size_t size(const ResourceManager* resources) const {
|
||||||
return isCollection() ? content_.asCollection.size(resources) : 0;
|
if (isObject())
|
||||||
|
return content_.asObject.size(resources);
|
||||||
|
|
||||||
|
if (isArray())
|
||||||
|
return content_.asArray.size(resources);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t size(const VariantData* var, const ResourceManager* resources) {
|
static size_t size(const VariantData* var, const ResourceManager* resources) {
|
||||||
@ -489,7 +504,7 @@ class VariantData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t type() const {
|
uint8_t type() const {
|
||||||
return flags_ & VALUE_MASK;
|
return flags_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -503,8 +518,7 @@ class VariantData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setType(uint8_t t) {
|
void setType(uint8_t t) {
|
||||||
flags_ &= OWNED_KEY_BIT;
|
flags_ = t;
|
||||||
flags_ |= t;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ class VariantSlot {
|
|||||||
VariantContent content_;
|
VariantContent content_;
|
||||||
uint8_t flags_;
|
uint8_t flags_;
|
||||||
SlotId next_;
|
SlotId next_;
|
||||||
const char* key_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Placement new
|
// Placement new
|
||||||
@ -30,7 +29,9 @@ class VariantSlot {
|
|||||||
|
|
||||||
static void operator delete(void*, void*) noexcept {}
|
static void operator delete(void*, void*) noexcept {}
|
||||||
|
|
||||||
VariantSlot() : flags_(0), next_(NULL_SLOT), key_(0) {}
|
VariantSlot() : flags_(0), next_(NULL_SLOT) {
|
||||||
|
(void)flags_; // HACK: suppress Clang warning "private field is not used"
|
||||||
|
}
|
||||||
|
|
||||||
VariantData* data() {
|
VariantData* data() {
|
||||||
return reinterpret_cast<VariantData*>(&content_);
|
return reinterpret_cast<VariantData*>(&content_);
|
||||||
@ -47,26 +48,6 @@ class VariantSlot {
|
|||||||
void setNext(SlotId slot) {
|
void setNext(SlotId slot) {
|
||||||
next_ = slot;
|
next_ = slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setKey(const char* k) {
|
|
||||||
ARDUINOJSON_ASSERT(k);
|
|
||||||
flags_ &= VALUE_MASK;
|
|
||||||
key_ = k;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setKey(StringNode* k) {
|
|
||||||
ARDUINOJSON_ASSERT(k);
|
|
||||||
flags_ |= OWNED_KEY_BIT;
|
|
||||||
key_ = k->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* key() const {
|
|
||||||
return key_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ownsKey() const {
|
|
||||||
return (flags_ & OWNED_KEY_BIT) != 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline VariantData* slotData(VariantSlot* slot) {
|
inline VariantData* slotData(VariantSlot* slot) {
|
||||||
@ -80,7 +61,7 @@ constexpr size_t sizeofArray(size_t n) {
|
|||||||
|
|
||||||
// Returns the size (in bytes) of an object with n members.
|
// Returns the size (in bytes) of an object with n members.
|
||||||
constexpr size_t sizeofObject(size_t n) {
|
constexpr size_t sizeofObject(size_t n) {
|
||||||
return n * sizeof(VariantSlot);
|
return 2 * n * sizeof(VariantSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||||
|
Reference in New Issue
Block a user