Store object members with two slots: one for the key and one for the value

This commit is contained in:
Benoit Blanchon
2024-08-23 15:31:47 +02:00
parent a2b09bfbd2
commit 09c89dcacf
24 changed files with 229 additions and 173 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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()) {
if (isKey)
indent(); indent();
base::visit(it.key());
base::write(": ");
it->accept(*this); it->accept(*this);
it.next(base::resources_); it.next(base::resources_);
if (isKey)
base::write(": ");
else
base::write(it.done() ? "\r\n" : ",\r\n"); base::write(it.done() ? "\r\n" : ",\r\n");
isKey = !isKey;
} }
nesting_--; nesting_--;
indent(); indent();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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