1// Copyright (C) 2022 The Qt Company Ltd.
2// Copyright (C) 2019 Alexey Edelev <semlanik@gmail.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include <QtProtobuf/qtprotobufglobal.h>
6
7#include <QtCore/qreadwritelock.h>
8#include <QtCore/qmutex.h>
9#include <QtCore/qpair.h>
10
11#include <QtProtobuf/private/qtprotobuflogging_p.h>
12
13#include "qtprotobuftypes.h"
14#include "qprotobufobject.h"
15
16#include <mutex>
17#include <limits>
18
19QT_BEGIN_NAMESPACE
20
21namespace {
22/*
23 \internal
24 \brief The ProtobufOrderingRegistry stores the mapping between
25 QtProtobufPrivate::QProtobufPropertyOrdering and QMetaType.
26*/
27struct ProtobufOrderingRegistry
28{
29 using ProtobufOrderingRegistryRecord =
30 QPair<QMetaType, QtProtobufPrivate::QProtobufPropertyOrdering>;
31
32 void registerOrdering(QMetaType type, QtProtobufPrivate::QProtobufPropertyOrdering ordering)
33 {
34 QWriteLocker locker(&m_lock);
35 m_registry[ordering.getMessageFullName().toString()] =
36 ProtobufOrderingRegistryRecord(type, ordering);
37 }
38
39 QtProtobufPrivate::QProtobufPropertyOrdering getOrdering(QMetaType type) const
40 {
41 QtProtobufPrivate::QProtobufPropertyOrdering ordering{ .data: nullptr };
42
43 QReadLocker locker(&m_lock);
44 for (auto it = m_registry.constBegin(); it != m_registry.constEnd(); ++it) {
45 if (type == it.value().first) {
46 ordering = it.value().second;
47 break;
48 }
49 }
50
51 return ordering;
52 }
53
54 ProtobufOrderingRegistryRecord
55 findMessageByName(const QString &messageName) const
56 {
57 ProtobufOrderingRegistryRecord record = {
58 QMetaType(QMetaType::UnknownType), { .data: nullptr }
59 };
60
61 QReadLocker locker(&m_lock);
62 auto it = m_registry.constFind(key: messageName);
63 if (it != m_registry.constEnd())
64 record = it.value();
65 return record;
66 }
67
68private:
69 mutable QReadWriteLock m_lock;
70 QHash<QString, ProtobufOrderingRegistryRecord> m_registry;
71};
72
73Q_GLOBAL_STATIC(ProtobufOrderingRegistry, orderingRegistry)
74}
75
76namespace QtProtobufPrivate {
77 constexpr uint jsonNameOffsetsOffset = 0;
78 // Use this constant to make the +/- 1 more easily readable
79 constexpr int NullTerminator = 1;
80 QUtf8StringView QProtobufPropertyOrdering::getMessageFullName() const
81 {
82 Q_ASSERT(data);
83 const char *name = char_data();
84 return { name, qsizetype(data->fullPackageNameSize) };
85 }
86
87 QUtf8StringView QProtobufPropertyOrdering::getJsonName(int index) const
88 {
89 Q_ASSERT(data);
90 const uint stringOffset = uint_dataForIndex(index, offset: jsonNameOffsetsOffset);
91 const char *name = char_data() + stringOffset;
92 // This is fine because we store an extra offset for end-of-string
93 const uint nameLength =
94 uint_dataForIndex(index: index + 1, offset: jsonNameOffsetsOffset) - NullTerminator - stringOffset;
95 return { name, qsizetype(nameLength) };
96 }
97
98 int QProtobufPropertyOrdering::getFieldNumber(int index) const
99 {
100 Q_ASSERT(data);
101 uint fieldNumber = uint_dataForIndex(index, offset: data->fieldNumberOffset);
102 if (Q_UNLIKELY(fieldNumber > uint(std::numeric_limits<int>::max())))
103 return -1;
104 return int(fieldNumber);
105 }
106
107 int QProtobufPropertyOrdering::getPropertyIndex(int index) const
108 {
109 Q_ASSERT(data);
110 uint propertyIndex = uint_dataForIndex(index, offset: data->propertyIndexOffset);
111 if (Q_UNLIKELY(propertyIndex > uint(std::numeric_limits<int>::max())))
112 return -1;
113 return int(propertyIndex);
114 }
115
116 int QProtobufPropertyOrdering::indexOfFieldNumber(int fieldNumber) const
117 {
118 Q_ASSERT(data);
119 if (Q_LIKELY(fieldNumber > 0)) {
120 for (int i = 0; i < fieldCount(); ++i) {
121 if (uint_dataForIndex(index: i, offset: data->fieldNumberOffset) == static_cast<uint>(fieldNumber))
122 return i;
123 }
124 }
125 return -1;
126 }
127
128 uint QProtobufPropertyOrdering::getFieldFlags(int index) const
129 {
130 Q_ASSERT(data);
131 return uint_dataForIndex(index, offset: data->flagsOffset);
132 }
133
134 const uint *QProtobufPropertyOrdering::uint_data() const
135 {
136 Q_ASSERT(data);
137 Q_ASSERT(data->version == 0);
138 quintptr dataPtr = quintptr(data);
139 dataPtr += sizeof(Data);
140#if 0 // if Data is ever not just a bunch of uints, remove the #if 0
141 if (dataPtr % alignof(uint) != 0)
142 dataPtr += alignof(uint) - (dataPtr % alignof(uint));
143#else
144 static_assert(alignof(Data) == alignof(uint));
145#endif
146 return reinterpret_cast<const uint *>(dataPtr);
147 }
148
149 const char *QProtobufPropertyOrdering::char_data() const
150 {
151 Q_ASSERT(data);
152 const uint LastOffset = data->flagsOffset;
153 const uint *u_data = uint_data() + LastOffset + data->numFields;
154 quintptr uptr_data = quintptr(u_data);
155 if (size_t(uptr_data) % alignof(char) != 0)
156 uptr_data += alignof(char) - size_t(uptr_data) % alignof(char);
157 const char *c_data = reinterpret_cast<const char *>(uptr_data);
158 return c_data;
159 }
160
161 const uint &QProtobufPropertyOrdering::uint_dataForIndex(int index, uint offset) const
162 {
163 Q_ASSERT(data);
164 Q_ASSERT(index >= 0);
165 Q_ASSERT(uint(index) < data->numFields
166 || (offset == jsonNameOffsetsOffset && uint(index) == data->numFields));
167 return *(uint_data() + offset + index);
168 }
169
170 void registerOrdering(QMetaType type, QProtobufPropertyOrdering ordering)
171 {
172 orderingRegistry->registerOrdering(type, ordering);
173 }
174
175 QProtobufPropertyOrdering getOrderingByMetaType(QMetaType type)
176 {
177 return orderingRegistry->getOrdering(type);
178 }
179
180 QProtobufMessagePointer constructMessageByName(const QString &messageType)
181 {
182 qRegisterProtobufTypes();
183 ProtobufOrderingRegistry::ProtobufOrderingRegistryRecord messageOrderingRecord =
184 orderingRegistry->findMessageByName(messageName: messageType);
185 QMetaType type = messageOrderingRecord.first;
186 QProtobufMessagePointer pointer;
187 if (type.id() != QMetaType::UnknownType) {
188 void *msg = type.create();
189 pointer.reset(p: reinterpret_cast<QProtobufMessage *>(msg));
190 return pointer;
191 }
192 qProtoWarning() << "Unable to find protobuf message with name" << messageType
193 << ". Message is not registered.";
194 return pointer;
195 }
196}
197
198namespace QtProtobuf {
199
200template<typename T>
201void registerBasicConverters()
202{
203 QMetaType::registerConverter<int32_t, T>(T::fromType);
204 QMetaType::registerConverter<T, int32_t>(T::toType);
205 QMetaType::registerConverter<int64_t, T>(T::fromType);
206 QMetaType::registerConverter<T, int64_t>(T::toType);
207 QMetaType::registerConverter<uint32_t, T>(T::fromType);
208 QMetaType::registerConverter<T, uint32_t>(T::toType);
209 QMetaType::registerConverter<uint64_t, T>(T::fromType);
210 QMetaType::registerConverter<T, uint64_t>(T::toType);
211 if constexpr (!std::is_same_v<long long, int64_t>) {
212 QMetaType::registerConverter<long long, T>(T::fromType);
213 QMetaType::registerConverter<T, long long>(T::toType);
214 QMetaType::registerConverter<unsigned long long, T>(T::fromType);
215 QMetaType::registerConverter<T, unsigned long long>(T::toType);
216 }
217 QMetaType::registerConverter<double, T>(T::fromType);
218 QMetaType::registerConverter<T, double>(T::toType);
219 QMetaType::registerConverter<T, QString>(T::toString);
220}
221
222Q_CONSTINIT QBasicMutex registerMutex;
223std::vector<QtProtobuf::RegisterFunction> &registerFunctions()
224{
225 // no need for implicit sharing etc, so stick with std::vector
226 static std::vector<QtProtobuf::RegisterFunction> registrationList;
227 return registrationList;
228}
229
230ProtoTypeRegistrar::ProtoTypeRegistrar(QtProtobuf::RegisterFunction initializer)
231{
232 std::scoped_lock lock(registerMutex);
233 QtProtobuf::registerFunctions().push_back(x: initializer);
234}
235
236} // namespace QtProtobuf
237
238static void qRegisterBaseTypes()
239{
240 [[maybe_unused]] // definitely unused
241 static bool registered = [] {
242 qRegisterMetaType<QtProtobuf::int32>();
243 qRegisterMetaType<QtProtobuf::int64>();
244 qRegisterMetaType<QtProtobuf::uint32>();
245 qRegisterMetaType<QtProtobuf::uint64>();
246 qRegisterMetaType<QtProtobuf::sint32>();
247 qRegisterMetaType<QtProtobuf::sint64>();
248 qRegisterMetaType<QtProtobuf::fixed32>();
249 qRegisterMetaType<QtProtobuf::fixed64>();
250 qRegisterMetaType<QtProtobuf::sfixed32>();
251 qRegisterMetaType<QtProtobuf::sfixed64>();
252 qRegisterMetaType<QtProtobuf::boolean>();
253
254 qRegisterMetaType<QtProtobuf::int32List>();
255 qRegisterMetaType<QtProtobuf::int64List>();
256 qRegisterMetaType<QtProtobuf::uint32List>();
257 qRegisterMetaType<QtProtobuf::uint64List>();
258 qRegisterMetaType<QtProtobuf::sint32List>();
259 qRegisterMetaType<QtProtobuf::sint64List>();
260 qRegisterMetaType<QtProtobuf::fixed32List>();
261 qRegisterMetaType<QtProtobuf::fixed64List>();
262 qRegisterMetaType<QtProtobuf::sfixed32List>();
263 qRegisterMetaType<QtProtobuf::sfixed64List>();
264
265 qRegisterMetaType<QtProtobuf::doubleList>();
266 qRegisterMetaType<QtProtobuf::floatList>();
267 qRegisterMetaType<QtProtobuf::boolList>();
268
269 QtProtobuf::registerBasicConverters<QtProtobuf::int32>();
270 QtProtobuf::registerBasicConverters<QtProtobuf::int64>();
271 QtProtobuf::registerBasicConverters<QtProtobuf::sfixed32>();
272 QtProtobuf::registerBasicConverters<QtProtobuf::sfixed64>();
273 QtProtobuf::registerBasicConverters<QtProtobuf::fixed32>();
274 QtProtobuf::registerBasicConverters<QtProtobuf::fixed64>();
275 return true;
276 }();
277}
278
279/*!
280 \relates QtProtobuf
281 Calling this function registers all, currently known, protobuf types with
282 the serializer registry.
283
284 \note You should not have to call this function manually, as it is called
285 automatically upon attempting serialization or deserialization of a protobuf
286 message.
287*/
288void qRegisterProtobufTypes()
289{
290 qRegisterBaseTypes();
291
292 std::vector<QtProtobuf::RegisterFunction> registrationList;
293 // Move the list to a local variable, emptying the global one.
294 {
295 std::scoped_lock lock(QtProtobuf::registerMutex);
296 registrationList.swap(x&: QtProtobuf::registerFunctions());
297 }
298
299 for (QtProtobuf::RegisterFunction registerFunc : registrationList)
300 registerFunc();
301}
302
303QT_IMPL_METATYPE_EXTERN_TAGGED(QtProtobuf::int32, QtProtobuf_int32)
304QT_IMPL_METATYPE_EXTERN_TAGGED(QtProtobuf::int64, QtProtobuf_int64)
305QT_IMPL_METATYPE_EXTERN_TAGGED(QtProtobuf::fixed32, QtProtobuf_fixed32)
306QT_IMPL_METATYPE_EXTERN_TAGGED(QtProtobuf::fixed64, QtProtobuf_fixed64)
307QT_IMPL_METATYPE_EXTERN_TAGGED(QtProtobuf::sfixed32, QtProtobuf_sfixed32)
308QT_IMPL_METATYPE_EXTERN_TAGGED(QtProtobuf::sfixed64, QtProtobuf_sfixed64)
309
310/*!
311 \enum QtProtobuf::WireTypes
312 \brief The WireTypes enumeration reflects protobuf default wiretypes.
313
314 The following table shows the values in the enumeration and their
315 corresponding types:
316
317 \value Unknown Invalid wire type
318 \value Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
319 \value Fixed64 fixed64, sfixed64, double
320 \value LengthDelimited string, bytes, embedded messages, packed repeated fields
321 \value StartGroup groups. Deprecated in proto syntax 3. Not supported by QtProtobuf.
322 \value EndGroup groups. Deprecated in proto syntax 3. Not supported by QtProtobuf.
323 \value Fixed32 fixed32, sfixed32, float
324
325 \sa {https://protobuf.dev/programming-guides/encoding} {encoding}
326*/
327
328/*!
329 \class QProtobufPropertyOrderingInfo
330 \inmodule QtProtobuf
331 \internal
332 \brief Holds a property's index in the property system, and the json_name.
333
334 This class is used by the QAbstractProtobufSerializer to help serialize/
335 deserialize protobuf messages.
336
337 \sa QProtobufPropertyOrdering
338*/
339
340/*!
341 \typealias QProtobufPropertyOrdering
342 \internal
343
344 A map between the property field index and an instance of
345 QProtobufPropertyOrderingInfo.
346
347 \sa Q_PROTOBUF_OBJECT
348*/
349
350/*!
351 \namespace QtProtobuf
352 \brief The QtProtobuf namespace contains type aliases and classes needed to support QtProtobuf.
353 \inmodule QtProtobuf
354*/
355
356/*!
357 \struct QtProtobuf::transparent
358 \inmodule QtProtobuf
359 \internal
360 \brief Only used to create new, unique types for numeric types.
361*/
362
363/*!
364 \typealias QtProtobuf::int32
365
366 int32 is a regular signed 32-bit integer that is represented in protobuf as
367 a variable size integer, an alias for WireTypes::Varint.
368*/
369
370/*!
371 \typealias QtProtobuf::int64
372
373 int64 is a regular signed 64-bit integer that is represented in protobuf as
374 a variable size integer, an alias for WireTypes::Varint.
375*/
376
377/*!
378 \typealias QtProtobuf::uint32
379
380 uint32 is an unsigned 32-bit integer that is represented in protobuf as
381 variable size integer, an alias for WireTypes::Varint.
382*/
383
384/*!
385 \typealias QtProtobuf::uint64
386
387 uint64 is an unsigned 64-bit integer that is represented in protobuf as
388 variable size integer, an alias for WireTypes::Varint.
389*/
390
391/*!
392 \typealias QtProtobuf::sint32
393
394 sint32 is a 32-bit integer with forced sign marker that is represented in
395 protobuf as variable size integer, an alias for WireTypes::Varint.
396 sint32 is serialized using ZigZag conversion to reduce size of negative
397 numbers.
398
399 \sa {https://protobuf.dev/programming-guides/encoding/#signed-ints} {signed-integers}
400*/
401
402/*!
403 \typealias QtProtobuf::sint64
404
405 sint64 is a 64-bit integer with forced sign marker that is represented in
406 protobuf as variable size integer, an alias for WireTypes::Varint.
407 sint64 is serialized using ZigZag conversion to reduce size of negative numbers.
408
409 \sa {https://protobuf.dev/programming-guides/encoding/#signed-ints} {signed-integers}
410*/
411
412/*!
413 \typealias QtProtobuf::fixed32
414
415 fixed32 is an unsigned 32-bit integer that is represented in protobuf as a
416 fixed size 32-bit field, an alias for WireTypes::Fixed32.
417*/
418
419/*!
420 \typealias QtProtobuf::fixed64
421
422 fixed64 is an unsigned 64-bit integer that is represented in protobuf as a
423 fixed size 64-bit field, an alias for WireTypes::Fixed64.
424*/
425
426/*!
427 \typealias QtProtobuf::sfixed32
428
429 sfixed32 is a signed 32-bit integer that is represented in protobuf as a
430 fixed size 32-bit field, an alias for WireTypes::Fixed32.
431*/
432
433/*!
434 \typealias QtProtobuf::sfixed64
435
436 sfixed64 is a signed 64-bit integer that is represented in protobuf as a
437 fixed size 64-bit field, an alias for WireTypes::Fixed64.
438*/
439
440/*!
441 \typealias QtProtobuf::int32List
442
443 Alias for a list of QtProtobuf::int32.
444*/
445
446/*!
447 \typealias QtProtobuf::int64List
448
449 Alias for a list of QtProtobuf::int64.
450*/
451
452/*!
453 \typealias QtProtobuf::uint32List
454
455 Alias for a list of QtProtobuf::uint32.
456*/
457
458/*!
459 \typealias QtProtobuf::uint64List
460
461 Alias for a list of QtProtobuf::uint64.
462*/
463
464/*!
465 \typealias QtProtobuf::sint32List
466
467 Alias for a list of QtProtobuf::sint32.
468*/
469
470/*!
471 \typealias QtProtobuf::sint64List
472
473 Alias for a list of QtProtobuf::sint64.
474*/
475
476/*!
477 \typealias QtProtobuf::fixed32List
478
479 Alias for a list of QtProtobuf::fixed32.
480*/
481
482/*!
483 \typealias QtProtobuf::fixed64List
484
485 Alias for a list of QtProtobuf::fixed64.
486*/
487
488/*!
489 \typealias QtProtobuf::sfixed32List
490
491 Alias for a list of QtProtobuf::sfixed32.
492*/
493
494/*!
495 \typealias QtProtobuf::sfixed64List
496
497 Alias for a list of QtProtobuf::sfixed64.
498*/
499
500/*!
501 \typealias QtProtobuf::floatList
502
503 Alias for a list of float.
504*/
505
506/*!
507 \typealias QtProtobuf::doubleList
508
509 Alias for a list of double.
510*/
511
512/*!
513 \typealias QtProtobuf::boolList
514
515 Alias for a list of bool.
516*/
517
518/*!
519 \typealias QtProtobuf::RegisterFunction
520 \internal
521*/
522
523/*!
524 \fn template <typename T> struct QtProtobuf::ProtoTypeRegistrar
525 \internal
526
527 Used in the type registration process.
528*/
529
530/*!
531 \fn template<typename T> bool QtProtobuf::repeatedValueCompare(const QList<T> &a, const QList<T> &b)
532 \fn template<typename K, typename V> bool QtProtobuf::repeatedValueCompare(const QHash<K, V> &a, const QHash<K, V> &b)
533
534 Compares two \c{repeated} fields (essentially a list) to each other.
535 Returns \c true if the two fields are equal, \c false otherwise.
536
537 These functions are used in the generated code to implement operator==.
538*/
539
540
541QT_END_NAMESPACE
542

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