From 3df9d285dab04e09a461e85efef437a856fffc9c Mon Sep 17 00:00:00 2001 From: Alexey Edelev Date: Fri, 27 Sep 2024 12:55:53 +0200 Subject: [PATCH] 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 --- src/protobuf/CMakeLists.txt | 1 + src/protobuf/qprotobufdeserializerbase.cpp | 136 +++++ src/protobuf/qprotobufdeserializerbase_p.h | 65 ++ src/protobuf/qprotobufjsonserializer.cpp | 454 +++++++------- src/protobuf/qprotobufserializer.cpp | 559 ++++++++---------- src/protobuf/qprotobufserializer_p.h | 69 ++- .../expect_failing_tests_v3.21.9.0.txt | 2 - ...buf_deserialization_json_repeatedtypes.cpp | 4 +- 8 files changed, 680 insertions(+), 610 deletions(-) create mode 100644 src/protobuf/qprotobufdeserializerbase.cpp create mode 100644 src/protobuf/qprotobufdeserializerbase_p.h diff --git a/src/protobuf/CMakeLists.txt b/src/protobuf/CMakeLists.txt index 5f7314a4..055d7961 100644 --- a/src/protobuf/CMakeLists.txt +++ b/src/protobuf/CMakeLists.txt @@ -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 diff --git a/src/protobuf/qprotobufdeserializerbase.cpp b/src/protobuf/qprotobufdeserializerbase.cpp new file mode 100644 index 00000000..fe7299ad --- /dev/null +++ b/src/protobuf/qprotobufdeserializerbase.cpp @@ -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 + +#include +#include +#include + +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())) + return false; + + continue; + } + + const auto fieldFlags = fieldInfo.fieldFlags(); + if ((fieldFlags.testFlags(RepeatedMessageFlags) + || fieldFlags.testFlags({ QtProtobufPrivate::FieldFlag::Map })) + && m_cachedPropertyValue.canView(QMetaType::fromType())) { + if (!m_cachedRepeatedIterator.isValid()) + m_cachedRepeatedIterator = m_cachedPropertyValue.view(); + + 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 diff --git a/src/protobuf/qprotobufdeserializerbase_p.h b/src/protobuf/qprotobufdeserializerbase_p.h new file mode 100644 index 00000000..213a0bd2 --- /dev/null +++ b/src/protobuf/qprotobufdeserializerbase_p.h @@ -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 +#include +#include + +#include + +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 diff --git a/src/protobuf/qprotobufjsonserializer.cpp b/src/protobuf/qprotobufjsonserializer.cpp index 447c87d7..a5042aae 100644 --- a/src/protobuf/qprotobufjsonserializer.cpp +++ b/src/protobuf/qprotobufjsonserializer.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -19,7 +20,6 @@ #include #include -#include #include 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 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()] = createCommonHandler(); @@ -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 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(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(); - Q_ASSERT(messageProperty != nullptr); - ok = deserializeObject(messageProperty); - return propertyData; - } - - if (propertyData.canView(QMetaType::fromType())) { - QProtobufRepeatedIterator propertyIt = propertyData.view(); - 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 msgContainer; // map - 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(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::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(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(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(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 diff --git a/src/protobuf/qprotobufserializer.cpp b/src/protobuf/qprotobufserializer.cpp index efc5951f..b7a84ba0 100644 --- a/src/protobuf/qprotobufserializer.cpp +++ b/src/protobuf/qprotobufserializer.cpp @@ -287,6 +287,236 @@ QByteArray QProtobufSerializerImpl::encodeHeader(int fieldIndex, QtProtobuf::Wir return QProtobufSerializerPrivate::serializeVarintCommon(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>()); + bool result = false; + if (m_wireType == QtProtobuf::WireTypes::Varint) { + result = QProtobufSerializerPrivate::deserializeNonPackedList(m_it, + value); + } else if (m_wireType == QtProtobuf::WireTypes::LengthDelimited) { + result = QProtobufSerializerPrivate::deserializeList(m_it, value); + } + value.convert(metaType); + return result; + } + + return QProtobufSerializerPrivate::deserializeBasic(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(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(basicHandler->wireType) : -1) + .arg(static_cast(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(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(it); + if (!opt) + return false; + uint32_t header = opt.value(); + wireType = static_cast(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 - 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 &value) -{ - QVariant variantValue; - if (!QProtobufSerializerPrivate::deserializeList(it, variantValue)) { - setUnexpectedEndOfStreamError(); - return false; - } - value = variantValue.value>(); - 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(it); - if (!opt) - return false; - uint32_t header = opt.value(); - wireType = static_cast(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(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(); - 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>(); - if (deserializeEnumList(intList)) { - cachedPropertyValue.setValue(intList); - return true; - } - return false; - } else { - if (deserializeBasic(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(basicHandler->wireType) : -1) - .arg(static_cast(wireType))); - return false; - } - } - - if (!basicHandler->deserializer(it, cachedPropertyValue)) { - setUnexpectedEndOfStreamError(); - return false; - } - return true; - } - - if (cachedPropertyValue.canView(QMetaType::fromType())) { - if (!cachedRepeatedIterator.isValid()) { - cachedRepeatedIterator = cachedPropertyValue.view(); - } - 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 diff --git a/src/protobuf/qprotobufserializer_p.h b/src/protobuf/qprotobufserializer_p.h index 72cb7827..1e14b13e 100644 --- a/src/protobuf/qprotobufserializer_p.h +++ b/src/protobuf/qprotobufserializer_p.h @@ -23,9 +23,10 @@ #include #include +#include +#include #include #include -#include #include #include @@ -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 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(), 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 &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) diff --git a/tests/auto/protobuf/conformance/expect_failing_tests_v3.21.9.0.txt b/tests/auto/protobuf/conformance/expect_failing_tests_v3.21.9.0.txt index 9e6ada01..e62069db 100644 --- a/tests/auto/protobuf/conformance/expect_failing_tests_v3.21.9.0.txt +++ b/tests/auto/protobuf/conformance/expect_failing_tests_v3.21.9.0.txt @@ -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 diff --git a/tests/auto/protobuf/json/tst_protobuf_deserialization_json_repeatedtypes.cpp b/tests/auto/protobuf/json/tst_protobuf_deserialization_json_repeatedtypes.cpp index bd78754e..9eba6c37 100644 --- a/tests/auto/protobuf/json/tst_protobuf_deserialization_json_repeatedtypes.cpp +++ b/tests/auto/protobuf/json/tst_protobuf_deserialization_json_repeatedtypes.cpp @@ -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); }