1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtProtobuf/qprotobufmessage.h>
5
6#include <QtProtobuf/private/qprotobufmessage_p.h>
7#include <QtProtobuf/private/qprotobufpropertyorderingbuilder_p.h>
8#include <QtProtobuf/qabstractprotobufserializer.h>
9#include <QtProtobuf/qprotobufpropertyordering.h>
10
11#include <QtCore/qassert.h>
12#include <QtCore/qmetaobject.h>
13
14#include <QtCore/private/qmetaobjectbuilder_p.h>
15
16#include <string>
17
18QT_BEGIN_NAMESPACE
19
20static std::string nullTerminate(QLatin1StringView l1) noexcept
21{
22 return l1.isNull() ? std::string{} : std::string{l1.data(), size_t(l1.size())};
23}
24
25/*!
26 \class QProtobufMessage
27 \inmodule QtProtobuf
28
29 \brief Base class for all protobuf messages.
30
31 Provides access to the properties of a message, using setProperty()
32 and property(), without depending on what the message is.
33*/
34
35/*!
36 \internal
37 Used from generated classes to construct the QProtobufMessage base class.
38 Internally the \a metaObject is used to query QMetaProperty
39*/
40QProtobufMessage::QProtobufMessage(const QMetaObject *metaObject,
41 const QtProtobufPrivate::QProtobufPropertyOrdering *ordering)
42 : d_ptr(new QProtobufMessagePrivate(metaObject, ordering))
43{
44}
45
46/*!
47 \internal
48 Allows constructing QProtobufMessage using the private message implementation from the
49 derived class.
50*/
51QProtobufMessage::QProtobufMessage(QProtobufMessagePrivate &dd) : d_ptr(&dd)
52{
53}
54
55QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QProtobufMessagePrivate)
56
57QProtobufMessagePrivate::QProtobufMessagePrivate(const QMetaObject *metaObject,
58 const QtProtobufPrivate::QProtobufPropertyOrdering
59 *ordering)
60 : metaObject(metaObject), ordering(ordering)
61{
62}
63
64QProtobufMessagePrivate::~QProtobufMessagePrivate()
65 = default;
66
67/*!
68 \internal
69*/
70int QProtobufMessagePrivate::propertyIndex(QAnyStringView propertyName) const
71{
72 return propertyName.visit(v: [this](auto propertyName) {
73 if constexpr (std::is_same_v<QStringView, decltype(propertyName)>) {
74 return metaObject->indexOfProperty(name: propertyName.toLatin1().constData());
75 } else if constexpr (std::is_same_v<QUtf8StringView, decltype(propertyName)>) {
76 return metaObject->indexOfProperty(name: propertyName.toString().toLatin1().constData());
77 } else if constexpr (std::is_same_v<QLatin1StringView, decltype(propertyName)>) {
78 return metaObject->indexOfProperty(name: nullTerminate(propertyName).data());
79 }
80 return -1;
81 });
82}
83
84void QProtobufMessagePrivate::storeUnknownEntry(QProtobufMessage *message,
85 QByteArrayView entry, int fieldNumber)
86{
87 Q_ASSERT(message);
88 message->d_ptr.detach();
89 message->d_ptr->unknownEntries[fieldNumber].append(t: entry.toByteArray());
90}
91
92std::optional<QMetaProperty> QProtobufMessagePrivate::metaProperty(QAnyStringView name) const
93{
94 const int index = propertyIndex(propertyName: name);
95 const QMetaProperty property = metaObject->property(index);
96 if (property.isValid())
97 return property;
98 return std::nullopt;
99}
100
101std::optional<QMetaProperty>
102QProtobufMessagePrivate::metaProperty(QtProtobufPrivate::QProtobufFieldInfo info) const
103{
104 const int propertyIndex = info.propertyIndex() + metaObject->propertyOffset();
105 const QMetaProperty metaProperty = metaObject->property(index: propertyIndex);
106 if (metaProperty.isValid())
107 return metaProperty;
108 return std::nullopt;
109}
110
111/*!
112 Set the property \a propertyName to the value stored in \a value.
113
114 If the \a propertyName isn't a known fields, then the value is ignored, and
115 the function returns \c false.
116
117 Returns \c false if it fails to store the \a value on the property.
118 Otherwise \c{true}.
119*/
120bool QProtobufMessage::setProperty(QAnyStringView propertyName, const QVariant &value)
121{
122 Q_D(QProtobufMessage);
123
124 if (auto mp = d->metaProperty(name: propertyName))
125 return mp->writeOnGadget(gadget: this, value);
126
127 return false;
128}
129
130/*!
131 \overload
132 \since 6.6
133*/
134bool QProtobufMessage::setProperty(QAnyStringView propertyName, QVariant &&value)
135{
136 Q_D(QProtobufMessage);
137
138 if (auto mp = d->metaProperty(name: propertyName))
139 return mp->writeOnGadget(gadget: this, value: std::move(value));
140
141 return false;
142}
143
144/*!
145 Get the value of the property \a propertyName.
146
147 If the \a propertyName isn't known then the returned QVariant is invalid.
148*/
149QVariant QProtobufMessage::property(QAnyStringView propertyName) const
150{
151 Q_D(const QProtobufMessage);
152
153 if (const auto mp = d->metaProperty(name: propertyName))
154 return mp->readOnGadget(gadget: this);
155 return {};
156}
157
158/*!
159 \internal
160*/
161QProtobufMessage::QProtobufMessage(const QProtobufMessage &other)
162 = default;
163
164/*!
165 \internal
166*/
167QProtobufMessage &QProtobufMessage::operator=(const QProtobufMessage &other)
168 = default;
169
170
171/*!
172 \internal
173*/
174QProtobufMessage::~QProtobufMessage()
175 = default;
176
177/*!
178 \since 6.8
179 Returns the pointer to the property ordering of the derived protobuf message.
180*/
181const QtProtobufPrivate::QProtobufPropertyOrdering *QProtobufMessage::propertyOrdering() const
182{
183 Q_D(const QProtobufMessage);
184 return d->ordering;
185}
186
187/*!
188 \internal
189*/
190bool comparesEqual(const QProtobufMessage &lhs, const QProtobufMessage &rhs) noexcept
191{
192 if (lhs.d_ptr == rhs.d_ptr)
193 return true;
194 return lhs.d_func()->unknownEntries == rhs.d_func()->unknownEntries;
195}
196
197namespace QtProtobufPrivate {
198/*!
199 \internal
200*/
201extern QProtobufMessagePointer constructMessageByName(const QString &messageType);
202}
203
204/*!
205 Constructs QProtobufMessage using \a messageType.
206 Returns a pointer to the constructed QProtobufMessage.
207
208 This function attempts to create a message with a type that matches \a messageType.
209 If \a messageType is unknown, the function returns \nullptr. If the message
210 is not found in the registry, the function returns \nullptr.
211 The function caller is given ownership of the constructed message.
212*/
213QProtobufMessagePointer QProtobufMessage::constructByName(const QString &messageType)
214{
215 return QtProtobufPrivate::constructMessageByName(messageType);
216}
217
218/*!
219 \typedef QProtobufMessagePointer
220 \relates QProtobufMessage
221 \inmodule QtProtobuf
222
223 Synonym for std::unique_ptr<QProtobufMessage, QProtobufMessageDeleter>.
224 Use this to manage the lifetime of dynamically allocated QProtobufMessages,
225 such as those created by calling QProtobufMessage::constructByName.
226*/
227
228/*!
229 \class QProtobufMessageDeleter
230 \inmodule QtProtobuf
231 \brief Calls the destructor of the child class of a QProtobufMessage.
232
233 This class calls the destructor of a protobuf message using the meta-type
234 system. It is intended to be used with smart pointers, such as std::unique_ptr.
235
236 \sa QProtobufMessagePointer
237*/
238
239/*!
240 Destroys the message pointed to by \a ptr.
241 This is intended for use with smart pointers.
242
243 \sa QProtobufMessagePointer
244*/
245void QProtobufMessageDeleter::operator()(QProtobufMessage *ptr) const noexcept
246{
247 if (!ptr)
248 return;
249 const QMetaObject *mobj = ptr->d_ptr->metaObject;
250 QMetaType type = mobj->metaType();
251 type.destroy(data: ptr);
252}
253
254/*!
255 Returns the field numbers that were not known to QtProtobuf during
256 deserialization.
257 \since 6.7
258 */
259QList<qint32> QProtobufMessage::unknownFieldNumbers() const
260{
261 Q_D(const QProtobufMessage);
262 return d->unknownEntries.keys();
263}
264
265/*!
266 Returns the unknown \a field values sorted as they were received from the
267 wire.
268 \since 6.7
269*/
270QList<QByteArray> QProtobufMessage::unknownFieldData(qint32 field) const
271{
272 Q_D(const QProtobufMessage);
273 return d->unknownEntries.value(key: field);
274}
275
276/*!
277 \since 6.8
278 Serializes this protobuf message into a QByteArray using \a serializer.
279
280 \sa deserialize()
281*/
282QByteArray QProtobufMessage::serialize(QAbstractProtobufSerializer *serializer) const
283{
284 return serializer->serialize(message: this);
285}
286
287/*!
288 \since 6.8
289 Deserializes this protobuf message from a QByteArray \a data using
290 \a serializer.
291 Returns \c true if deserialization was successful, otherwise \c false.
292
293 \sa serialize()
294*/
295bool QProtobufMessage::deserialize(QAbstractProtobufSerializer *serializer, QByteArrayView data)
296{
297 return serializer->deserialize(message: this, data);
298}
299
300static bool isProtobufMessage(QMetaType type)
301{
302 if (const auto *mo = type.metaObject(); mo && mo->inherits(metaObject: &QProtobufMessage::staticMetaObject))
303 return true;
304 return false;
305}
306
307using StaticMetaCallFn = void (*)(QObject *, QMetaObject::Call, int, void **);
308QMetaObject *buildMetaObject(QMetaType key, QMetaType value, StaticMetaCallFn metaCall)
309{
310 using namespace Qt::StringLiterals;
311 QMetaObjectBuilder builder;
312 builder.addProperty(name: "key", type: key.name(), metaType: key);
313 if (isProtobufMessage(type: key)) {
314 auto propBuilder = builder.addProperty(name: "has_key", type: "bool", metaType: QMetaType(QMetaType::Bool));
315 propBuilder.setWritable(false);
316 }
317 builder.addProperty(name: "value", type: value.name(), metaType: value);
318 if (isProtobufMessage(type: value)) {
319 auto propBuilder = builder.addProperty(name: "has_value", type: "bool", metaType: QMetaType(QMetaType::Bool));
320 propBuilder.setWritable(false);
321 }
322 builder.setClassName("QProtobufMapEntry<"_ba + key.name() + ", " + value.name() + '>');
323 builder.setSuperClass(&QProtobufMapEntryBase::staticMetaObject);
324 builder.setStaticMetacallFunction(metaCall);
325 builder.setFlags(MetaObjectFlag::PropertyAccessInStaticMetaCall);
326 return builder.toMetaObject();
327}
328
329QtProtobufPrivate::FieldFlags getFlagForType(QMetaType type)
330{
331 using FieldFlag = QtProtobufPrivate::FieldFlag;
332 QtProtobufPrivate::FieldFlags flag = {};
333
334 if (isProtobufMessage(type))
335 flag |= FieldFlag::Message | FieldFlag::ExplicitPresence;
336 if (type.flags() & QMetaType::IsEnumeration)
337 flag |= FieldFlag::Enum;
338 if (QByteArrayView(type.name()).startsWith(other: "QList<")) // Surely there's a better way
339 flag |= FieldFlag::Repeated;
340 if (QByteArrayView(type.name()).startsWith(other: "QHash<")) // Surely there's a better way
341 flag |= FieldFlag::Map;
342
343 flag |= FieldFlag::Optional; // Hardcoded for MapEntry uses...
344
345 // Need to get this info::
346 // NonPacked = 0x1,
347 // Oneof = 0x02,
348 // Optional = 0x04,
349 // Repeated = 0x40,
350 // Map = 0x80,
351
352 return flag;
353}
354
355QtProtobufPrivate::QProtobufPropertyOrdering::Data *buildMapEntryOrdering(QMetaType key,
356 QMetaType value)
357{
358 using namespace QtProtobufPrivate;
359 QProtobufPropertyOrderingBuilder builder("MapEntry");
360
361 const auto keyFlags = getFlagForType(type: key);
362 builder.addV0Field(jsonName: "key", fieldNumber: 1, propertyIndex: 0, flags: keyFlags);
363
364 const uint valueIndex = keyFlags.testFlag(flag: FieldFlag::ExplicitPresence) ? 2 : 1;
365 builder.addV0Field(jsonName: "value", fieldNumber: 2, propertyIndex: valueIndex, flags: getFlagForType(type: value));
366
367 return builder.build();
368}
369
370class QProtobufMapEntryBasePrivate : public QProtobufMessagePrivate
371{
372 Q_DISABLE_COPY_MOVE(QProtobufMapEntryBasePrivate)
373public:
374 QProtobufMapEntryBasePrivate(QMetaType key, QMetaType value, StaticMetaCallFn metaCall);
375 ~QProtobufMapEntryBasePrivate() override;
376
377 QtProtobufPrivate::QProtobufPropertyOrdering::Data *data = nullptr;
378 QtProtobufPrivate::QProtobufPropertyOrdering mutableOrdering{};
379 QMetaObject *mutableMetaObject = nullptr;
380};
381
382QProtobufMapEntryBase::QProtobufMapEntryBase(QMetaType key, QMetaType value,
383 StaticMetaCallFn metaCall)
384 : QProtobufMessage(*new QProtobufMapEntryBasePrivate(key, value, metaCall))
385{
386}
387
388QProtobufMapEntryBasePrivate::QProtobufMapEntryBasePrivate(QMetaType key, QMetaType value,
389 StaticMetaCallFn metaCall)
390 : QProtobufMessagePrivate(),
391 data(buildMapEntryOrdering(key, value)),
392 mutableMetaObject(buildMetaObject(key, value, metaCall))
393{
394 mutableOrdering.data = data;
395 QProtobufMessagePrivate::ordering = &mutableOrdering;
396 QProtobufMessagePrivate::metaObject = mutableMetaObject;
397}
398
399QProtobufMapEntryBase::~QProtobufMapEntryBase()
400 = default;
401
402QProtobufMapEntryBasePrivate::~QProtobufMapEntryBasePrivate()
403{
404 data->~Data();
405 free(ptr: data);
406 free(ptr: mutableMetaObject);
407}
408
409QT_END_NAMESPACE
410
411#include "moc_qprotobufmessage.cpp"
412

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtgrpc/src/protobuf/qprotobufmessage.cpp