Add protobuf message registry and raw QProtobufMessage serialization

This allows to create messages using the full protobuf message
name, if the message was registered.

Using this mapping between QMetaType and QProtobufOrdering it's
now possible to serialize and deserialize pointers to QProtobufMessage
without casting QProtobufMessage to the protobuf message type.

This is partially required for conformance testing and Any type
handling.

Change-Id: I58fd2d821b76ce3f59f121b65ad28c2b28a189e7
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Alexey Edelev 2022-10-05 19:11:20 +02:00
parent e1a98f4b16
commit 660608d6b0
10 changed files with 248 additions and 25 deletions

View File

@ -107,4 +107,25 @@ bool QAbstractProtobufSerializer::doDeserialize(QProtobufMessage *message,
assumed to not be \nullptr. assumed to not be \nullptr.
Returns \c true if deserialization was successful, otherwise \c false. Returns \c true if deserialization was successful, otherwise \c false.
*/ */
namespace QtProtobufPrivate {
extern QtProtobufPrivate::QProtobufPropertyOrdering getOrderingByMetaType(QMetaType type);
}
QByteArray QAbstractProtobufSerializer::serializeRawMessage(const QProtobufMessage *message) const
{
Q_ASSERT(message != nullptr && message->metaObject() != nullptr);
auto ordering = QtProtobufPrivate::getOrderingByMetaType(message->metaObject()->metaType());
Q_ASSERT(ordering.data != nullptr);
return serializeMessage(message, ordering);
}
bool QAbstractProtobufSerializer::deserializeRawMessage(QProtobufMessage *message, QByteArrayView data) const
{
Q_ASSERT(message != nullptr && message->metaObject() != nullptr);
auto ordering = QtProtobufPrivate::getOrderingByMetaType(message->metaObject()->metaType());
Q_ASSERT(ordering.data != nullptr);
return deserializeMessage(message, ordering, data);
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -52,6 +52,9 @@ public:
virtual QAbstractProtobufSerializer::DeserializationError deserializationError() const = 0; virtual QAbstractProtobufSerializer::DeserializationError deserializationError() const = 0;
virtual QString deserializationErrorString() const = 0; virtual QString deserializationErrorString() const = 0;
QByteArray serializeRawMessage(const QProtobufMessage *message) const;
bool deserializeRawMessage(QProtobufMessage *message, QByteArrayView data) const;
protected: protected:
virtual QByteArray virtual QByteArray
serializeMessage(const QProtobufMessage *message, serializeMessage(const QProtobufMessage *message,

View File

@ -125,6 +125,27 @@ bool QProtobufMessage::isEqual(const QProtobufMessage &lhs, const QProtobufMessa
return lhs.d_func()->unknownEntries == rhs.d_func()->unknownEntries; return lhs.d_func()->unknownEntries == rhs.d_func()->unknownEntries;
} }
namespace QtProtobufPrivate {
/*!
\internal
*/
extern QProtobufMessage *constructMessageByName(const QString &messageType);
}
/*!
Constructs QProtobufMessage using \a messageType.
Returns a pointer to the constructed QProtobufMessage.
This function attempts to create a message whose type matches \a messageType. If \a messageType
is unknown, the function returns \nullptr. If the message is not found in the registry, the
function returns \nullptr.
Ownership of the constructed message is given to the function caller.
*/
QProtobufMessage *QProtobufMessage::constructByName(const QString &messageType)
{
return QtProtobufPrivate::constructMessageByName(messageType);
}
QT_END_NAMESPACE QT_END_NAMESPACE
#include "moc_qprotobufmessage.cpp" #include "moc_qprotobufmessage.cpp"

View File

@ -21,6 +21,8 @@ public:
QVariant property(QAnyStringView propertyName) const; QVariant property(QAnyStringView propertyName) const;
bool setProperty(QAnyStringView propertyName, const QVariant &value); bool setProperty(QAnyStringView propertyName, const QVariant &value);
Q_REQUIRED_RESULT static QProtobufMessage *constructByName(const QString &messageType);
protected: protected:
explicit QProtobufMessage(const QMetaObject *metaObject); explicit QProtobufMessage(const QMetaObject *metaObject);
QProtobufMessage(const QProtobufMessage &other); QProtobufMessage(const QProtobufMessage &other);
@ -37,6 +39,7 @@ private:
const QMetaObject *metaObject() const; const QMetaObject *metaObject() const;
friend class QProtobufSerializer; friend class QProtobufSerializer;
friend class QAbstractProtobufSerializer;
friend class QProtobufSerializerPrivate; friend class QProtobufSerializerPrivate;
friend class QAbstractProtobufSerializer; friend class QAbstractProtobufSerializer;

View File

@ -276,6 +276,7 @@ void deserializeEnumList(const QProtobufSerializer *serializer, QProtobufSelfche
template<typename T> template<typename T>
inline void qRegisterProtobufType() { inline void qRegisterProtobufType() {
T::registerTypes(); T::registerTypes();
QtProtobufPrivate::registerOrdering(QMetaType::fromType<T>(), T::propertyOrdering);
QtProtobufPrivate::registerHandler(QMetaType::fromType<T *>(), { QtProtobufPrivate::serializeObject<T>, QtProtobufPrivate::registerHandler(QMetaType::fromType<T *>(), { QtProtobufPrivate::serializeObject<T>,
QtProtobufPrivate::deserializeObject<T> }); QtProtobufPrivate::deserializeObject<T> });
QtProtobufPrivate::registerHandler(QMetaType::fromType<QList<std::shared_ptr<T>>>(), { QtProtobufPrivate::serializeList<T>, QtProtobufPrivate::registerHandler(QMetaType::fromType<QList<std::shared_ptr<T>>>(), { QtProtobufPrivate::serializeList<T>,

View File

@ -4,7 +4,11 @@
#include <QtProtobuf/qtprotobufglobal.h> #include <QtProtobuf/qtprotobufglobal.h>
#include <QtCore/qreadwritelock.h>
#include <QtCore/qmutex.h> #include <QtCore/qmutex.h>
#include <QtCore/qpair.h>
#include <QtProtobuf/private/qtprotobuflogging_p.h>
#include "qtprotobuftypes.h" #include "qtprotobuftypes.h"
#include "qprotobufobject.h" #include "qprotobufobject.h"
@ -14,7 +18,60 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
#define registerProtobufType(X) qRegisterMetaType<X>(#X) namespace {
/*
\internal
\brief The ProtobufOrderingRegistry stores the mapping between
QtProtobufPrivate::QProtobufPropertyOrdering and QMetaType.
*/
struct ProtobufOrderingRegistry
{
using ProtobufOrderingRegistryRecord =
QPair<QMetaType, QtProtobufPrivate::QProtobufPropertyOrdering>;
void registerOrdering(QMetaType type, QtProtobufPrivate::QProtobufPropertyOrdering ordering)
{
QWriteLocker locker(&m_lock);
m_registry[ordering.getMessageFullName().toString()] =
ProtobufOrderingRegistryRecord(type, ordering);
}
QtProtobufPrivate::QProtobufPropertyOrdering getOrdering(QMetaType type) const
{
QtProtobufPrivate::QProtobufPropertyOrdering ordering{ nullptr };
QReadLocker locker(&m_lock);
for (auto it = m_registry.constBegin(); it != m_registry.constEnd(); ++it) {
if (type == it.value().first) {
ordering = it.value().second;
break;
}
}
return ordering;
}
ProtobufOrderingRegistryRecord
findMessageByName(const QString &messageName) const
{
ProtobufOrderingRegistryRecord record = {
QMetaType(QMetaType::UnknownType), { nullptr }
};
QReadLocker locker(&m_lock);
auto it = m_registry.constFind(messageName);
if (it != m_registry.constEnd())
record = it.value();
return record;
}
private:
mutable QReadWriteLock m_lock;
QHash<QString, ProtobufOrderingRegistryRecord> m_registry;
};
Q_GLOBAL_STATIC(ProtobufOrderingRegistry, orderingRegistry)
}
namespace QtProtobufPrivate { namespace QtProtobufPrivate {
constexpr uint jsonNameOffsetsOffset = 0; constexpr uint jsonNameOffsetsOffset = 0;
@ -109,6 +166,31 @@ namespace QtProtobufPrivate {
|| (offset == jsonNameOffsetsOffset && uint(index) == data->numFields)); || (offset == jsonNameOffsetsOffset && uint(index) == data->numFields));
return *(uint_data() + offset + index); return *(uint_data() + offset + index);
} }
void registerOrdering(QMetaType type, QProtobufPropertyOrdering ordering)
{
orderingRegistry->registerOrdering(type, ordering);
}
QProtobufPropertyOrdering getOrderingByMetaType(QMetaType type)
{
return orderingRegistry->getOrdering(type);
}
QProtobufMessage *constructMessageByName(const QString &messageType)
{
qRegisterProtobufTypes();
ProtobufOrderingRegistry::ProtobufOrderingRegistryRecord messageOrderingRecord =
orderingRegistry->findMessageByName(messageType);
QMetaType type = messageOrderingRecord.first;
if (type.id() != QMetaType::UnknownType) {
void *msg = type.create();
return reinterpret_cast<QProtobufMessage *>(msg);
}
qProtoWarning() << "Unable to find protobuf message with name" << messageType
<< ". Message is not registered.";
return nullptr;
}
} }
namespace QtProtobuf { namespace QtProtobuf {
@ -155,32 +237,32 @@ static void qRegisterBaseTypes()
{ {
[[maybe_unused]] // definitely unused [[maybe_unused]] // definitely unused
static bool registered = [] { static bool registered = [] {
registerProtobufType(QtProtobuf::int32); qRegisterMetaType<QtProtobuf::int32>();
registerProtobufType(QtProtobuf::int64); qRegisterMetaType<QtProtobuf::int64>();
registerProtobufType(QtProtobuf::uint32); qRegisterMetaType<QtProtobuf::uint32>();
registerProtobufType(QtProtobuf::uint64); qRegisterMetaType<QtProtobuf::uint64>();
registerProtobufType(QtProtobuf::sint32); qRegisterMetaType<QtProtobuf::sint32>();
registerProtobufType(QtProtobuf::sint64); qRegisterMetaType<QtProtobuf::sint64>();
registerProtobufType(QtProtobuf::fixed32); qRegisterMetaType<QtProtobuf::fixed32>();
registerProtobufType(QtProtobuf::fixed64); qRegisterMetaType<QtProtobuf::fixed64>();
registerProtobufType(QtProtobuf::sfixed32); qRegisterMetaType<QtProtobuf::sfixed32>();
registerProtobufType(QtProtobuf::sfixed64); qRegisterMetaType<QtProtobuf::sfixed64>();
registerProtobufType(QtProtobuf::boolean); qRegisterMetaType<QtProtobuf::boolean>();
registerProtobufType(QtProtobuf::int32List); qRegisterMetaType<QtProtobuf::int32List>();
registerProtobufType(QtProtobuf::int64List); qRegisterMetaType<QtProtobuf::int64List>();
registerProtobufType(QtProtobuf::uint32List); qRegisterMetaType<QtProtobuf::uint32List>();
registerProtobufType(QtProtobuf::uint64List); qRegisterMetaType<QtProtobuf::uint64List>();
registerProtobufType(QtProtobuf::sint32List); qRegisterMetaType<QtProtobuf::sint32List>();
registerProtobufType(QtProtobuf::sint64List); qRegisterMetaType<QtProtobuf::sint64List>();
registerProtobufType(QtProtobuf::fixed32List); qRegisterMetaType<QtProtobuf::fixed32List>();
registerProtobufType(QtProtobuf::fixed64List); qRegisterMetaType<QtProtobuf::fixed64List>();
registerProtobufType(QtProtobuf::sfixed32List); qRegisterMetaType<QtProtobuf::sfixed32List>();
registerProtobufType(QtProtobuf::sfixed64List); qRegisterMetaType<QtProtobuf::sfixed64List>();
registerProtobufType(QtProtobuf::doubleList); qRegisterMetaType<QtProtobuf::doubleList>();
registerProtobufType(QtProtobuf::floatList); qRegisterMetaType<QtProtobuf::floatList>();
registerProtobufType(QtProtobuf::boolList); qRegisterMetaType<QtProtobuf::boolList>();
QtProtobuf::registerBasicConverters<QtProtobuf::int32>(); QtProtobuf::registerBasicConverters<QtProtobuf::int32>();
QtProtobuf::registerBasicConverters<QtProtobuf::int64>(); QtProtobuf::registerBasicConverters<QtProtobuf::int64>();

View File

@ -11,6 +11,7 @@
#include <QtCore/QHash> #include <QtCore/QHash>
#include <QtCore/QMetaType> #include <QtCore/QMetaType>
#include <QtCore/QtEndian> #include <QtCore/QtEndian>
#include <QtProtobuf/QProtobufMessage>
#include <memory> #include <memory>
#include <functional> #include <functional>
@ -49,6 +50,8 @@ private:
}; };
static_assert(std::is_trivially_destructible_v<QProtobufPropertyOrdering>); static_assert(std::is_trivially_destructible_v<QProtobufPropertyOrdering>);
extern Q_PROTOBUF_EXPORT void registerOrdering(QMetaType type, QProtobufPropertyOrdering ordering);
// Convenience structure to hold a reference to a single entry // Convenience structure to hold a reference to a single entry
struct QProtobufPropertyOrderingInfo struct QProtobufPropertyOrderingInfo
{ {
@ -207,6 +210,7 @@ struct qMakeUnsignedImpl<int64> {
}; };
template <typename T> template <typename T>
using qMakeUnsigned = typename qMakeUnsignedImpl<T>::type; using qMakeUnsigned = typename qMakeUnsignedImpl<T>::type;
} //namespace QtProtobuf } //namespace QtProtobuf
Q_PROTOBUF_EXPORT void qRegisterProtobufTypes(); Q_PROTOBUF_EXPORT void qRegisterProtobufTypes();

View File

@ -162,6 +162,15 @@ qt6_add_protobuf(tst_protobuf_non_packed_repeatedtypes
qt_autogen_tools_initial_setup(tst_protobuf_non_packed_repeatedtypes) qt_autogen_tools_initial_setup(tst_protobuf_non_packed_repeatedtypes)
qt_internal_add_test(tst_protobuf_raw_serializers
SOURCES
tst_protobuf_raw_serializers.cpp
LIBRARIES
Qt::Test
Qt::ProtobufPrivate
tst_protobuf_basictypes_gen
)
if(UNIX AND NOT CMAKE_CROSSCOMPILING) if(UNIX AND NOT CMAKE_CROSSCOMPILING)
qt_internal_add_test(tst_protobuf_internals qt_internal_add_test(tst_protobuf_internals
SOURCES SOURCES

View File

@ -8,6 +8,8 @@
#include <QSignalSpy> #include <QSignalSpy>
#include <QTest> #include <QTest>
#include <memory>
#include <qtprotobuftestscommon.h> #include <qtprotobuftestscommon.h>
class QtProtobufTypesGenerationTest : public QObject class QtProtobufTypesGenerationTest : public QObject
@ -35,6 +37,8 @@ private slots:
void AssignmentOperatorTest(); void AssignmentOperatorTest();
void MoveOperatorTest(); void MoveOperatorTest();
void AccessMessageFieldsFromGetter(); void AccessMessageFieldsFromGetter();
void InvalidMessageConstructorTest();
}; };
using namespace qtprotobufnamespace::tests; using namespace qtprotobufnamespace::tests;
@ -45,6 +49,10 @@ void QtProtobufTypesGenerationTest::EmptyMessageTest()
QCOMPARE(qtprotobufnamespace::tests::EmptyMessage::staticMetaObject.propertyCount(), 0); QCOMPARE(qtprotobufnamespace::tests::EmptyMessage::staticMetaObject.propertyCount(), 0);
QCOMPARE(qtprotobufnamespace::tests::EmptyMessage::propertyOrdering.getMessageFullName(), QCOMPARE(qtprotobufnamespace::tests::EmptyMessage::propertyOrdering.getMessageFullName(),
"qtprotobufnamespace.tests.EmptyMessage"); "qtprotobufnamespace.tests.EmptyMessage");
std::unique_ptr<QProtobufMessage> rawMessage(
QProtobufMessage::constructByName("qtprotobufnamespace.tests.EmptyMessage"));
QVERIFY(reinterpret_cast<qtprotobufnamespace::tests::EmptyMessage*>(rawMessage.get()) != nullptr);
} }
void QtProtobufTypesGenerationTest::BoolMessageTest() void QtProtobufTypesGenerationTest::BoolMessageTest()
@ -284,6 +292,15 @@ void QtProtobufTypesGenerationTest::ComplexMessageTest()
.value<qtprotobufnamespace::tests::SimpleStringMessage *>()), .value<qtprotobufnamespace::tests::SimpleStringMessage *>()),
stringMsg); stringMsg);
QCOMPARE(test.testComplexField(), stringMsg); QCOMPARE(test.testComplexField(), stringMsg);
std::unique_ptr<QProtobufMessage> rawObject(
QProtobufMessage::constructByName("qtprotobufnamespace.tests.ComplexMessage"));
auto *rawMessage = reinterpret_cast<qtprotobufnamespace::tests::ComplexMessage*>(rawObject.get());
QVERIFY(rawMessage);
QCOMPARE(rawMessage->testFieldInt(), 0);
qtprotobufnamespace::tests::SimpleStringMessage embeddedStringMessage =
rawMessage->testComplexField();
QCOMPARE(embeddedStringMessage.testFieldString(), QString());
} }
void QtProtobufTypesGenerationTest::BytesMessageTest() void QtProtobufTypesGenerationTest::BytesMessageTest()
@ -350,5 +367,13 @@ void QtProtobufTypesGenerationTest::AccessMessageFieldsFromGetter()
QCOMPARE(actual, expected); QCOMPARE(actual, expected);
} }
void QtProtobufTypesGenerationTest::InvalidMessageConstructorTest()
{
std::unique_ptr<QProtobufMessage> message(QProtobufMessage::constructByName(
"qtprotobufnamespace.tests.InvalidMessageConstructorTestNotExists"));
QCOMPARE(message, nullptr);
}
QTEST_MAIN(QtProtobufTypesGenerationTest) QTEST_MAIN(QtProtobufTypesGenerationTest)
#include "tst_protobuf_basictypes.moc" #include "tst_protobuf_basictypes.moc"

View File

@ -0,0 +1,54 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "basicmessages.qpb.h"
#include "fieldindexrange.qpb.h"
#include <QtTest/QtTest>
#include <QProtobufSerializer>
#include <limits>
class QtProtobufRawSerializersTest : public QObject
{
Q_OBJECT
private slots:
void init() { m_serializer.reset(new QProtobufSerializer); }
void ComplexMessageSerializeTest();
void ComplexMessageDeserializeTest();
private:
std::unique_ptr<QProtobufSerializer> m_serializer;
};
class NonMessageQObject : public QObject
{
Q_OBJECT
};
void QtProtobufRawSerializersTest::ComplexMessageSerializeTest()
{
QProtobufMessage *rawMessage =
QProtobufMessage::constructByName("qtprotobufnamespace.tests.ComplexMessage");
m_serializer->deserializeRawMessage(
rawMessage, QByteArray::fromHex("1208320671776572747908d3ffffffffffffffff01"));
auto *message = reinterpret_cast<qtprotobufnamespace::tests::ComplexMessage *>(rawMessage);
QCOMPARE(message->testFieldInt(), -45);
QCOMPARE(message->testComplexField().testFieldString(), QLatin1String("qwerty"));
delete rawMessage;
}
void QtProtobufRawSerializersTest::ComplexMessageDeserializeTest()
{
QProtobufMessage *rawMessage =
QProtobufMessage::constructByName("qtprotobufnamespace.tests.ComplexMessage");
auto *message = reinterpret_cast<qtprotobufnamespace::tests::ComplexMessage *>(rawMessage);
message->setTestFieldInt(-45);
message->testComplexField().setTestFieldString(QLatin1String("qwerty"));
QByteArray buffer = m_serializer->serializeRawMessage(rawMessage);
QCOMPARE(buffer.toHex(), "08d3ffffffffffffffff0112083206717765727479");
delete rawMessage;
}
QTEST_MAIN(QtProtobufRawSerializersTest)
#include "tst_protobuf_raw_serializers.moc"