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 | |
19 | QT_BEGIN_NAMESPACE |
20 | |
21 | namespace { |
22 | /* |
23 | \internal |
24 | \brief The ProtobufOrderingRegistry stores the mapping between |
25 | QtProtobufPrivate::QProtobufPropertyOrdering and QMetaType. |
26 | */ |
27 | struct 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 | |
68 | private: |
69 | mutable QReadWriteLock m_lock; |
70 | QHash<QString, ProtobufOrderingRegistryRecord> m_registry; |
71 | }; |
72 | |
73 | Q_GLOBAL_STATIC(ProtobufOrderingRegistry, orderingRegistry) |
74 | } |
75 | |
76 | namespace 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 | |
198 | namespace QtProtobuf { |
199 | |
200 | template<typename T> |
201 | void 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 | |
222 | Q_CONSTINIT QBasicMutex registerMutex; |
223 | std::vector<QtProtobuf::RegisterFunction> ®isterFunctions() |
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 | |
230 | ProtoTypeRegistrar::ProtoTypeRegistrar(QtProtobuf::RegisterFunction initializer) |
231 | { |
232 | std::scoped_lock lock(registerMutex); |
233 | QtProtobuf::registerFunctions().push_back(x: initializer); |
234 | } |
235 | |
236 | } // namespace QtProtobuf |
237 | |
238 | static 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 | */ |
288 | void 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 | |
303 | QT_IMPL_METATYPE_EXTERN_TAGGED(QtProtobuf::int32, QtProtobuf_int32) |
304 | QT_IMPL_METATYPE_EXTERN_TAGGED(QtProtobuf::int64, QtProtobuf_int64) |
305 | QT_IMPL_METATYPE_EXTERN_TAGGED(QtProtobuf::fixed32, QtProtobuf_fixed32) |
306 | QT_IMPL_METATYPE_EXTERN_TAGGED(QtProtobuf::fixed64, QtProtobuf_fixed64) |
307 | QT_IMPL_METATYPE_EXTERN_TAGGED(QtProtobuf::sfixed32, QtProtobuf_sfixed32) |
308 | QT_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 | |
541 | QT_END_NAMESPACE |
542 | |