Cache property values when deserializing them

When deserializing protobuf messages, cache the property values in
the serializer. The cached value life-time is limited by field
number change. This improves the performance of the deserializer
when deserializing repeated fields. So before storing the new value
into the message we first collect all values of the repeated field
that we received from the wire and only when we recognize the change
of the field in the byte stream, update the property value.

Fixes: QTBUG-124594
Pick-to: 6.8 6.7
Change-Id: I5ce5d5c81bf0f7ebf071b5ad374a46787b2767e8
Reviewed-by: Dennis Oberst <dennis.oberst@qt.io>
This commit is contained in:
Alexey Edelev 2024-07-10 22:14:05 +02:00
parent 5edd77f14b
commit 0da8af84b8
3 changed files with 120 additions and 33 deletions

View File

@ -538,7 +538,8 @@ public:
if (metaType.flags() & QMetaType::IsPointer) { if (metaType.flags() & QMetaType::IsPointer) {
auto *messageProperty = propertyData.value<QProtobufMessage *>(); auto *messageProperty = propertyData.value<QProtobufMessage *>();
Q_ASSERT(messageProperty != nullptr); Q_ASSERT(messageProperty != nullptr);
return deserializeObject(messageProperty); ok = deserializeObject(messageProperty);
return propertyData;
} }
auto handler = QtProtobufPrivate::findHandler(metaType); auto handler = QtProtobufPrivate::findHandler(metaType);
@ -593,6 +594,15 @@ public:
{ {
Q_ASSERT(message != nullptr); Q_ASSERT(message != nullptr);
auto restoreOnReturn = qScopeGuard([prevCachedPropertyValue = cachedPropertyValue,
prevCachedIndex = cachedIndex, this]() {
cachedPropertyValue = prevCachedPropertyValue;
cachedIndex = prevCachedIndex;
});
cachedPropertyValue.clear();
cachedIndex = -1;
auto ordering = message->propertyOrdering(); auto ordering = message->propertyOrdering();
Q_ASSERT(ordering != nullptr); Q_ASSERT(ordering != nullptr);
@ -626,21 +636,28 @@ public:
auto store = activeValue; auto store = activeValue;
activeValue = activeObject.value(key); activeValue = activeObject.value(key);
QVariant newPropertyValue = message->property(iter->second, true); if (auto index = ordering->indexOfFieldNumber(iter->second.getFieldNumber());
index != cachedIndex) {
if (!storeCachedValue(message))
return false;
cachedPropertyValue = message->property(iter->second, true);
cachedIndex = index;
}
bool ok = false; bool ok = false;
const auto fieldFlags = iter->second.getFieldFlags(); const auto fieldFlags = iter->second.getFieldFlags();
if (fieldFlags & QtProtobufPrivate::FieldFlag::Enum) { if (fieldFlags & QtProtobufPrivate::FieldFlag::Enum) {
if (fieldFlags & QtProtobufPrivate::FieldFlag::Repeated) { if (fieldFlags & QtProtobufPrivate::FieldFlag::Repeated) {
QMetaType originalMetatype = newPropertyValue.metaType(); QMetaType originalMetatype = cachedPropertyValue.metaType();
newPropertyValue.setValue(deserializeList<QStringList, QString>(activeValue, cachedPropertyValue
ok)); .setValue(deserializeList<QStringList, QString>(activeValue, ok));
if (ok) if (ok)
ok = newPropertyValue.convert(originalMetatype); ok = cachedPropertyValue.convert(originalMetatype);
} else { } else {
const auto metaEnum = getMetaEnum(newPropertyValue.metaType()); const auto metaEnum = getMetaEnum(cachedPropertyValue.metaType());
Q_ASSERT(metaEnum.isValid()); Q_ASSERT(metaEnum.isValid());
newPropertyValue.setValue(deserializeEnum(activeValue, metaEnum, ok)); cachedPropertyValue.setValue(deserializeEnum(activeValue, metaEnum, ok));
} }
if (!ok) if (!ok)
setInvalidFormatError(); setInvalidFormatError();
@ -651,15 +668,17 @@ public:
mapObj.insert("key"_L1, it.key()); mapObj.insert("key"_L1, it.key());
mapObj.insert("value"_L1, it.value()); mapObj.insert("value"_L1, it.value());
activeValue = mapObj; activeValue = mapObj;
newPropertyValue = deserializeValue(newPropertyValue, ok); cachedPropertyValue = deserializeValue(cachedPropertyValue, ok);
} }
activeValue = store; activeValue = store;
} else { } else {
newPropertyValue = deserializeValue(newPropertyValue, ok); cachedPropertyValue = deserializeValue(cachedPropertyValue, ok);
activeValue = store; activeValue = store;
} }
if (ok) if (!ok) {
message->setProperty(iter->second, newPropertyValue); cachedPropertyValue.clear();
cachedIndex = -1;
}
} }
} }
@ -667,7 +686,7 @@ public:
// to deserialize // to deserialize
activeValue = {}; activeValue = {};
return true; return storeCachedValue(message);
} }
void setDeserializationError(QAbstractProtobufSerializer::DeserializationError error, void setDeserializationError(QAbstractProtobufSerializer::DeserializationError error,
@ -701,10 +720,28 @@ public:
static SerializerRegistry handlers; static SerializerRegistry handlers;
[[nodiscard]] bool storeCachedValue(QProtobufMessage *message);
QVariant cachedPropertyValue;
int cachedIndex = -1;
private: private:
QProtobufJsonSerializer *qPtr; QProtobufJsonSerializer *qPtr;
}; };
bool QProtobufJsonSerializerPrivate::storeCachedValue(QProtobufMessage *message)
{
bool ok = true;
if (cachedIndex >= 0 && !cachedPropertyValue.isNull()) {
const auto *ordering = message->propertyOrdering();
QProtobufFieldInfo fieldInfo(*ordering, cachedIndex);
ok = message->setProperty(fieldInfo, cachedPropertyValue);
cachedPropertyValue.clear();
cachedIndex = -1;
}
return ok;
}
QProtobufJsonSerializerPrivate::SerializerRegistry QProtobufJsonSerializerPrivate::handlers = {}; QProtobufJsonSerializerPrivate::SerializerRegistry QProtobufJsonSerializerPrivate::handlers = {};
void QProtobufJsonSerializerPrivate::clearError() void QProtobufJsonSerializerPrivate::clearError()
@ -759,6 +796,8 @@ bool QProtobufJsonSerializer::deserializeMessage(QProtobufMessage *message,
} }
d_ptr->activeValue = document.object(); d_ptr->activeValue = document.object();
d_ptr->cachedPropertyValue.clear();
d_ptr->cachedIndex = -1;
return d_ptr->deserializeObject(message); return d_ptr->deserializeObject(message);
} }

View File

@ -251,12 +251,22 @@ void QProtobufSerializerPrivate::clearError()
bool QProtobufSerializer::deserializeMessage(QProtobufMessage *message, QByteArrayView data) const bool QProtobufSerializer::deserializeMessage(QProtobufMessage *message, QByteArrayView data) const
{ {
d_ptr->cachedPropertyValue = QVariant();
d_ptr->cachedIndex = -1;
d_ptr->clearError(); d_ptr->clearError();
d_ptr->it = QProtobufSelfcheckIterator::fromView(data); d_ptr->it = QProtobufSelfcheckIterator::fromView(data);
bool ok = true;
while (d_ptr->it.isValid() && d_ptr->it != data.end()) { while (d_ptr->it.isValid() && d_ptr->it != data.end()) {
if (!d_ptr->deserializeProperty(message)) if (!d_ptr->deserializeProperty(message)) {
return false; ok = false;
break;
} }
}
if (!d_ptr->storeCachedValue(message) || !ok)
return false;
if (!d_ptr->it.isValid()) if (!d_ptr->it.isValid())
d_ptr->setUnexpectedEndOfStreamError(); d_ptr->setUnexpectedEndOfStreamError();
return d_ptr->it.isValid(); return d_ptr->it.isValid();
@ -287,15 +297,31 @@ bool QProtobufSerializerPrivate::deserializeObject(QProtobufMessage *message)
setUnexpectedEndOfStreamError(); setUnexpectedEndOfStreamError();
return false; return false;
} }
auto prevIt = it;
auto restoreOnReturn = qScopeGuard([&](){ it = prevIt; }); auto restoreOnReturn = qScopeGuard([prevIt = it, prevCachedPropertyValue = cachedPropertyValue,
prevCachedIndex = cachedIndex, this]() {
it = prevIt;
cachedPropertyValue = prevCachedPropertyValue;
cachedIndex = prevCachedIndex;
});
cachedPropertyValue.clear();
cachedIndex = -1;
QByteArrayView data = *array; QByteArrayView data = *array;
clearError(); clearError();
it = QProtobufSelfcheckIterator::fromView(data); it = QProtobufSelfcheckIterator::fromView(data);
bool ok = true;
while (it.isValid() && it != data.end()) { while (it.isValid() && it != data.end()) {
if (!deserializeProperty(message)) if (!deserializeProperty(message)) {
return false; ok = false;
break;
} }
}
if (!storeCachedValue(message) || !ok)
return false;
if (!it.isValid()) if (!it.isValid())
setUnexpectedEndOfStreamError(); setUnexpectedEndOfStreamError();
return it.isValid(); return it.isValid();
@ -550,32 +576,36 @@ bool QProtobufSerializerPrivate::deserializeProperty(QProtobufMessage *message)
} }
QProtobufFieldInfo fieldInfo(*ordering, index); QProtobufFieldInfo fieldInfo(*ordering, index);
QVariant newPropertyValue = message->property(fieldInfo, true); if (cachedIndex != index) {
QMetaType metaType = newPropertyValue.metaType(); if (!storeCachedValue(message))
return false;
cachedPropertyValue = message->property(fieldInfo, true);
cachedIndex = index;
}
QMetaType metaType = cachedPropertyValue.metaType();
qProtoDebug() << "wireType:" << wireType << "metaType:" << metaType.name() qProtoDebug() << "wireType:" << wireType << "metaType:" << metaType.name()
<< "currentByte:" << QString::number((*it), 16); << "currentByte:" << QString::number((*it), 16);
if (metaType.flags() & QMetaType::IsPointer) { if (metaType.flags() & QMetaType::IsPointer) {
auto *messageProperty = newPropertyValue.value<QProtobufMessage *>(); auto *messageProperty = cachedPropertyValue.value<QProtobufMessage *>();
Q_ASSERT(messageProperty != nullptr); Q_ASSERT(messageProperty != nullptr);
if (deserializeObject(messageProperty)) return deserializeObject(messageProperty);
return message->setProperty(fieldInfo, std::move(newPropertyValue));
return false;
} }
const auto fieldFlags = fieldInfo.getFieldFlags(); const auto fieldFlags = fieldInfo.getFieldFlags();
if (fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::Enum)) { if (fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::Enum)) {
if (fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::Repeated)) { if (fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::Repeated)) {
auto intList = newPropertyValue.value<QList<QtProtobuf::int64>>(); auto intList = cachedPropertyValue.value<QList<QtProtobuf::int64>>();
if (deserializeEnumList(intList)) { if (deserializeEnumList(intList)) {
newPropertyValue.setValue(intList); cachedPropertyValue.setValue(intList);
return message->setProperty(fieldInfo, std::move(newPropertyValue)); return true;
} }
return false; return false;
} else { } else {
if (deserializeBasic<QtProtobuf::int64>(it, newPropertyValue)) if (deserializeBasic<QtProtobuf::int64>(it, cachedPropertyValue))
return message->setProperty(fieldInfo, std::move(newPropertyValue)); return true;
return false; return false;
} }
} }
@ -608,7 +638,7 @@ bool QProtobufSerializerPrivate::deserializeProperty(QProtobufMessage *message)
} }
} }
if (!basicHandler->deserializer(it, newPropertyValue)) { if (!basicHandler->deserializer(it, cachedPropertyValue)) {
setUnexpectedEndOfStreamError(); setUnexpectedEndOfStreamError();
return false; return false;
} }
@ -624,10 +654,23 @@ bool QProtobufSerializerPrivate::deserializeProperty(QProtobufMessage *message)
QCoreApplication::translate("QtProtobuf", error.toUtf8().data())); QCoreApplication::translate("QtProtobuf", error.toUtf8().data()));
return false; return false;
} }
handler.deserializer(q_ptr, newPropertyValue); handler.deserializer(q_ptr, cachedPropertyValue);
} }
return message->setProperty(fieldInfo, std::move(newPropertyValue)); return true;
}
bool QProtobufSerializerPrivate::storeCachedValue(QProtobufMessage *message)
{
bool ok = true;
if (cachedIndex >= 0 && !cachedPropertyValue.isNull()) {
const auto *ordering = message->propertyOrdering();
QProtobufFieldInfo fieldInfo(*ordering, cachedIndex);
ok = message->setProperty(fieldInfo, cachedPropertyValue);
cachedPropertyValue.clear();
cachedIndex = -1;
}
return ok;
} }
QAbstractProtobufSerializer::DeserializationError QProtobufSerializer::deserializationError() const QAbstractProtobufSerializer::DeserializationError QProtobufSerializer::deserializationError() const

View File

@ -520,6 +520,8 @@ public:
void clearError(); void clearError();
void setUnexpectedEndOfStreamError(); void setUnexpectedEndOfStreamError();
[[nodiscard]] bool storeCachedValue(QProtobufMessage *message);
QAbstractProtobufSerializer::DeserializationError deserializationError = QAbstractProtobufSerializer::DeserializationError deserializationError =
QAbstractProtobufSerializer::NoDeserializerError; QAbstractProtobufSerializer::NoDeserializerError;
QString deserializationErrorString; QString deserializationErrorString;
@ -531,6 +533,9 @@ public:
static const QtProtobufPrivate::QProtobufFieldInfo mapValueOrdering; static const QtProtobufPrivate::QProtobufFieldInfo mapValueOrdering;
QVariant cachedPropertyValue;
int cachedIndex = -1;
private: private:
Q_DISABLE_COPY_MOVE(QProtobufSerializerPrivate) Q_DISABLE_COPY_MOVE(QProtobufSerializerPrivate)
QProtobufSerializer *q_ptr; QProtobufSerializer *q_ptr;