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
|
protobuffieldpresencechecker_p.h
|
||||||
qtprotobufglobal.h
|
qtprotobufglobal.h
|
||||||
qabstractprotobufserializer.cpp qabstractprotobufserializer.h
|
qabstractprotobufserializer.cpp qabstractprotobufserializer.h
|
||||||
|
qprotobufdeserializerbase_p.h qprotobufdeserializerbase.cpp
|
||||||
qprotobufjsonserializer.cpp qprotobufjsonserializer.h
|
qprotobufjsonserializer.cpp qprotobufjsonserializer.h
|
||||||
qprotobuflazymessagepointer.h
|
qprotobuflazymessagepointer.h
|
||||||
qprotobufmessage.cpp qprotobufmessage.h qprotobufmessage_p.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/qprotobufjsonserializer.h>
|
||||||
|
|
||||||
#include <QtProtobuf/private/protobuffieldpresencechecker_p.h>
|
#include <QtProtobuf/private/protobuffieldpresencechecker_p.h>
|
||||||
|
#include <QtProtobuf/private/qprotobufdeserializerbase_p.h>
|
||||||
#include <QtProtobuf/private/qprotobufregistration_p.h>
|
#include <QtProtobuf/private/qprotobufregistration_p.h>
|
||||||
#include <QtProtobuf/private/qprotobufserializerbase_p.h>
|
#include <QtProtobuf/private/qprotobufserializerbase_p.h>
|
||||||
#include <QtProtobuf/private/qtprotobufdefs_p.h>
|
#include <QtProtobuf/private/qtprotobufdefs_p.h>
|
||||||
|
@ -19,7 +20,6 @@
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <map>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
@ -85,6 +85,38 @@ private:
|
||||||
Q_DISABLE_COPY_MOVE(QProtobufJsonSerializerImpl)
|
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
|
class QProtobufJsonSerializerPrivate final
|
||||||
{
|
{
|
||||||
Q_DISABLE_COPY_MOVE(QProtobufJsonSerializerPrivate)
|
Q_DISABLE_COPY_MOVE(QProtobufJsonSerializerPrivate)
|
||||||
|
@ -211,7 +243,7 @@ public:
|
||||||
return QJsonValue(arr);
|
return QJsonValue(arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
QProtobufJsonSerializerPrivate()
|
QProtobufJsonSerializerPrivate() : deserializer(this)
|
||||||
{
|
{
|
||||||
[[maybe_unused]] static bool initialized = []() -> bool {
|
[[maybe_unused]] static bool initialized = []() -> bool {
|
||||||
handlers[qMetaTypeId<QtProtobuf::int32>()] = createCommonHandler<QtProtobuf::int32>();
|
handlers[qMetaTypeId<QtProtobuf::int32>()] = createCommonHandler<QtProtobuf::int32>();
|
||||||
|
@ -274,17 +306,6 @@ public:
|
||||||
}
|
}
|
||||||
~QProtobufJsonSerializerPrivate() = default;
|
~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>
|
template <typename T>
|
||||||
static QVariant deserializeCommon(const QJsonValue &value, bool &ok)
|
static QVariant deserializeCommon(const QJsonValue &value, bool &ok)
|
||||||
{
|
{
|
||||||
|
@ -427,236 +448,15 @@ public:
|
||||||
return QVariant::fromValue(list);
|
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();
|
void clearError();
|
||||||
|
|
||||||
QAbstractProtobufSerializer::Error lastError = QAbstractProtobufSerializer::Error::None;
|
QAbstractProtobufSerializer::Error lastError = QAbstractProtobufSerializer::Error::None;
|
||||||
QString lastErrorString;
|
QString lastErrorString;
|
||||||
QJsonValue activeValue;
|
|
||||||
|
|
||||||
static SerializerRegistry handlers;
|
static SerializerRegistry handlers;
|
||||||
|
|
||||||
[[nodiscard]] bool storeCachedValue(QProtobufMessage *message);
|
|
||||||
|
|
||||||
QVariant cachedPropertyValue;
|
|
||||||
int cachedIndex = -1;
|
|
||||||
|
|
||||||
QProtobufJsonSerializerImpl serializer;
|
QProtobufJsonSerializerImpl serializer;
|
||||||
|
QProtobufJsonDeserializerImpl deserializer;
|
||||||
};
|
};
|
||||||
|
|
||||||
QProtobufJsonSerializerImpl::~QProtobufJsonSerializerImpl() = default;
|
QProtobufJsonSerializerImpl::~QProtobufJsonSerializerImpl() = default;
|
||||||
|
@ -741,20 +541,171 @@ void QProtobufJsonSerializerImpl::serializeMessageFieldEnd(const QProtobufMessag
|
||||||
m_result = store;
|
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();
|
QProtobufJsonDeserializerImpl::~QProtobufJsonDeserializerImpl()
|
||||||
QProtobufFieldInfo fieldInfo(*ordering, cachedIndex);
|
= default;
|
||||||
ok = QtProtobufSerializerHelpers::setMessageProperty(message, fieldInfo,
|
|
||||||
cachedPropertyValue);
|
void QProtobufJsonDeserializerImpl::reset(QJsonObject obj)
|
||||||
cachedPropertyValue.clear();
|
{
|
||||||
cachedIndex = -1;
|
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;
|
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 = {};
|
QProtobufJsonSerializerPrivate::SerializerRegistry QProtobufJsonSerializerPrivate::handlers = {};
|
||||||
|
|
||||||
void QProtobufJsonSerializerPrivate::clearError()
|
void QProtobufJsonSerializerPrivate::clearError()
|
||||||
|
@ -804,19 +755,22 @@ bool QProtobufJsonSerializer::deserializeMessage(QProtobufMessage *message,
|
||||||
QJsonParseError err;
|
QJsonParseError err;
|
||||||
auto document = QJsonDocument::fromJson(data.toByteArray(), &err);
|
auto document = QJsonDocument::fromJson(data.toByteArray(), &err);
|
||||||
if (err.error != QJsonParseError::NoError) {
|
if (err.error != QJsonParseError::NoError) {
|
||||||
d_ptr->setUnexpectedEndOfStreamError();
|
d_ptr->deserializer.setUnexpectedEndOfStreamError();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!document.isObject()) {
|
if (!document.isObject()) {
|
||||||
d_ptr->setInvalidFormatError();
|
d_ptr->deserializer.setInvalidFormatError();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
d_ptr->activeValue = document.object();
|
|
||||||
|
|
||||||
d_ptr->cachedPropertyValue.clear();
|
if (auto obj = document.object(); !obj.isEmpty()) {
|
||||||
d_ptr->cachedIndex = -1;
|
d_ptr->deserializer.reset(obj);
|
||||||
return d_ptr->deserializeObject(message);
|
bool result = d_ptr->deserializer.deserializeMessage(message);
|
||||||
|
d_ptr->deserializer.reset({});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
|
@ -287,6 +287,236 @@ QByteArray QProtobufSerializerImpl::encodeHeader(int fieldIndex, QtProtobuf::Wir
|
||||||
return QProtobufSerializerPrivate::serializeVarintCommon<uint32_t>(header);
|
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.
|
Constructs a new serializer instance.
|
||||||
*/
|
*/
|
||||||
|
@ -310,327 +540,13 @@ QByteArray QProtobufSerializer::serializeMessage(const QProtobufMessage *message
|
||||||
return result;
|
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
|
bool QProtobufSerializer::deserializeMessage(QProtobufMessage *message, QByteArrayView data) const
|
||||||
{
|
{
|
||||||
d_ptr->clearCachedValue();
|
|
||||||
d_ptr->clearError();
|
d_ptr->clearError();
|
||||||
d_ptr->it = QProtobufSelfcheckIterator::fromView(data);
|
d_ptr->deserializer.reset(data);
|
||||||
|
d_ptr->deserializer.deserializeMessage(message);
|
||||||
bool ok = true;
|
d_ptr->deserializer.reset({});
|
||||||
while (d_ptr->it.isValid() && d_ptr->it != data.end()) {
|
return d_ptr->lastError == QAbstractProtobufSerializer::Error::None;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -651,13 +567,6 @@ QString QProtobufSerializer::lastErrorString() const
|
||||||
return d_ptr->lastErrorString;
|
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
|
Controls whether the unknown fields received from the wire should be
|
||||||
stored in the resulting message or if it should be omitted, based
|
stored in the resulting message or if it should be omitted, based
|
||||||
|
|
|
@ -23,9 +23,10 @@
|
||||||
#include <QtProtobuf/qtprotobuftypes.h>
|
#include <QtProtobuf/qtprotobuftypes.h>
|
||||||
|
|
||||||
#include <QtProtobuf/private/protobuffieldpresencechecker_p.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/qprotobufselfcheckiterator_p.h>
|
||||||
#include <QtProtobuf/private/qprotobufserializerbase_p.h>
|
#include <QtProtobuf/private/qprotobufserializerbase_p.h>
|
||||||
#include <QtProtobuf/private/qtprotobuflogging_p.h>
|
|
||||||
|
|
||||||
#include <QtCore/qendian.h>
|
#include <QtCore/qendian.h>
|
||||||
#include <QtCore/qstring.h>
|
#include <QtCore/qstring.h>
|
||||||
|
@ -68,6 +69,40 @@ private:
|
||||||
Q_DISABLE_COPY_MOVE(QProtobufSerializerImpl)
|
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
|
class QProtobufSerializerPrivate
|
||||||
{
|
{
|
||||||
// The below type trait structures help to determine the required encoding method for protobuf
|
// The below type trait structures help to determine the required encoding method for protobuf
|
||||||
|
@ -175,7 +210,7 @@ public:
|
||||||
QtProtobuf::WireTypes wireType; // Serialization WireType
|
QtProtobuf::WireTypes wireType; // Serialization WireType
|
||||||
};
|
};
|
||||||
|
|
||||||
QProtobufSerializerPrivate() = default;
|
QProtobufSerializerPrivate();
|
||||||
~QProtobufSerializerPrivate() = default;
|
~QProtobufSerializerPrivate() = default;
|
||||||
// ###########################################################################
|
// ###########################################################################
|
||||||
// Serializers
|
// Serializers
|
||||||
|
@ -487,10 +522,6 @@ public:
|
||||||
return { result };
|
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
|
Gets length of a byte-array and prepends to it its serialized length value
|
||||||
using the appropriate serialization algorithm
|
using the appropriate serialization algorithm
|
||||||
|
@ -515,40 +546,16 @@ public:
|
||||||
return s(variantValue.value<T>(), header);
|
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 clearError();
|
||||||
void setUnexpectedEndOfStreamError();
|
|
||||||
|
|
||||||
[[nodiscard]] bool storeCachedValue(QProtobufMessage *message);
|
|
||||||
void clearCachedValue();
|
|
||||||
|
|
||||||
QAbstractProtobufSerializer::Error lastError =
|
QAbstractProtobufSerializer::Error lastError =
|
||||||
QAbstractProtobufSerializer::Error::UnknownType;
|
QAbstractProtobufSerializer::Error::UnknownType;
|
||||||
QString lastErrorString;
|
QString lastErrorString;
|
||||||
|
|
||||||
QProtobufSelfcheckIterator it;
|
|
||||||
|
|
||||||
bool preserveUnknownFields = true;
|
bool preserveUnknownFields = true;
|
||||||
|
|
||||||
QVariant cachedPropertyValue;
|
|
||||||
QProtobufRepeatedIterator cachedRepeatedIterator;
|
|
||||||
int cachedIndex = -1;
|
|
||||||
|
|
||||||
QProtobufSerializerImpl serializer;
|
QProtobufSerializerImpl serializer;
|
||||||
|
QProtobufDeserializerImpl deserializer;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_DISABLE_COPY_MOVE(QProtobufSerializerPrivate)
|
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.JsonOutput
|
||||||
Required.Proto3.JsonInput.Any.ProtobufOutput
|
Required.Proto3.JsonInput.Any.ProtobufOutput
|
||||||
Required.Proto3.JsonInput.AnyNested.JsonOutput
|
Required.Proto3.JsonInput.AnyNested.JsonOutput
|
||||||
|
@ -105,7 +104,6 @@ Required.Proto3.JsonInput.ValueAcceptObject.JsonOutput
|
||||||
Required.Proto3.JsonInput.ValueAcceptObject.ProtobufOutput
|
Required.Proto3.JsonInput.ValueAcceptObject.ProtobufOutput
|
||||||
Required.Proto3.JsonInput.ValueAcceptString.JsonOutput
|
Required.Proto3.JsonInput.ValueAcceptString.JsonOutput
|
||||||
Required.Proto3.JsonInput.ValueAcceptString.ProtobufOutput
|
Required.Proto3.JsonInput.ValueAcceptString.ProtobufOutput
|
||||||
Required.Proto3.ProtobufInput.ValidDataRepeated.ENUM.UnpackedInput.JsonOutput
|
|
||||||
Required.DurationProtoInputTooLarge.JsonOutput
|
Required.DurationProtoInputTooLarge.JsonOutput
|
||||||
Required.DurationProtoInputTooSmall.JsonOutput
|
Required.DurationProtoInputTooSmall.JsonOutput
|
||||||
Required.TimestampProtoInputTooLarge.JsonOutput
|
Required.TimestampProtoInputTooLarge.JsonOutput
|
||||||
|
|
|
@ -260,14 +260,14 @@ void QtProtobufRepeatedTypesJsonDeserializationTest::invalidTypeTest()
|
||||||
// expected sint, string is used
|
// expected sint, string is used
|
||||||
RepeatedSInt64Message test;
|
RepeatedSInt64Message test;
|
||||||
test.deserialize(serializer.get(), "{\"testRepeatedInt\":[1,321,\"abcd\",12324523123123,-3,3]}"_ba);
|
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);
|
QCOMPARE(serializer->lastError(), QAbstractProtobufSerializer::Error::InvalidFormat);
|
||||||
|
|
||||||
// expected bool, float is used
|
// expected bool, float is used
|
||||||
RepeatedBoolMessage boolMsg;
|
RepeatedBoolMessage boolMsg;
|
||||||
boolMsg.deserialize(serializer.get(),
|
boolMsg.deserialize(serializer.get(),
|
||||||
"{\"testRepeatedBool\":[true,true,true,7.8,false,false,false,false,false,false,false,false,true]}"_ba);
|
"{\"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);
|
QCOMPARE(serializer->lastError(), QAbstractProtobufSerializer::Error::InvalidFormat);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue