mirror of https://github.com/qt/qtgrpc.git
Generalize deserializer in protobuf and JSON serializers
Move the generic logic of serializer to a separate class and use this class to implement the protobuf and JSON deserialization. This also fixed one of the conformance usecases since related to invalid enum handling. Pick-to: 6.8 6.9 Task-number: QTBUG-128812 Fixes: QTBUG-112423 Fixes: QTBUG-112425 Fixes: QTBUG-112424 Change-Id: I400303a8666df90050a54bd7036daa0107adcce5 Reviewed-by: Dennis Oberst <dennis.oberst@qt.io>
This commit is contained in:
parent
27b618fcc4
commit
3df9d285da
|
@ -6,6 +6,7 @@ qt_internal_add_module(Protobuf
|
|||
protobuffieldpresencechecker_p.h
|
||||
qtprotobufglobal.h
|
||||
qabstractprotobufserializer.cpp qabstractprotobufserializer.h
|
||||
qprotobufdeserializerbase_p.h qprotobufdeserializerbase.cpp
|
||||
qprotobufjsonserializer.cpp qprotobufjsonserializer.h
|
||||
qprotobuflazymessagepointer.h
|
||||
qprotobufmessage.cpp qprotobufmessage.h qprotobufmessage_p.h
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#include <QtProtobuf/private/qprotobufdeserializerbase_p.h>
|
||||
|
||||
#include <QtProtobuf/private/qprotobufregistration_p.h>
|
||||
#include <QtProtobuf/private/qtprotobuflogging_p.h>
|
||||
#include <QtProtobuf/private/qtprotobufserializerhelpers_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QProtobufDeserializerBase::QProtobufDeserializerBase()
|
||||
= default;
|
||||
|
||||
QProtobufDeserializerBase::~QProtobufDeserializerBase()
|
||||
= default;
|
||||
|
||||
bool QProtobufDeserializerBase::deserializeMessageField(QProtobufMessage *message)
|
||||
{
|
||||
Q_ASSERT(message != nullptr);
|
||||
|
||||
auto prevCachedRepeatedIterator = std::move(m_cachedRepeatedIterator);
|
||||
auto prevCachedPropertyValue = m_cachedPropertyValue;
|
||||
auto prevCachedIndex = m_cachedIndex;
|
||||
clearCachedValue();
|
||||
bool result = deserializeMessage(message);
|
||||
m_cachedPropertyValue = prevCachedPropertyValue;
|
||||
m_cachedIndex = prevCachedIndex;
|
||||
m_cachedRepeatedIterator = std::move(prevCachedRepeatedIterator);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QProtobufDeserializerBase::deserializeMessage(QProtobufMessage *message)
|
||||
{
|
||||
Q_ASSERT(message != nullptr);
|
||||
|
||||
const auto *ordering = message->propertyOrdering();
|
||||
for (int fieldIndex = nextFieldIndex(message); fieldIndex >= 0;
|
||||
fieldIndex = nextFieldIndex(message)) {
|
||||
QtProtobufPrivate::QProtobufFieldInfo fieldInfo(*ordering, fieldIndex);
|
||||
if (m_cachedIndex != fieldIndex) {
|
||||
if (!storeCachedValue(message)) {
|
||||
setError(QAbstractProtobufSerializer::Error::InvalidFormat,
|
||||
"Unable to store the property in message");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_cachedPropertyValue = QtProtobufSerializerHelpers::messageProperty(message, fieldInfo,
|
||||
true);
|
||||
m_cachedIndex = fieldIndex;
|
||||
}
|
||||
|
||||
QMetaType metaType = m_cachedPropertyValue.metaType();
|
||||
if (metaType.flags().testFlag(QMetaType::IsPointer)) {
|
||||
if (!deserializeMessageField(m_cachedPropertyValue.value<QProtobufMessage *>()))
|
||||
return false;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto fieldFlags = fieldInfo.fieldFlags();
|
||||
if ((fieldFlags.testFlags(RepeatedMessageFlags)
|
||||
|| fieldFlags.testFlags({ QtProtobufPrivate::FieldFlag::Map }))
|
||||
&& m_cachedPropertyValue.canView(QMetaType::fromType<QProtobufRepeatedIterator>())) {
|
||||
if (!m_cachedRepeatedIterator.isValid())
|
||||
m_cachedRepeatedIterator = m_cachedPropertyValue.view<QProtobufRepeatedIterator>();
|
||||
|
||||
if (!deserializeMessageField(m_cachedRepeatedIterator.addNext()))
|
||||
return false;
|
||||
|
||||
m_cachedRepeatedIterator.push();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::Enum)) {
|
||||
if (!deserializeEnum(m_cachedPropertyValue, fieldInfo)) {
|
||||
setError(QAbstractProtobufSerializer::Error::UnknownType,
|
||||
"Unable to covert enum field to compatible serialization format");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (deserializeScalarField(m_cachedPropertyValue, fieldInfo)) {
|
||||
if (!m_cachedPropertyValue.isValid())
|
||||
return false;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
auto handler = QtProtobufPrivate::findHandler(metaType);
|
||||
if (!handler.deserializer) {
|
||||
qProtoWarning() << "No deserializer for type" << metaType.name();
|
||||
setError(QAbstractProtobufSerializer::Error::UnknownType,
|
||||
QString::fromUtf8("No deserializer is registered for type %1")
|
||||
.arg(QString::fromUtf8(metaType.name())));
|
||||
return false;
|
||||
}
|
||||
|
||||
handler.deserializer([this](QProtobufMessage *
|
||||
message) { return this->deserializeMessageField(message); },
|
||||
m_cachedPropertyValue.data());
|
||||
}
|
||||
|
||||
if (!storeCachedValue(message)) {
|
||||
setError(QAbstractProtobufSerializer::Error::InvalidFormat,
|
||||
"Unable to store the property in message");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QProtobufDeserializerBase::storeCachedValue(QProtobufMessage *message)
|
||||
{
|
||||
bool ok = true;
|
||||
if (m_cachedIndex >= 0 && !m_cachedPropertyValue.isNull()) {
|
||||
const auto *ordering = message->propertyOrdering();
|
||||
QtProtobufPrivate::QProtobufFieldInfo fieldInfo(*ordering, m_cachedIndex);
|
||||
ok = QtProtobufSerializerHelpers::setMessageProperty(message, fieldInfo,
|
||||
m_cachedPropertyValue);
|
||||
|
||||
clearCachedValue();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
void QProtobufDeserializerBase::clearCachedValue()
|
||||
{
|
||||
m_cachedPropertyValue.clear();
|
||||
m_cachedIndex = -1;
|
||||
m_cachedRepeatedIterator = {};
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
#ifndef QPROTOBUFDESERIALIZERBASE_P_H
|
||||
#define QPROTOBUFDESERIALIZERBASE_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtProtobuf/qabstractprotobufserializer.h>
|
||||
#include <QtProtobuf/qprotobufpropertyordering.h>
|
||||
#include <QtProtobuf/qprotobufrepeatediterator.h>
|
||||
|
||||
#include <QtCore/qtconfigmacros.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QVariant;
|
||||
class QProtobufMessage;
|
||||
|
||||
class QProtobufDeserializerBase
|
||||
{
|
||||
public:
|
||||
QProtobufDeserializerBase();
|
||||
|
||||
bool deserializeMessage(QProtobufMessage *message);
|
||||
void clearCachedValue();
|
||||
|
||||
protected:
|
||||
~QProtobufDeserializerBase();
|
||||
|
||||
virtual void setError(QAbstractProtobufSerializer::Error error, QAnyStringView errorString) = 0;
|
||||
|
||||
private:
|
||||
virtual bool deserializeEnum(QVariant &value,
|
||||
const QtProtobufPrivate::QProtobufFieldInfo &fieldInfo) = 0;
|
||||
virtual int nextFieldIndex(QProtobufMessage *message) = 0;
|
||||
virtual bool deserializeScalarField(QVariant &value,
|
||||
const QtProtobufPrivate::QProtobufFieldInfo &fieldInfo) = 0;
|
||||
|
||||
bool deserializeMessageField(QProtobufMessage *message);
|
||||
bool storeCachedValue(QProtobufMessage *message);
|
||||
|
||||
QVariant m_cachedPropertyValue;
|
||||
QProtobufRepeatedIterator m_cachedRepeatedIterator;
|
||||
int m_cachedIndex = -1;
|
||||
|
||||
Q_DISABLE_COPY_MOVE(QProtobufDeserializerBase)
|
||||
|
||||
public:
|
||||
static constexpr QtProtobufPrivate::FieldFlags RepeatedMessageFlags{
|
||||
QtProtobufPrivate::FieldFlag::Message, QtProtobufPrivate::FieldFlag::Repeated
|
||||
};
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QPROTOBUFDESERIALIZERBASE_P_H
|
|
@ -4,6 +4,7 @@
|
|||
#include <QtProtobuf/qprotobufjsonserializer.h>
|
||||
|
||||
#include <QtProtobuf/private/protobuffieldpresencechecker_p.h>
|
||||
#include <QtProtobuf/private/qprotobufdeserializerbase_p.h>
|
||||
#include <QtProtobuf/private/qprotobufregistration_p.h>
|
||||
#include <QtProtobuf/private/qprotobufserializerbase_p.h>
|
||||
#include <QtProtobuf/private/qtprotobufdefs_p.h>
|
||||
|
@ -19,7 +20,6 @@
|
|||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
@ -85,6 +85,38 @@ private:
|
|||
Q_DISABLE_COPY_MOVE(QProtobufJsonSerializerImpl)
|
||||
};
|
||||
|
||||
class QProtobufJsonSerializerPrivate;
|
||||
class QProtobufJsonDeserializerImpl final : public QProtobufDeserializerBase
|
||||
{
|
||||
public:
|
||||
explicit QProtobufJsonDeserializerImpl(QProtobufJsonSerializerPrivate *parent);
|
||||
~QProtobufJsonDeserializerImpl();
|
||||
|
||||
void reset(QJsonObject obj);
|
||||
|
||||
void setError(QAbstractProtobufSerializer::Error error, QAnyStringView errorString) override;
|
||||
void setUnexpectedEndOfStreamError();
|
||||
void setInvalidFormatError();
|
||||
|
||||
private:
|
||||
bool deserializeEnum(QVariant &value,
|
||||
const QtProtobufPrivate::QProtobufFieldInfo &fieldInfo) override;
|
||||
int nextFieldIndex(QProtobufMessage *message) override;
|
||||
bool deserializeScalarField(QVariant &, const QtProtobufPrivate::QProtobufFieldInfo &) override;
|
||||
|
||||
struct JsonDeserializerState
|
||||
{
|
||||
JsonDeserializerState(const QJsonObject &obj) : obj(obj) { }
|
||||
|
||||
QJsonObject obj = {};
|
||||
int index = 0;
|
||||
QJsonValue scalarValue = {};
|
||||
};
|
||||
|
||||
QList<JsonDeserializerState> m_state;
|
||||
QProtobufJsonSerializerPrivate *m_parent = nullptr;
|
||||
};
|
||||
|
||||
class QProtobufJsonSerializerPrivate final
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(QProtobufJsonSerializerPrivate)
|
||||
|
@ -211,7 +243,7 @@ public:
|
|||
return QJsonValue(arr);
|
||||
}
|
||||
|
||||
QProtobufJsonSerializerPrivate()
|
||||
QProtobufJsonSerializerPrivate() : deserializer(this)
|
||||
{
|
||||
[[maybe_unused]] static bool initialized = []() -> bool {
|
||||
handlers[qMetaTypeId<QtProtobuf::int32>()] = createCommonHandler<QtProtobuf::int32>();
|
||||
|
@ -274,17 +306,6 @@ public:
|
|||
}
|
||||
~QProtobufJsonSerializerPrivate() = default;
|
||||
|
||||
[[nodiscard]] static QMetaEnum getMetaEnum(QMetaType enumMetaType)
|
||||
{
|
||||
const auto *metaObject = enumMetaType.metaObject();
|
||||
Q_ASSERT(metaObject);
|
||||
for (int i = 0; i < metaObject->enumeratorCount(); ++i) {
|
||||
if (metaObject->enumerator(i).metaType() == enumMetaType)
|
||||
return metaObject->enumerator(i);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static QVariant deserializeCommon(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
|
@ -427,236 +448,15 @@ public:
|
|||
return QVariant::fromValue(list);
|
||||
}
|
||||
|
||||
static QtProtobuf::int64 deserializeEnum(const QJsonValue &value, const QMetaEnum &metaEnum,
|
||||
bool &ok)
|
||||
{
|
||||
QtProtobuf::int64 result = 0;
|
||||
if (value.isString()) {
|
||||
QString enumKey = value.toString();
|
||||
result = metaEnum.keyToValue(enumKey.toUtf8().data(), &ok);
|
||||
}
|
||||
if (ok)
|
||||
return result;
|
||||
|
||||
result = deserialize<QtProtobuf::int64>(value, ok);
|
||||
if (ok) {
|
||||
ok = false;
|
||||
// Make sure that it's the known enum value
|
||||
for (int i = 0; i < metaEnum.keyCount(); ++i) {
|
||||
if (metaEnum.value(i) == result) {
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariant deserializeValue(QVariant propertyData, bool &ok)
|
||||
{
|
||||
ok = false;
|
||||
auto metaType = propertyData.metaType();
|
||||
if (metaType.flags() & QMetaType::IsPointer) {
|
||||
auto *messageProperty = propertyData.value<QProtobufMessage *>();
|
||||
Q_ASSERT(messageProperty != nullptr);
|
||||
ok = deserializeObject(messageProperty);
|
||||
return propertyData;
|
||||
}
|
||||
|
||||
if (propertyData.canView(QMetaType::fromType<QProtobufRepeatedIterator>())) {
|
||||
QProtobufRepeatedIterator propertyIt = propertyData.view<QProtobufRepeatedIterator>();
|
||||
if (activeValue.isArray()) {
|
||||
QJsonArray array = activeValue.toArray();
|
||||
if (array.isEmpty()) {
|
||||
ok = true;
|
||||
activeValue = {};
|
||||
return propertyData;
|
||||
}
|
||||
|
||||
while (!array.isEmpty()
|
||||
&& lastError == QAbstractProtobufSerializer::Error::None) {
|
||||
activeValue = array.takeAt(0);
|
||||
if (deserializeObject(propertyIt.addNext()))
|
||||
propertyIt.push();
|
||||
}
|
||||
ok = propertyData.isValid();
|
||||
} else {
|
||||
while (!activeValue.isNull()
|
||||
&& lastError == QAbstractProtobufSerializer::Error::None) {
|
||||
if (deserializeObject(propertyIt.addNext()))
|
||||
propertyIt.push();
|
||||
}
|
||||
}
|
||||
ok = lastError == QAbstractProtobufSerializer::Error::None;
|
||||
return propertyData;
|
||||
}
|
||||
|
||||
auto handler = QtProtobufPrivate::findHandler(metaType);
|
||||
if (handler.deserializer) {
|
||||
while (!activeValue.isNull()
|
||||
&& lastError == QAbstractProtobufSerializer::Error::None) {
|
||||
handler
|
||||
.deserializer([this](QProtobufMessage
|
||||
*message) { return this->deserializeObject(message); },
|
||||
propertyData.data());
|
||||
}
|
||||
ok = propertyData.isValid();
|
||||
} else {
|
||||
int userType = propertyData.userType();
|
||||
auto handler = handlers.constFind(userType);
|
||||
if (handler != handlers.constEnd() && handler.value().deserializer) {
|
||||
propertyData = handler.value().deserializer(activeValue, ok);
|
||||
if (!ok)
|
||||
setInvalidFormatError();
|
||||
} else {
|
||||
setDeserializationError(QAbstractProtobufSerializer::Error::UnknownType,
|
||||
QCoreApplication::
|
||||
translate("QtProtobuf",
|
||||
"No deserializer is registered for type %1")
|
||||
.arg(userType));
|
||||
}
|
||||
}
|
||||
return propertyData;
|
||||
}
|
||||
|
||||
bool deserializeObject(QProtobufMessage *message)
|
||||
{
|
||||
Q_ASSERT(message != nullptr);
|
||||
|
||||
auto restoreOnReturn = qScopeGuard([prevCachedPropertyValue = cachedPropertyValue,
|
||||
prevCachedIndex = cachedIndex, this]() {
|
||||
cachedPropertyValue = prevCachedPropertyValue;
|
||||
cachedIndex = prevCachedIndex;
|
||||
});
|
||||
|
||||
cachedPropertyValue.clear();
|
||||
cachedIndex = -1;
|
||||
|
||||
auto ordering = message->propertyOrdering();
|
||||
Q_ASSERT(ordering != nullptr);
|
||||
|
||||
std::map<QString, QProtobufFieldInfo> msgContainer; // map<key, fieldInfo>
|
||||
for (int index = 0; index < ordering->fieldCount(); ++index) {
|
||||
int fieldIndex = ordering->fieldNumber(index);
|
||||
Q_ASSERT_X(fieldIndex <= ProtobufFieldNumMax && fieldIndex >= ProtobufFieldNumMin, "",
|
||||
"fieldIndex is out of range");
|
||||
QProtobufFieldInfo fieldInfo(*ordering, index);
|
||||
QString key = fieldInfo.jsonName().toString();
|
||||
msgContainer.insert(std::pair<QString, QProtobufFieldInfo>(key, fieldInfo));
|
||||
}
|
||||
|
||||
if (!activeValue.isObject()) {
|
||||
setInvalidFormatError();
|
||||
activeValue = {};
|
||||
return false;
|
||||
}
|
||||
QJsonObject activeObject = activeValue.toObject();
|
||||
// Go through QJSON doc and find keys that are presented in msgContainer
|
||||
for (auto &key : activeObject.keys()) {
|
||||
if (activeObject.value(key).isNull())
|
||||
continue;
|
||||
|
||||
std::map<QString, QProtobufFieldInfo>::iterator iter = msgContainer
|
||||
.find(key);
|
||||
if (iter == msgContainer.end())
|
||||
iter = msgContainer.find(convertJsonKeyToJsonName(key));
|
||||
|
||||
if (iter != msgContainer.end()) {
|
||||
auto store = activeValue;
|
||||
activeValue = activeObject.value(key);
|
||||
|
||||
if (auto index = ordering->indexOfFieldNumber(iter->second.fieldNumber());
|
||||
index != cachedIndex) {
|
||||
if (!storeCachedValue(message))
|
||||
return false;
|
||||
cachedPropertyValue = QtProtobufSerializerHelpers::messageProperty(message,
|
||||
iter->second,
|
||||
true);
|
||||
cachedIndex = index;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
|
||||
const auto fieldFlags = iter->second.fieldFlags();
|
||||
if (fieldFlags & QtProtobufPrivate::FieldFlag::Enum) {
|
||||
if (fieldFlags & QtProtobufPrivate::FieldFlag::Repeated) {
|
||||
QMetaType originalMetatype = cachedPropertyValue.metaType();
|
||||
cachedPropertyValue
|
||||
.setValue(deserializeList<QStringList, QString>(activeValue, ok));
|
||||
if (ok)
|
||||
ok = cachedPropertyValue.convert(originalMetatype);
|
||||
} else {
|
||||
const auto metaEnum = getMetaEnum(cachedPropertyValue.metaType());
|
||||
Q_ASSERT(metaEnum.isValid());
|
||||
cachedPropertyValue.setValue(deserializeEnum(activeValue, metaEnum, ok));
|
||||
}
|
||||
if (!ok)
|
||||
setInvalidFormatError();
|
||||
} else if (fieldFlags & QtProtobufPrivate::FieldFlag::Map) {
|
||||
auto activeValueObj = activeValue.toObject();
|
||||
for (auto it = activeValueObj.begin(); it != activeValueObj.end(); ++it) {
|
||||
auto mapObj = QJsonObject{};
|
||||
mapObj.insert("key"_L1, it.key());
|
||||
mapObj.insert("value"_L1, it.value());
|
||||
activeValue = mapObj;
|
||||
cachedPropertyValue = deserializeValue(cachedPropertyValue, ok);
|
||||
}
|
||||
activeValue = store;
|
||||
} else {
|
||||
cachedPropertyValue = deserializeValue(cachedPropertyValue, ok);
|
||||
activeValue = store;
|
||||
}
|
||||
if (!ok) {
|
||||
cachedPropertyValue.clear();
|
||||
cachedIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Once all keys are deserialized we assume that activeValue is empty, nothing left
|
||||
// to deserialize
|
||||
activeValue = {};
|
||||
|
||||
return storeCachedValue(message);
|
||||
}
|
||||
|
||||
void setDeserializationError(QAbstractProtobufSerializer::Error error,
|
||||
const QString &errorString)
|
||||
{
|
||||
lastError = error;
|
||||
lastErrorString = errorString;
|
||||
}
|
||||
|
||||
void setUnexpectedEndOfStreamError()
|
||||
{
|
||||
setDeserializationError(QAbstractProtobufSerializer::Error::UnexpectedEndOfStream,
|
||||
QCoreApplication::translate("QtProtobuf",
|
||||
"JSON: Unexpected end of stream"));
|
||||
}
|
||||
|
||||
void setInvalidFormatError()
|
||||
{
|
||||
setDeserializationError(QAbstractProtobufSerializer::Error::InvalidFormat,
|
||||
QCoreApplication::
|
||||
translate("QtProtobuf",
|
||||
"JSON: One or more fields have invalid format"));
|
||||
}
|
||||
|
||||
void clearError();
|
||||
|
||||
QAbstractProtobufSerializer::Error lastError = QAbstractProtobufSerializer::Error::None;
|
||||
QString lastErrorString;
|
||||
QJsonValue activeValue;
|
||||
|
||||
static SerializerRegistry handlers;
|
||||
|
||||
[[nodiscard]] bool storeCachedValue(QProtobufMessage *message);
|
||||
|
||||
QVariant cachedPropertyValue;
|
||||
int cachedIndex = -1;
|
||||
|
||||
QProtobufJsonSerializerImpl serializer;
|
||||
QProtobufJsonDeserializerImpl deserializer;
|
||||
};
|
||||
|
||||
QProtobufJsonSerializerImpl::~QProtobufJsonSerializerImpl() = default;
|
||||
|
@ -741,20 +541,171 @@ void QProtobufJsonSerializerImpl::serializeMessageFieldEnd(const QProtobufMessag
|
|||
m_result = store;
|
||||
}
|
||||
|
||||
bool QProtobufJsonSerializerPrivate::storeCachedValue(QProtobufMessage *message)
|
||||
QProtobufJsonDeserializerImpl::QProtobufJsonDeserializerImpl(QProtobufJsonSerializerPrivate *parent)
|
||||
: m_parent(parent)
|
||||
{
|
||||
bool ok = true;
|
||||
if (cachedIndex >= 0 && !cachedPropertyValue.isNull()) {
|
||||
const auto *ordering = message->propertyOrdering();
|
||||
QProtobufFieldInfo fieldInfo(*ordering, cachedIndex);
|
||||
ok = QtProtobufSerializerHelpers::setMessageProperty(message, fieldInfo,
|
||||
cachedPropertyValue);
|
||||
cachedPropertyValue.clear();
|
||||
cachedIndex = -1;
|
||||
}
|
||||
|
||||
QProtobufJsonDeserializerImpl::~QProtobufJsonDeserializerImpl()
|
||||
= default;
|
||||
|
||||
void QProtobufJsonDeserializerImpl::reset(QJsonObject obj)
|
||||
{
|
||||
m_state.clear();
|
||||
if (!obj.isEmpty())
|
||||
m_state.push_back({ obj });
|
||||
}
|
||||
|
||||
void QProtobufJsonDeserializerImpl::setError(QAbstractProtobufSerializer::Error error,
|
||||
QAnyStringView errorString)
|
||||
{
|
||||
m_parent->lastError = error;
|
||||
m_parent->lastErrorString = errorString.toString();
|
||||
}
|
||||
|
||||
bool QProtobufJsonDeserializerImpl::deserializeEnum(QVariant &value,
|
||||
const QtProtobufPrivate::QProtobufFieldInfo
|
||||
&fieldInfo)
|
||||
{
|
||||
bool ok = false;
|
||||
auto &state = m_state.last();
|
||||
if (fieldInfo.fieldFlags().testFlag(QtProtobufPrivate::FieldFlag::Repeated)) {
|
||||
value = QProtobufJsonSerializerPrivate::deserializeList<QStringList,
|
||||
QString>(state.scalarValue, ok);
|
||||
} else {
|
||||
// It's allowed to pass single enum value as numeric value.
|
||||
// Make the backward value conversion and deserialize enum as QtProtobuf::int64.
|
||||
if (state.scalarValue.isString()) {
|
||||
value = QProtobufJsonSerializerPrivate::deserializeCommon<QString>(state.scalarValue,
|
||||
ok);
|
||||
} else {
|
||||
value = QProtobufJsonSerializerPrivate::deserializeCommon<
|
||||
QtProtobuf::int64>(state.scalarValue, ok);
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
int QProtobufJsonDeserializerImpl::nextFieldIndex(QProtobufMessage *message)
|
||||
{
|
||||
const auto *ordering = message->propertyOrdering();
|
||||
const int fieldCount = ordering->fieldCount();
|
||||
if (fieldCount == 0)
|
||||
return -1;
|
||||
|
||||
JsonDeserializerState &state = m_state.last();
|
||||
state.scalarValue = {};
|
||||
while (state.index < fieldCount) {
|
||||
const auto jsonName = ordering->jsonName(state.index);
|
||||
const auto keys = state.obj.keys();
|
||||
const auto it = std::find_if(keys.constBegin(), keys.constEnd(),
|
||||
[&jsonName](const auto &val) {
|
||||
return jsonName == val
|
||||
|| jsonName == convertJsonKeyToJsonName(val);
|
||||
});
|
||||
|
||||
if (it == keys.constEnd()) {
|
||||
++state.index;
|
||||
continue;
|
||||
}
|
||||
|
||||
QtProtobufPrivate::FieldFlags flags = ordering->fieldFlags(state.index);
|
||||
QJsonValue val = state.obj.value(*it);
|
||||
if (val.isNull()) {
|
||||
++state.index;
|
||||
continue;
|
||||
}
|
||||
|
||||
int index = state.index;
|
||||
if (flags.testFlags({ QtProtobufPrivate::FieldFlag::Message,
|
||||
QtProtobufPrivate::FieldFlag::Repeated })) {
|
||||
if (!val.isArray()) {
|
||||
setInvalidFormatError();
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto array = val.toArray();
|
||||
if (array.isEmpty()) {
|
||||
++state.index;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!array.at(0).isObject()) {
|
||||
setInvalidFormatError();
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto nextObject = array.takeAt(0).toObject();
|
||||
state.obj.insert(*it, array);
|
||||
m_state.push_back({ nextObject });
|
||||
} else if (flags.testFlag(QtProtobufPrivate::FieldFlag::Map)) {
|
||||
if (!val.isObject()) {
|
||||
setInvalidFormatError();
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto mapObject = val.toObject();
|
||||
if (mapObject.isEmpty()) {
|
||||
++state.index;
|
||||
continue;
|
||||
}
|
||||
|
||||
QString key = mapObject.begin().key();
|
||||
QJsonObject nextObject;
|
||||
nextObject.insert("key"_L1, key);
|
||||
nextObject.insert("value"_L1, mapObject.take(key));
|
||||
state.obj.insert(*it, mapObject);
|
||||
m_state.push_back({ nextObject });
|
||||
} else if (flags.testFlag(QtProtobufPrivate::FieldFlag::Message)) {
|
||||
if (!val.isObject()) {
|
||||
setInvalidFormatError();
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto nextObject = val.toObject();
|
||||
++state.index;
|
||||
m_state.push_back({ nextObject });
|
||||
} else {
|
||||
state.scalarValue = val;
|
||||
++state.index;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
m_state.pop_back();
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool QProtobufJsonDeserializerImpl::deserializeScalarField(QVariant &value,
|
||||
const QProtobufFieldInfo &)
|
||||
{
|
||||
auto handler = QProtobufJsonSerializerPrivate::handlers.constFind(value.userType());
|
||||
if (handler == QProtobufJsonSerializerPrivate::handlers.constEnd()
|
||||
|| !handler.value().deserializer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
value = handler.value().deserializer(m_state.last().scalarValue, ok);
|
||||
if (!ok)
|
||||
setInvalidFormatError();
|
||||
return true;
|
||||
}
|
||||
|
||||
void QProtobufJsonDeserializerImpl::setUnexpectedEndOfStreamError()
|
||||
{
|
||||
setError(QAbstractProtobufSerializer::Error::UnexpectedEndOfStream,
|
||||
QCoreApplication::translate("QtProtobuf", "JSON: Unexpected end of stream"));
|
||||
}
|
||||
|
||||
void QProtobufJsonDeserializerImpl::setInvalidFormatError()
|
||||
{
|
||||
setError(QAbstractProtobufSerializer::Error::InvalidFormat,
|
||||
QCoreApplication::translate("QtProtobuf",
|
||||
"JSON: One or more fields have invalid format"));
|
||||
}
|
||||
|
||||
QProtobufJsonSerializerPrivate::SerializerRegistry QProtobufJsonSerializerPrivate::handlers = {};
|
||||
|
||||
void QProtobufJsonSerializerPrivate::clearError()
|
||||
|
@ -804,19 +755,22 @@ bool QProtobufJsonSerializer::deserializeMessage(QProtobufMessage *message,
|
|||
QJsonParseError err;
|
||||
auto document = QJsonDocument::fromJson(data.toByteArray(), &err);
|
||||
if (err.error != QJsonParseError::NoError) {
|
||||
d_ptr->setUnexpectedEndOfStreamError();
|
||||
d_ptr->deserializer.setUnexpectedEndOfStreamError();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!document.isObject()) {
|
||||
d_ptr->setInvalidFormatError();
|
||||
d_ptr->deserializer.setInvalidFormatError();
|
||||
return false;
|
||||
}
|
||||
d_ptr->activeValue = document.object();
|
||||
|
||||
d_ptr->cachedPropertyValue.clear();
|
||||
d_ptr->cachedIndex = -1;
|
||||
return d_ptr->deserializeObject(message);
|
||||
if (auto obj = document.object(); !obj.isEmpty()) {
|
||||
d_ptr->deserializer.reset(obj);
|
||||
bool result = d_ptr->deserializer.deserializeMessage(message);
|
||||
d_ptr->deserializer.reset({});
|
||||
return result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
@ -287,6 +287,236 @@ QByteArray QProtobufSerializerImpl::encodeHeader(int fieldIndex, QtProtobuf::Wir
|
|||
return QProtobufSerializerPrivate::serializeVarintCommon<uint32_t>(header);
|
||||
}
|
||||
|
||||
QProtobufDeserializerImpl::QProtobufDeserializerImpl(QProtobufSerializerPrivate *parent)
|
||||
: m_parent(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QProtobufDeserializerImpl::~QProtobufDeserializerImpl()
|
||||
= default;
|
||||
|
||||
void QProtobufDeserializerImpl::reset(QByteArrayView data)
|
||||
{
|
||||
m_it = QProtobufSelfcheckIterator::fromView(data);
|
||||
m_state.push_back(data.end());
|
||||
clearCachedValue();
|
||||
}
|
||||
|
||||
void QProtobufDeserializerImpl::setError(QAbstractProtobufSerializer::Error error,
|
||||
QAnyStringView errorString)
|
||||
{
|
||||
m_parent->lastError = error;
|
||||
m_parent->lastErrorString = errorString.toString();
|
||||
}
|
||||
|
||||
bool QProtobufDeserializerImpl::deserializeEnum(QVariant &value,
|
||||
const QProtobufFieldInfo &fieldInfo)
|
||||
{
|
||||
const auto fieldFlags = fieldInfo.fieldFlags();
|
||||
if (fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::Repeated)) {
|
||||
QMetaType metaType = value.metaType();
|
||||
value.convert(QMetaType::fromType<QList<QtProtobuf::int64>>());
|
||||
bool result = false;
|
||||
if (m_wireType == QtProtobuf::WireTypes::Varint) {
|
||||
result = QProtobufSerializerPrivate::deserializeNonPackedList<QtProtobuf::int64>(m_it,
|
||||
value);
|
||||
} else if (m_wireType == QtProtobuf::WireTypes::LengthDelimited) {
|
||||
result = QProtobufSerializerPrivate::deserializeList<QtProtobuf::int64>(m_it, value);
|
||||
}
|
||||
value.convert(metaType);
|
||||
return result;
|
||||
}
|
||||
|
||||
return QProtobufSerializerPrivate::deserializeBasic<QtProtobuf::int64>(m_it, value);
|
||||
}
|
||||
|
||||
int QProtobufDeserializerImpl::nextFieldIndex(QProtobufMessage *message)
|
||||
{
|
||||
Q_ASSERT(message);
|
||||
|
||||
const auto *ordering = message->propertyOrdering();
|
||||
Q_ASSERT(ordering != nullptr);
|
||||
|
||||
while (m_it.isValid() && m_it != m_state.last()) {
|
||||
// Each iteration we expect iterator is setup to beginning of next chunk
|
||||
int fieldNumber = QtProtobuf::InvalidFieldNumber;
|
||||
const QProtobufSelfcheckIterator fieldBegin = m_it; // copy this, we may need it later
|
||||
if (!decodeHeader(m_it, fieldNumber, m_wireType)) {
|
||||
setError(QAbstractProtobufSerializer::Error::InvalidHeader,
|
||||
"Message received doesn't contain valid header byte.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int index = ordering->indexOfFieldNumber(fieldNumber);
|
||||
if (index == -1) {
|
||||
// This is an unknown field, it may have been added in a later revision
|
||||
// of the Message we are currently deserializing. We must store the
|
||||
// bytes for this field and re-emit them later if this message is
|
||||
// serialized again.
|
||||
if (auto length = skipField(fieldBegin); length < 0) {
|
||||
return -1;
|
||||
} else if (length > 0 && m_parent->preserveUnknownFields) {
|
||||
QByteArrayView fieldData(fieldBegin.data(), length);
|
||||
QProtobufMessagePrivate::storeUnknownEntry(message, fieldData, fieldNumber);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ordering->fieldFlags(index).testAnyFlags({ QtProtobufPrivate::FieldFlag::Message,
|
||||
QtProtobufPrivate::FieldFlag::Map })) {
|
||||
auto
|
||||
opt = QProtobufSerializerPrivate::deserializeVarintCommon<QtProtobuf::uint64>(m_it);
|
||||
if (!opt) {
|
||||
setUnexpectedEndOfStreamError();
|
||||
return -1;
|
||||
}
|
||||
|
||||
quint64 length = *opt;
|
||||
if (!m_it.isValid() || quint64(m_it.bytesLeft()) < length
|
||||
|| length > quint64(QByteArray::maxSize())) {
|
||||
setUnexpectedEndOfStreamError();
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_state.push_back(m_it.data() + length);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
if (!m_it.isValid())
|
||||
setUnexpectedEndOfStreamError();
|
||||
|
||||
m_state.pop_back();
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool QProtobufDeserializerImpl::deserializeScalarField(QVariant &value,
|
||||
const QtProtobufPrivate::QProtobufFieldInfo
|
||||
&fieldInfo)
|
||||
{
|
||||
QMetaType metaType = value.metaType();
|
||||
bool isNonPacked = fieldInfo.fieldFlags().testFlag(QtProtobufPrivate::FieldFlag::NonPacked);
|
||||
auto basicHandler = findIntegratedTypeHandler(metaType, isNonPacked);
|
||||
|
||||
if (!basicHandler)
|
||||
return false;
|
||||
|
||||
if (basicHandler->wireType != m_wireType) {
|
||||
// If the handler wiretype mismatches the wiretype received from the
|
||||
// wire that most probably means that we received the list in wrong
|
||||
// format. This can happen because of mismatch of the field packed
|
||||
// option in the protobuf schema on the wire ends. Invert the
|
||||
// isNonPacked flag and try to find the handler one more time to make
|
||||
// sure that we cover this exceptional case.
|
||||
// See the conformance tests
|
||||
// Required.Proto3.ProtobufInput.ValidDataRepeated.*.UnpackedInput
|
||||
// for details.
|
||||
basicHandler = findIntegratedTypeHandler(metaType, !isNonPacked);
|
||||
if (!basicHandler || basicHandler->wireType != m_wireType) {
|
||||
setError(QAbstractProtobufSerializer::Error::InvalidHeader,
|
||||
QCoreApplication::translate("QtProtobuf",
|
||||
"Invalid wiretype for the %1 "
|
||||
"field number %1. Expected %2, received %3")
|
||||
.arg(QString::fromUtf8(metaType.name()))
|
||||
.arg(fieldInfo.fieldNumber())
|
||||
.arg(basicHandler ? static_cast<int>(basicHandler->wireType) : -1)
|
||||
.arg(static_cast<int>(m_wireType)));
|
||||
value.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!basicHandler->deserializer(m_it, value)) {
|
||||
value.clear();
|
||||
setUnexpectedEndOfStreamError();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
qsizetype QProtobufDeserializerImpl::skipField(const QProtobufSelfcheckIterator &fieldBegin)
|
||||
{
|
||||
switch (m_wireType) {
|
||||
case QtProtobuf::WireTypes::Varint:
|
||||
skipVarint();
|
||||
break;
|
||||
case QtProtobuf::WireTypes::Fixed32:
|
||||
m_it += sizeof(decltype(QtProtobuf::fixed32::t));
|
||||
break;
|
||||
case QtProtobuf::WireTypes::Fixed64:
|
||||
m_it += sizeof(decltype(QtProtobuf::fixed64::t));
|
||||
break;
|
||||
case QtProtobuf::WireTypes::LengthDelimited:
|
||||
skipLengthDelimited();
|
||||
break;
|
||||
case QtProtobuf::WireTypes::Unknown:
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!m_it.isValid()) {
|
||||
setUnexpectedEndOfStreamError();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return std::distance(fieldBegin, m_it);
|
||||
}
|
||||
|
||||
void QProtobufDeserializerImpl::skipVarint()
|
||||
{
|
||||
while ((*m_it) & 0x80)
|
||||
++m_it;
|
||||
++m_it;
|
||||
}
|
||||
|
||||
void QProtobufDeserializerImpl::skipLengthDelimited()
|
||||
{
|
||||
//Get length of length-delimited field
|
||||
auto opt = QProtobufSerializerPrivate::deserializeVarintCommon<QtProtobuf::uint64>(m_it);
|
||||
if (!opt) {
|
||||
m_it += m_it.bytesLeft() + 1;
|
||||
return;
|
||||
}
|
||||
QtProtobuf::uint64 length = opt.value();
|
||||
m_it += length;
|
||||
}
|
||||
|
||||
void QProtobufDeserializerImpl::setUnexpectedEndOfStreamError()
|
||||
{
|
||||
setError(QAbstractProtobufSerializer::Error::UnexpectedEndOfStream,
|
||||
QCoreApplication::translate("QtProtobuf", "Unexpected end of stream"));
|
||||
}
|
||||
|
||||
bool QProtobufDeserializerImpl::decodeHeader(QProtobufSelfcheckIterator &it, int &fieldIndex,
|
||||
QtProtobuf::WireTypes &wireType)
|
||||
{
|
||||
if (it.bytesLeft() == 0)
|
||||
return false;
|
||||
auto opt = QProtobufSerializerPrivate::deserializeVarintCommon<uint32_t>(it);
|
||||
if (!opt)
|
||||
return false;
|
||||
uint32_t header = opt.value();
|
||||
wireType = static_cast<QtProtobuf::WireTypes>(header & 0b00000111);
|
||||
fieldIndex = header >> 3;
|
||||
|
||||
constexpr int maxFieldIndex = (1 << 29) - 1;
|
||||
return fieldIndex <= maxFieldIndex && fieldIndex > 0
|
||||
&& (wireType == QtProtobuf::WireTypes::Varint || wireType == QtProtobuf::WireTypes::Fixed64
|
||||
|| wireType == QtProtobuf::WireTypes::Fixed32
|
||||
|| wireType == QtProtobuf::WireTypes::LengthDelimited);
|
||||
}
|
||||
|
||||
QProtobufSerializerPrivate::QProtobufSerializerPrivate() : deserializer(this)
|
||||
{
|
||||
}
|
||||
|
||||
void QProtobufSerializerPrivate::clearError()
|
||||
{
|
||||
lastError = QAbstractProtobufSerializer::Error::None;
|
||||
lastErrorString.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a new serializer instance.
|
||||
*/
|
||||
|
@ -310,327 +540,13 @@ QByteArray QProtobufSerializer::serializeMessage(const QProtobufMessage *message
|
|||
return result;
|
||||
}
|
||||
|
||||
void QProtobufSerializerPrivate::setUnexpectedEndOfStreamError()
|
||||
{
|
||||
setDeserializationError(QAbstractProtobufSerializer::Error::UnexpectedEndOfStream,
|
||||
QCoreApplication::translate("QtProtobuf", "Unexpected end of stream"));
|
||||
}
|
||||
|
||||
void QProtobufSerializerPrivate::clearError()
|
||||
{
|
||||
lastError = QAbstractProtobufSerializer::Error::None;
|
||||
lastErrorString.clear();
|
||||
}
|
||||
|
||||
bool QProtobufSerializer::deserializeMessage(QProtobufMessage *message, QByteArrayView data) const
|
||||
{
|
||||
d_ptr->clearCachedValue();
|
||||
d_ptr->clearError();
|
||||
d_ptr->it = QProtobufSelfcheckIterator::fromView(data);
|
||||
|
||||
bool ok = true;
|
||||
while (d_ptr->it.isValid() && d_ptr->it != data.end()) {
|
||||
if (!d_ptr->deserializeProperty(message)) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!d_ptr->storeCachedValue(message) || !ok)
|
||||
return false;
|
||||
|
||||
if (!d_ptr->it.isValid())
|
||||
d_ptr->setUnexpectedEndOfStreamError();
|
||||
return d_ptr->it.isValid();
|
||||
}
|
||||
|
||||
bool QProtobufSerializerPrivate::deserializeObject(QProtobufMessage *message)
|
||||
{
|
||||
if (it.bytesLeft() == 0) {
|
||||
setUnexpectedEndOfStreamError();
|
||||
return false;
|
||||
}
|
||||
std::optional<QByteArray>
|
||||
array = QProtobufSerializerPrivate::deserializeLengthDelimited(it);
|
||||
if (!array) {
|
||||
setUnexpectedEndOfStreamError();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto prevCachedRepeatedIterator = std::move(cachedRepeatedIterator);
|
||||
auto restoreOnReturn = qScopeGuard([prevIt = it, prevCachedPropertyValue = cachedPropertyValue,
|
||||
prevCachedIndex = cachedIndex, &prevCachedRepeatedIterator,
|
||||
this]() {
|
||||
it = prevIt;
|
||||
cachedPropertyValue = prevCachedPropertyValue;
|
||||
cachedIndex = prevCachedIndex;
|
||||
cachedRepeatedIterator = std::move(prevCachedRepeatedIterator);
|
||||
});
|
||||
clearCachedValue();
|
||||
|
||||
QByteArrayView data = *array;
|
||||
clearError();
|
||||
it = QProtobufSelfcheckIterator::fromView(data);
|
||||
bool ok = true;
|
||||
while (it.isValid() && it != data.end()) {
|
||||
if (!deserializeProperty(message)) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!storeCachedValue(message) || !ok)
|
||||
return false;
|
||||
|
||||
if (!it.isValid())
|
||||
setUnexpectedEndOfStreamError();
|
||||
return it.isValid();
|
||||
}
|
||||
|
||||
bool QProtobufSerializerPrivate::deserializeEnumList(QList<QtProtobuf::int64> &value)
|
||||
{
|
||||
QVariant variantValue;
|
||||
if (!QProtobufSerializerPrivate::deserializeList<QtProtobuf::int64>(it, variantValue)) {
|
||||
setUnexpectedEndOfStreamError();
|
||||
return false;
|
||||
}
|
||||
value = variantValue.value<QList<QtProtobuf::int64>>();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Decode a property field index and its serialization type from input bytes
|
||||
|
||||
Iterator: that points to header with encoded field index and serialization type
|
||||
fieldIndex: Decoded index of a property in parent object
|
||||
wireType: Decoded serialization type used for the property with index
|
||||
Return true if both decoded wireType and fieldIndex have "allowed" values and false, otherwise
|
||||
*/
|
||||
bool QProtobufSerializerPrivate::decodeHeader(QProtobufSelfcheckIterator &it,
|
||||
int &fieldIndex,
|
||||
QtProtobuf::WireTypes &wireType)
|
||||
{
|
||||
if (it.bytesLeft() == 0)
|
||||
return false;
|
||||
auto opt = deserializeVarintCommon<uint32_t>(it);
|
||||
if (!opt)
|
||||
return false;
|
||||
uint32_t header = opt.value();
|
||||
wireType = static_cast<QtProtobuf::WireTypes>(header & 0b00000111);
|
||||
fieldIndex = header >> 3;
|
||||
|
||||
constexpr int maxFieldIndex = (1 << 29) - 1;
|
||||
return fieldIndex <= maxFieldIndex && fieldIndex > 0
|
||||
&& (wireType == QtProtobuf::WireTypes::Varint
|
||||
|| wireType == QtProtobuf::WireTypes::Fixed64
|
||||
|| wireType == QtProtobuf::WireTypes::Fixed32
|
||||
|| wireType == QtProtobuf::WireTypes::LengthDelimited);
|
||||
}
|
||||
|
||||
void QProtobufSerializerPrivate::skipVarint(QProtobufSelfcheckIterator &it)
|
||||
{
|
||||
while ((*it) & 0x80)
|
||||
++it;
|
||||
++it;
|
||||
}
|
||||
|
||||
void QProtobufSerializerPrivate::skipLengthDelimited(QProtobufSelfcheckIterator &it)
|
||||
{
|
||||
//Get length of length-delimited field
|
||||
auto opt = QProtobufSerializerPrivate::deserializeVarintCommon<QtProtobuf::uint64>(it);
|
||||
if (!opt) {
|
||||
it += it.bytesLeft() + 1;
|
||||
return;
|
||||
}
|
||||
QtProtobuf::uint64 length = opt.value();
|
||||
it += length;
|
||||
}
|
||||
|
||||
qsizetype QProtobufSerializerPrivate::skipSerializedFieldBytes(QProtobufSelfcheckIterator &it, QtProtobuf::WireTypes type)
|
||||
{
|
||||
const auto *initialIt = QByteArray::const_iterator(it);
|
||||
switch (type) {
|
||||
case QtProtobuf::WireTypes::Varint:
|
||||
skipVarint(it);
|
||||
break;
|
||||
case QtProtobuf::WireTypes::Fixed32:
|
||||
it += sizeof(decltype(QtProtobuf::fixed32::t));
|
||||
break;
|
||||
case QtProtobuf::WireTypes::Fixed64:
|
||||
it += sizeof(decltype(QtProtobuf::fixed64::t));
|
||||
break;
|
||||
case QtProtobuf::WireTypes::LengthDelimited:
|
||||
skipLengthDelimited(it);
|
||||
break;
|
||||
case QtProtobuf::WireTypes::Unknown:
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return std::distance(initialIt, QByteArray::const_iterator(it));
|
||||
}
|
||||
|
||||
bool QProtobufSerializerPrivate::deserializeProperty(QProtobufMessage *message)
|
||||
{
|
||||
Q_ASSERT(message != nullptr);
|
||||
Q_ASSERT(it.isValid() && it.bytesLeft() > 0);
|
||||
//Each iteration we expect iterator is setup to beginning of next chunk
|
||||
int fieldNumber = QtProtobuf::InvalidFieldNumber;
|
||||
QtProtobuf::WireTypes wireType = QtProtobuf::WireTypes::Unknown;
|
||||
const QProtobufSelfcheckIterator itBeforeHeader = it; // copy this, we may need it later
|
||||
if (!QProtobufSerializerPrivate::decodeHeader(it, fieldNumber, wireType)) {
|
||||
setDeserializationError(
|
||||
QAbstractProtobufSerializer::Error::InvalidHeader,
|
||||
QCoreApplication::translate("QtProtobuf",
|
||||
"Message received doesn't contain valid header byte."));
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ordering = message->propertyOrdering();
|
||||
Q_ASSERT(ordering != nullptr);
|
||||
|
||||
int index = ordering->indexOfFieldNumber(fieldNumber);
|
||||
if (index == -1) {
|
||||
// This is an unknown field, it may have been added in a later revision
|
||||
// of the Message we are currently deserializing. We must store the
|
||||
// bytes for this field and re-emit them later if this message is
|
||||
// serialized again.
|
||||
qsizetype length = std::distance(itBeforeHeader, it); // size of header
|
||||
length += QProtobufSerializerPrivate::skipSerializedFieldBytes(it, wireType);
|
||||
|
||||
if (!it.isValid()) {
|
||||
setUnexpectedEndOfStreamError();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preserveUnknownFields) {
|
||||
QProtobufMessagePrivate::storeUnknownEntry(message,
|
||||
QByteArrayView(itBeforeHeader.data(),
|
||||
length),
|
||||
fieldNumber);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QProtobufFieldInfo fieldInfo(*ordering, index);
|
||||
if (cachedIndex != index) {
|
||||
if (!storeCachedValue(message))
|
||||
return false;
|
||||
|
||||
cachedPropertyValue = QtProtobufSerializerHelpers::messageProperty(message, fieldInfo,
|
||||
true);
|
||||
cachedIndex = index;
|
||||
}
|
||||
QMetaType metaType = cachedPropertyValue.metaType();
|
||||
|
||||
qProtoDebug() << "wireType:" << wireType << "metaType:" << metaType.name()
|
||||
<< "currentByte:" << QString::number((*it), 16);
|
||||
|
||||
if (metaType.flags() & QMetaType::IsPointer) {
|
||||
auto *messageProperty = cachedPropertyValue.value<QProtobufMessage *>();
|
||||
Q_ASSERT(messageProperty != nullptr);
|
||||
return deserializeObject(messageProperty);
|
||||
}
|
||||
|
||||
const auto fieldFlags = fieldInfo.fieldFlags();
|
||||
if (fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::Enum)) {
|
||||
if (fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::Repeated)) {
|
||||
auto intList = cachedPropertyValue.value<QList<QtProtobuf::int64>>();
|
||||
if (deserializeEnumList(intList)) {
|
||||
cachedPropertyValue.setValue(intList);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
if (deserializeBasic<QtProtobuf::int64>(it, cachedPropertyValue))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isNonPacked = fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::NonPacked);
|
||||
auto basicHandler = findIntegratedTypeHandler(metaType, isNonPacked);
|
||||
|
||||
if (basicHandler) {
|
||||
if (basicHandler->wireType != wireType) {
|
||||
// If the handler wiretype mismatches the wiretype received from the
|
||||
// wire that most probably means that we received the list in wrong
|
||||
// format. This can happen because of mismatch of the field packed
|
||||
// option in the protobuf schema on the wire ends. Invert the
|
||||
// isNonPacked flag and try to find the handler one more time to make
|
||||
// sure that we cover this exceptional case.
|
||||
// See the conformance tests
|
||||
// Required.Proto3.ProtobufInput.ValidDataRepeated.*.UnpackedInput
|
||||
// for details.
|
||||
basicHandler = findIntegratedTypeHandler(metaType, !isNonPacked);
|
||||
if (!basicHandler || basicHandler->wireType != wireType) {
|
||||
setDeserializationError(
|
||||
QAbstractProtobufSerializer::Error::InvalidHeader,
|
||||
QCoreApplication::translate("QtProtobuf",
|
||||
"Message received has invalid wiretype for the "
|
||||
"field number %1. Expected %2, received %3")
|
||||
.arg(fieldNumber)
|
||||
.arg(basicHandler ? static_cast<int>(basicHandler->wireType) : -1)
|
||||
.arg(static_cast<int>(wireType)));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!basicHandler->deserializer(it, cachedPropertyValue)) {
|
||||
setUnexpectedEndOfStreamError();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cachedPropertyValue.canView(QMetaType::fromType<QProtobufRepeatedIterator>())) {
|
||||
if (!cachedRepeatedIterator.isValid()) {
|
||||
cachedRepeatedIterator = cachedPropertyValue.view<QProtobufRepeatedIterator>();
|
||||
}
|
||||
if (deserializeObject(cachedRepeatedIterator.addNext())) {
|
||||
cachedRepeatedIterator.push();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto handler = QtProtobufPrivate::findHandler(metaType);
|
||||
if (!handler.deserializer) {
|
||||
qProtoWarning() << "No deserializer for type" << metaType.name();
|
||||
QString error = QString::fromUtf8("No deserializer is registered for type %1")
|
||||
.arg(QString::fromUtf8(metaType.name()));
|
||||
setDeserializationError(QAbstractProtobufSerializer::Error::UnknownType,
|
||||
QCoreApplication::translate("QtProtobuf", error.toUtf8().data()));
|
||||
return false;
|
||||
}
|
||||
handler.deserializer([this](QProtobufMessage
|
||||
*message) { return this->deserializeObject(message); },
|
||||
cachedPropertyValue.data());
|
||||
|
||||
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 = QtProtobufSerializerHelpers::setMessageProperty(message, fieldInfo,
|
||||
cachedPropertyValue);
|
||||
|
||||
clearCachedValue();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
void QProtobufSerializerPrivate::clearCachedValue()
|
||||
{
|
||||
cachedPropertyValue.clear();
|
||||
cachedIndex = -1;
|
||||
cachedRepeatedIterator = QProtobufRepeatedIterator();
|
||||
d_ptr->deserializer.reset(data);
|
||||
d_ptr->deserializer.deserializeMessage(message);
|
||||
d_ptr->deserializer.reset({});
|
||||
return d_ptr->lastError == QAbstractProtobufSerializer::Error::None;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -651,13 +567,6 @@ QString QProtobufSerializer::lastErrorString() const
|
|||
return d_ptr->lastErrorString;
|
||||
}
|
||||
|
||||
void QProtobufSerializerPrivate::setDeserializationError(
|
||||
QAbstractProtobufSerializer::Error error, const QString &errorString)
|
||||
{
|
||||
lastError = error;
|
||||
lastErrorString = errorString;
|
||||
}
|
||||
|
||||
/*!
|
||||
Controls whether the unknown fields received from the wire should be
|
||||
stored in the resulting message or if it should be omitted, based
|
||||
|
|
|
@ -23,9 +23,10 @@
|
|||
#include <QtProtobuf/qtprotobuftypes.h>
|
||||
|
||||
#include <QtProtobuf/private/protobuffieldpresencechecker_p.h>
|
||||
#include <QtProtobuf/private/qtprotobuflogging_p.h>
|
||||
#include <QtProtobuf/private/qprotobufdeserializerbase_p.h>
|
||||
#include <QtProtobuf/private/qprotobufselfcheckiterator_p.h>
|
||||
#include <QtProtobuf/private/qprotobufserializerbase_p.h>
|
||||
#include <QtProtobuf/private/qtprotobuflogging_p.h>
|
||||
|
||||
#include <QtCore/qendian.h>
|
||||
#include <QtCore/qstring.h>
|
||||
|
@ -68,6 +69,40 @@ private:
|
|||
Q_DISABLE_COPY_MOVE(QProtobufSerializerImpl)
|
||||
};
|
||||
|
||||
class QProtobufDeserializerImpl final : public QProtobufDeserializerBase
|
||||
{
|
||||
public:
|
||||
explicit QProtobufDeserializerImpl(QProtobufSerializerPrivate *parent);
|
||||
~QProtobufDeserializerImpl();
|
||||
|
||||
void reset(QByteArrayView data);
|
||||
|
||||
QProtobufSelfcheckIterator m_it;
|
||||
|
||||
private:
|
||||
void setError(QAbstractProtobufSerializer::Error error, QAnyStringView errorString) override;
|
||||
bool deserializeEnum(QVariant &value,
|
||||
const QtProtobufPrivate::QProtobufFieldInfo &fieldInfo) override;
|
||||
int nextFieldIndex(QProtobufMessage *message) override;
|
||||
bool deserializeScalarField(QVariant &value,
|
||||
const QtProtobufPrivate::QProtobufFieldInfo &fieldInfo) override;
|
||||
|
||||
qsizetype skipField(const QProtobufSelfcheckIterator &fieldBegin);
|
||||
void skipVarint();
|
||||
void skipLengthDelimited();
|
||||
void setUnexpectedEndOfStreamError();
|
||||
|
||||
[[nodiscard]]
|
||||
static bool decodeHeader(QProtobufSelfcheckIterator &it, int &fieldIndex,
|
||||
QtProtobuf::WireTypes &wireType);
|
||||
|
||||
QtProtobuf::WireTypes m_wireType = QtProtobuf::WireTypes::Unknown;
|
||||
QList<QByteArrayView::iterator> m_state;
|
||||
QProtobufSerializerPrivate *m_parent = nullptr;
|
||||
|
||||
Q_DISABLE_COPY_MOVE(QProtobufDeserializerImpl)
|
||||
};
|
||||
|
||||
class QProtobufSerializerPrivate
|
||||
{
|
||||
// The below type trait structures help to determine the required encoding method for protobuf
|
||||
|
@ -175,7 +210,7 @@ public:
|
|||
QtProtobuf::WireTypes wireType; // Serialization WireType
|
||||
};
|
||||
|
||||
QProtobufSerializerPrivate() = default;
|
||||
QProtobufSerializerPrivate();
|
||||
~QProtobufSerializerPrivate() = default;
|
||||
// ###########################################################################
|
||||
// Serializers
|
||||
|
@ -487,10 +522,6 @@ public:
|
|||
return { result };
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static bool decodeHeader(QProtobufSelfcheckIterator &it, int &fieldIndex,
|
||||
QtProtobuf::WireTypes &wireType);
|
||||
|
||||
/*!
|
||||
Gets length of a byte-array and prepends to it its serialized length value
|
||||
using the appropriate serialization algorithm
|
||||
|
@ -515,40 +546,16 @@ public:
|
|||
return s(variantValue.value<T>(), header);
|
||||
}
|
||||
|
||||
// this set of 3 methods is used to skip bytes corresponding to an unexpected property
|
||||
// in a serialized message met while the message being deserialized
|
||||
static qsizetype skipSerializedFieldBytes(QProtobufSelfcheckIterator &it,
|
||||
QtProtobuf::WireTypes type);
|
||||
static void skipVarint(QProtobufSelfcheckIterator &it);
|
||||
static void skipLengthDelimited(QProtobufSelfcheckIterator &it);
|
||||
|
||||
bool deserializeObject(QProtobufMessage *message);
|
||||
|
||||
bool deserializeEnumList(QList<QtProtobuf::int64> &value);
|
||||
|
||||
[[nodiscard]] bool deserializeProperty(QProtobufMessage *message);
|
||||
|
||||
void setDeserializationError(QAbstractProtobufSerializer::Error error,
|
||||
const QString &errorString);
|
||||
void clearError();
|
||||
void setUnexpectedEndOfStreamError();
|
||||
|
||||
[[nodiscard]] bool storeCachedValue(QProtobufMessage *message);
|
||||
void clearCachedValue();
|
||||
|
||||
QAbstractProtobufSerializer::Error lastError =
|
||||
QAbstractProtobufSerializer::Error::UnknownType;
|
||||
QString lastErrorString;
|
||||
|
||||
QProtobufSelfcheckIterator it;
|
||||
|
||||
bool preserveUnknownFields = true;
|
||||
|
||||
QVariant cachedPropertyValue;
|
||||
QProtobufRepeatedIterator cachedRepeatedIterator;
|
||||
int cachedIndex = -1;
|
||||
|
||||
QProtobufSerializerImpl serializer;
|
||||
QProtobufDeserializerImpl deserializer;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY_MOVE(QProtobufSerializerPrivate)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
Required.Proto3.ProtobufInput.ValidDataRepeated.ENUM.UnpackedInput.ProtobufOutput
|
||||
Required.Proto3.JsonInput.Any.JsonOutput
|
||||
Required.Proto3.JsonInput.Any.ProtobufOutput
|
||||
Required.Proto3.JsonInput.AnyNested.JsonOutput
|
||||
|
@ -105,7 +104,6 @@ Required.Proto3.JsonInput.ValueAcceptObject.JsonOutput
|
|||
Required.Proto3.JsonInput.ValueAcceptObject.ProtobufOutput
|
||||
Required.Proto3.JsonInput.ValueAcceptString.JsonOutput
|
||||
Required.Proto3.JsonInput.ValueAcceptString.ProtobufOutput
|
||||
Required.Proto3.ProtobufInput.ValidDataRepeated.ENUM.UnpackedInput.JsonOutput
|
||||
Required.DurationProtoInputTooLarge.JsonOutput
|
||||
Required.DurationProtoInputTooSmall.JsonOutput
|
||||
Required.TimestampProtoInputTooLarge.JsonOutput
|
||||
|
|
|
@ -260,14 +260,14 @@ void QtProtobufRepeatedTypesJsonDeserializationTest::invalidTypeTest()
|
|||
// expected sint, string is used
|
||||
RepeatedSInt64Message test;
|
||||
test.deserialize(serializer.get(), "{\"testRepeatedInt\":[1,321,\"abcd\",12324523123123,-3,3]}"_ba);
|
||||
QVERIFY(test.testRepeatedInt().size() == 0);
|
||||
QCOMPARE(test.testRepeatedInt().size(), 2);
|
||||
QCOMPARE(serializer->lastError(), QAbstractProtobufSerializer::Error::InvalidFormat);
|
||||
|
||||
// expected bool, float is used
|
||||
RepeatedBoolMessage boolMsg;
|
||||
boolMsg.deserialize(serializer.get(),
|
||||
"{\"testRepeatedBool\":[true,true,true,7.8,false,false,false,false,false,false,false,false,true]}"_ba);
|
||||
QVERIFY(test.testRepeatedInt().size() == 0);
|
||||
QCOMPARE(boolMsg.testRepeatedBool().size(), 3);
|
||||
QCOMPARE(serializer->lastError(), QAbstractProtobufSerializer::Error::InvalidFormat);
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue